Version 7.1.7.1, tag libreoffice-7.1.7.1
[LibreOffice.git] / basic / source / basmgr / basicmanagerrepository.cxx
blob01ca8759e145b0a387c1f623bf93d1f11ad2f058
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 <sbintern.hxx>
25 #include <sbxbase.hxx>
27 #include <com/sun/star/document/XStorageBasedDocument.hpp>
28 #include <com/sun/star/document/XEmbeddedScripts.hpp>
29 #include <com/sun/star/frame/Desktop.hpp>
30 #include <svtools/ehdl.hxx>
31 #include <svtools/sfxecode.hxx>
32 #include <unotools/pathoptions.hxx>
33 #include <svl/hint.hxx>
34 #include <vcl/svapp.hxx>
35 #include <tools/debug.hxx>
36 #include <tools/diagnose_ex.h>
37 #include <tools/urlobj.hxx>
38 #include <comphelper/processfactory.hxx>
39 #include <comphelper/documentinfo.hxx>
40 #include <unotools/eventlisteneradapter.hxx>
42 #include <sot/storage.hxx>
44 #include <map>
47 namespace basic
49 using ::com::sun::star::uno::Reference;
50 using ::com::sun::star::uno::XComponentContext;
51 using ::com::sun::star::frame::XModel;
52 using ::com::sun::star::frame::Desktop;
53 using ::com::sun::star::uno::XInterface;
54 using ::com::sun::star::uno::UNO_QUERY;
55 using ::com::sun::star::embed::XStorage;
56 using ::com::sun::star::script::XPersistentLibraryContainer;
57 using ::com::sun::star::uno::UNO_QUERY_THROW;
58 using ::com::sun::star::uno::Exception;
59 using ::com::sun::star::document::XStorageBasedDocument;
60 using ::com::sun::star::document::XEmbeddedScripts;
62 typedef std::map< Reference< XInterface >, std::unique_ptr<BasicManager> > BasicManagerStore;
64 typedef std::vector< BasicManagerCreationListener* > CreationListeners;
66 class ImplRepository : public ::utl::OEventListenerAdapter, public SfxListener, public SvRefBase
68 private:
69 ImplRepository();
70 ~ImplRepository();
72 private:
73 BasicManagerStore m_aStore;
74 CreationListeners m_aCreationListeners;
76 public:
77 static ImplRepository& Instance();
79 BasicManager* getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel );
80 BasicManager* getOrCreateApplicationBasicManager();
81 static BasicManager* getApplicationBasicManager();
82 static void setApplicationBasicManager( std::unique_ptr<BasicManager> _pBasicManager );
83 void registerCreationListener( BasicManagerCreationListener& _rListener );
84 void revokeCreationListener( BasicManagerCreationListener& _rListener );
86 private:
87 /** retrieves the location at which the BasicManager for the given model
88 is stored.
90 If previously, the BasicManager for this model has never been requested,
91 then the model is added to the map, with an initial NULL BasicManager.
93 @param _rxDocumentModel
94 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
96 @precond
97 our mutex is locked
99 BasicManagerStore::iterator
100 impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel );
102 /** tests if there is a location set at which the BasicManager for the given model
103 is stored.
105 @param _rxDocumentModel
106 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
108 @precond
109 our mutex is locked
111 bool impl_hasLocationForModel( const Reference< XModel >& _rxDocumentModel ) const;
113 /** creates a new BasicManager instance for the given model
115 @param _out_rpBasicManager
116 reference to the pointer variable that will hold the new
117 BasicManager.
119 @param _rxDocumentModel
120 the model whose BasicManager will be created. Must not be <NULL/>.
122 bool impl_createManagerForModel(
123 BasicManagerStore::iterator location,
124 const Reference< XModel >& _rxDocumentModel );
126 /** creates the application-wide BasicManager
128 BasicManager* impl_createApplicationBasicManager();
130 /** notifies all listeners which expressed interest in the creation of BasicManager instances.
132 void impl_notifyCreationListeners(
133 const Reference< XModel >& _rxDocumentModel,
134 BasicManager& _rManager
137 /** retrieves the current storage of a given document
139 @param _rxDocument
140 the document whose storage is to be retrieved.
142 @param _out_rStorage
143 takes the storage upon successful return. Note that this might be <NULL/> even
144 if <TRUE/> is returned. In this case, the document has not yet been saved.
146 @return
147 <TRUE/> if the storage could be successfully retrieved (in which case
148 <arg>_out_rStorage</arg> might or might not be <NULL/>), <FALSE/> otherwise.
149 In the latter case, processing this document should stop.
151 static bool impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage );
153 /** retrieves the containers for Basic and Dialog libraries for a given document
155 @param _rxDocument
156 the document whose containers are to be retrieved.
158 @param _out_rxBasicLibraries
159 takes the basic library container upon successful return
161 @param _out_rxDialogLibraries
162 takes the dialog library container upon successful return
164 @return
165 <TRUE/> if and only if both containers exist, and could successfully be retrieved
167 static bool impl_getDocumentLibraryContainers_nothrow(
168 const Reference< XModel >& _rxDocument,
169 Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries,
170 Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries
173 /** initializes the given library containers, which belong to a document
175 static void impl_initDocLibraryContainers_nothrow(
176 const Reference< XPersistentLibraryContainer >& _rxBasicLibraries,
177 const Reference< XPersistentLibraryContainer >& _rxDialogLibraries
180 // OEventListenerAdapter overridables
181 virtual void _disposing( const css::lang::EventObject& _rSource ) override;
183 // SfxListener overridables
184 virtual void Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint ) override;
186 /** removes the Model/BasicManager pair given by iterator from our store
188 void impl_removeFromRepository( const BasicManagerStore::iterator& _pos );
190 private:
191 StarBASIC* impl_getDefaultAppBasicLibrary();
194 ImplRepository::ImplRepository()
198 ImplRepository::~ImplRepository()
200 // Avoid double-delete of managers when they are destroyed in our dtor, and start notify us
201 for (auto& it : m_aStore)
202 EndListening(*it.second);
205 ImplRepository& ImplRepository::Instance()
207 tools::SvRef<SvRefBase>& repository = GetSbxData_Impl().mrImplRepository;
209 static osl::Mutex aMutex;
210 osl::MutexGuard aGuard(aMutex);
211 if (!repository)
212 repository = new ImplRepository;
214 return *static_cast<ImplRepository*>(repository.get());
217 BasicManager* ImplRepository::getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel )
219 SolarMutexGuard g;
221 /* #163556# (DR) - This function may be called recursively while
222 constructing the Basic manager and loading the Basic storage. By
223 passing the map entry received from impl_getLocationForModel() to
224 the function impl_createManagerForModel(), the new Basic manager
225 will be put immediately into the map of existing Basic managers,
226 thus a recursive call of this function will find and return it
227 without creating another instance.
229 auto const loc = impl_getLocationForModel( _rxDocumentModel );
230 if (loc->second != nullptr)
231 return loc->second.get();
232 if (impl_createManagerForModel(loc, _rxDocumentModel))
233 return loc->second.get();
234 return nullptr;
237 BasicManager* ImplRepository::getOrCreateApplicationBasicManager()
239 SolarMutexGuard g;
241 BasicManager* pAppManager = GetSbData()->pAppBasMgr.get();
242 if (pAppManager == nullptr)
243 pAppManager = impl_createApplicationBasicManager();
244 return pAppManager;
247 BasicManager* ImplRepository::getApplicationBasicManager()
249 SolarMutexGuard g;
251 return GetSbData()->pAppBasMgr.get();
254 void ImplRepository::setApplicationBasicManager( std::unique_ptr<BasicManager> _pBasicManager )
256 SolarMutexGuard g;
258 GetSbData()->pAppBasMgr = std::move(_pBasicManager);
262 BasicManager* ImplRepository::impl_createApplicationBasicManager()
264 SolarMutexGuard g;
266 OSL_PRECOND(getApplicationBasicManager() == nullptr, "ImplRepository::impl_createApplicationBasicManager: there already is one!");
268 // Determine Directory
269 SvtPathOptions aPathCFG;
270 OUString aAppBasicDir( aPathCFG.GetBasicPath() );
271 if ( aAppBasicDir.isEmpty() )
273 aPathCFG.SetBasicPath("$(prog)");
276 // Create basic and load it
277 // AppBasicDir is now a PATH
278 INetURLObject aAppBasic( SvtPathOptions().SubstituteVariable("$(progurl)") );
279 aAppBasic.insertName( Application::GetAppName() );
281 BasicManager* pBasicManager = new BasicManager( new StarBASIC, &aAppBasicDir );
282 setApplicationBasicManager( std::unique_ptr<BasicManager>(pBasicManager) );
284 // The first dir in the path as destination:
285 OUString aFileName( aAppBasic.getName() );
286 aAppBasic = INetURLObject( aAppBasicDir.getToken(1, ';') );
287 DBG_ASSERT(aAppBasic.GetProtocol() != INetProtocol::NotValid,
288 OString("Invalid URL: \"" +
289 OUStringToOString(aAppBasicDir, osl_getThreadTextEncoding()) +
290 "\"").getStr());
291 aAppBasic.insertName( aFileName );
292 pBasicManager->SetStorageName( aAppBasic.PathToFileName() );
294 // Basic container
295 SfxScriptLibraryContainer* pBasicCont = new SfxScriptLibraryContainer( Reference< XStorage >() );
296 Reference< XPersistentLibraryContainer > xBasicCont( pBasicCont );
297 pBasicCont->setBasicManager( pBasicManager );
299 // Dialog container
300 SfxDialogLibraryContainer* pDialogCont = new SfxDialogLibraryContainer( Reference< XStorage >() );
301 Reference< XPersistentLibraryContainer > xDialogCont( pDialogCont );
303 LibraryContainerInfo aInfo( xBasicCont, xDialogCont, static_cast< OldBasicPassword* >( pBasicCont ) );
304 pBasicManager->SetLibraryContainerInfo( aInfo );
306 // global constants
308 // StarDesktop
309 Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
310 pBasicManager->SetGlobalUNOConstant( "StarDesktop", css::uno::Any( Desktop::create(xContext)));
312 // (BasicLibraries and DialogLibraries have automatically been added in SetLibraryContainerInfo)
314 // notify
315 impl_notifyCreationListeners( nullptr, *pBasicManager );
317 // outta here
318 return pBasicManager;
322 void ImplRepository::registerCreationListener( BasicManagerCreationListener& _rListener )
324 SolarMutexGuard g;
326 m_aCreationListeners.push_back( &_rListener );
330 void ImplRepository::revokeCreationListener( BasicManagerCreationListener& _rListener )
332 SolarMutexGuard g;
334 CreationListeners::iterator pos = std::find( m_aCreationListeners.begin(), m_aCreationListeners.end(), &_rListener );
335 if ( pos != m_aCreationListeners.end() )
336 m_aCreationListeners.erase( pos );
337 else {
338 OSL_FAIL( "ImplRepository::revokeCreationListener: listener is not registered!" );
343 void ImplRepository::impl_notifyCreationListeners( const Reference< XModel >& _rxDocumentModel, BasicManager& _rManager )
345 for (auto const& creationListener : m_aCreationListeners)
347 creationListener->onBasicManagerCreated( _rxDocumentModel, _rManager );
352 StarBASIC* ImplRepository::impl_getDefaultAppBasicLibrary()
354 BasicManager* pAppManager = getOrCreateApplicationBasicManager();
356 StarBASIC* pAppBasic = pAppManager ? pAppManager->GetLib(0) : nullptr;
357 DBG_ASSERT( pAppBasic != nullptr, "impl_getApplicationBasic: unable to determine the default application's Basic library!" );
358 return pAppBasic;
361 BasicManagerStore::iterator 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 return m_aStore.try_emplace(xNormalized).first;
369 bool ImplRepository::impl_hasLocationForModel( const Reference< XModel >& _rxDocumentModel ) const
371 Reference< XInterface > xNormalized( _rxDocumentModel, UNO_QUERY );
372 DBG_ASSERT( _rxDocumentModel.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
374 return m_aStore.find(xNormalized) != m_aStore.end();
377 void ImplRepository::impl_initDocLibraryContainers_nothrow( const Reference< XPersistentLibraryContainer >& _rxBasicLibraries, const Reference< XPersistentLibraryContainer >& _rxDialogLibraries )
379 OSL_PRECOND( _rxBasicLibraries.is() && _rxDialogLibraries.is(),
380 "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" );
384 // ensure there's a standard library in the basic container
385 OUString aStdLibName( "Standard" );
386 if ( !_rxBasicLibraries->hasByName( aStdLibName ) )
388 _rxBasicLibraries->createLibrary( aStdLibName );
390 // as well as in the dialog container
391 if ( !_rxDialogLibraries->hasByName( aStdLibName ) )
393 _rxDialogLibraries->createLibrary( aStdLibName );
396 catch( const Exception& )
398 DBG_UNHANDLED_EXCEPTION("basic");
402 bool ImplRepository::impl_createManagerForModel( BasicManagerStore::iterator location, const Reference< XModel >& _rxDocumentModel )
404 auto & _out_rpBasicManager = location->second;
406 StarBASIC* pAppBasic = impl_getDefaultAppBasicLibrary();
408 _out_rpBasicManager = nullptr;
409 Reference< XStorage > xStorage;
410 if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel, xStorage ) )
412 m_aStore.erase(location);
413 // the document is not able to provide the storage it is based on.
414 return false;
416 Reference< XPersistentLibraryContainer > xBasicLibs;
417 Reference< XPersistentLibraryContainer > xDialogLibs;
418 if ( !impl_getDocumentLibraryContainers_nothrow( _rxDocumentModel, xBasicLibs, xDialogLibs ) )
420 m_aStore.erase(location);
421 // the document does not have BasicLibraries and DialogLibraries
422 return false;
425 if ( xStorage.is() )
427 // load BASIC-manager
428 SfxErrorContext aErrContext( ERRCTX_SFX_LOADBASIC,
429 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel ) );
430 OUString aAppBasicDir = SvtPathOptions().GetBasicPath();
432 // Storage and BaseURL are only needed by binary documents!
433 tools::SvRef<SotStorage> xDummyStor = new SotStorage( OUString() );
434 _out_rpBasicManager.reset(new BasicManager( *xDummyStor, OUString() /* TODO/LATER: xStorage */,
435 pAppBasic,
436 &aAppBasicDir, true ));
437 if ( !_out_rpBasicManager->GetErrors().empty() )
439 // handle errors
440 std::vector<BasicError>& aErrors = _out_rpBasicManager->GetErrors();
441 for(const auto& rError : aErrors)
443 // show message to user
444 if ( ErrorHandler::HandleError( rError.GetErrorId() ) == DialogMask::ButtonsCancel )
446 // user wants to break loading of BASIC-manager
447 _out_rpBasicManager.reset();
448 xStorage.clear();
449 break;
455 // not loaded?
456 if ( !xStorage.is() )
458 // create new BASIC-manager
459 StarBASIC* pBasic = new StarBASIC( pAppBasic );
460 pBasic->SetFlag( SbxFlagBits::ExtSearch );
461 _out_rpBasicManager.reset(new BasicManager( pBasic, nullptr, true ));
464 // knit the containers with the BasicManager
465 LibraryContainerInfo aInfo( xBasicLibs, xDialogLibs, dynamic_cast< OldBasicPassword* >( xBasicLibs.get() ) );
466 OSL_ENSURE( aInfo.mpOldBasicPassword, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" );
467 _out_rpBasicManager->SetLibraryContainerInfo( aInfo );
469 // initialize the containers
470 impl_initDocLibraryContainers_nothrow( xBasicLibs, xDialogLibs );
472 // so that also dialogs etc. could be 'qualified' addressed
473 _out_rpBasicManager->GetLib(0)->SetParent( pAppBasic );
475 // global properties in the document's Basic
476 _out_rpBasicManager->SetGlobalUNOConstant( "ThisComponent", css::uno::Any( _rxDocumentModel ) );
478 // notify
479 impl_notifyCreationListeners( _rxDocumentModel, *_out_rpBasicManager );
481 // register as listener for this model being disposed/closed
482 OSL_ENSURE( _rxDocumentModel.is(), "ImplRepository::impl_createManagerForModel: the document must be an XComponent!" );
483 assert(impl_hasLocationForModel(_rxDocumentModel));
484 startComponentListening( _rxDocumentModel );
486 bool bOk = false;
487 // startComponentListening may fail in a disposed _rxDocumentModel, in which case _out_rpBasicManager will be removed
488 // from the map and destroyed
489 if (impl_hasLocationForModel(_rxDocumentModel))
491 bOk = true;
492 // register as listener for the BasicManager being destroyed
493 StartListening( *_out_rpBasicManager );
496 // #i104876: Library container must not be modified just after
497 // creation. This happens as side effect when creating default
498 // "Standard" libraries and needs to be corrected here
499 xBasicLibs->setModified( false );
500 xDialogLibs->setModified( false );
501 return bOk;
504 bool ImplRepository::impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage )
506 _out_rStorage.clear();
509 Reference< XStorageBasedDocument > xStorDoc( _rxDocument, UNO_QUERY_THROW );
510 _out_rStorage.set( xStorDoc->getDocumentStorage() );
512 catch( const Exception& )
514 DBG_UNHANDLED_EXCEPTION("basic");
515 return false;
517 return true;
521 bool ImplRepository::impl_getDocumentLibraryContainers_nothrow( const Reference< XModel >& _rxDocument,
522 Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries, Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries )
524 _out_rxBasicLibraries.clear();
525 _out_rxDialogLibraries.clear();
528 Reference< XEmbeddedScripts > xScripts( _rxDocument, UNO_QUERY_THROW );
529 _out_rxBasicLibraries.set( xScripts->getBasicLibraries(), UNO_QUERY_THROW );
530 _out_rxDialogLibraries.set( xScripts->getDialogLibraries(), UNO_QUERY_THROW );
532 catch( const Exception& )
534 DBG_UNHANDLED_EXCEPTION("basic");
536 return _out_rxBasicLibraries.is() && _out_rxDialogLibraries.is();
540 void ImplRepository::impl_removeFromRepository( const BasicManagerStore::iterator& _pos )
542 OSL_PRECOND( _pos != m_aStore.end(), "ImplRepository::impl_removeFromRepository: invalid position!" );
544 std::unique_ptr<BasicManager> pManager = std::move(_pos->second);
545 Reference<XModel> xModel(_pos->first, UNO_QUERY);
547 // *first* remove from map (else Notify won't work properly)
548 m_aStore.erase( _pos );
550 EndListening( *pManager );
552 assert(xModel.is());
553 if (xModel.is())
554 stopComponentListening(xModel);
558 void ImplRepository::_disposing( const css::lang::EventObject& _rSource )
560 SolarMutexGuard g;
562 Reference< XInterface > xNormalizedSource( _rSource.Source, UNO_QUERY );
564 BasicManagerStore::iterator it = std::find_if(m_aStore.begin(), m_aStore.end(),
565 [&xNormalizedSource](BasicManagerStore::reference rEntry) {
566 return rEntry.first.get() == xNormalizedSource.get(); });
567 if (it != m_aStore.end())
569 impl_removeFromRepository( it );
570 return;
573 OSL_FAIL( "ImplRepository::_disposing: where does this come from?" );
577 void ImplRepository::Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint )
579 if ( _rHint.GetId() != SfxHintId::Dying )
580 // not interested in
581 return;
583 BasicManager* pManager = dynamic_cast< BasicManager* >( &_rBC );
584 OSL_ENSURE( pManager, "ImplRepository::Notify: where does this come from?" );
586 BasicManagerStore::iterator it = std::find_if(m_aStore.begin(), m_aStore.end(),
587 [&pManager](BasicManagerStore::reference rEntry) { return rEntry.second.get() == pManager; });
588 if (it != m_aStore.end())
590 // a BasicManager which is still in our repository is being deleted.
591 // That's bad, since by definition, we *own* all instances in our
592 // repository.
593 OSL_FAIL( "ImplRepository::Notify: nobody should tamper with the managers, except ourself!" );
594 m_aStore.erase( it );
598 BasicManager* BasicManagerRepository::getDocumentBasicManager( const Reference< XModel >& _rxDocumentModel )
600 return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel );
603 BasicManager* BasicManagerRepository::getApplicationBasicManager()
605 return ImplRepository::Instance().getOrCreateApplicationBasicManager();
608 void BasicManagerRepository::resetApplicationBasicManager()
610 ImplRepository::setApplicationBasicManager( nullptr );
613 void BasicManagerRepository::registerCreationListener( BasicManagerCreationListener& _rListener )
615 ImplRepository::Instance().registerCreationListener( _rListener );
618 void BasicManagerRepository::revokeCreationListener( BasicManagerCreationListener& _rListener )
620 ImplRepository::Instance().revokeCreationListener( _rListener );
623 } // namespace basic
626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */