1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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
73 BasicManagerStore m_aStore
;
74 CreationListeners m_aCreationListeners
;
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
);
87 /** retrieves the location at which the BasicManager for the given model
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/>.
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
105 @param _rxDocumentModel
106 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
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
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
140 the document whose storage is to be retrieved.
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.
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
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
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
);
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
);
212 repository
= new ImplRepository
;
214 return *static_cast<ImplRepository
*>(repository
.get());
217 BasicManager
* ImplRepository::getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
)
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();
237 BasicManager
* ImplRepository::getOrCreateApplicationBasicManager()
241 BasicManager
* pAppManager
= GetSbData()->pAppBasMgr
.get();
242 if (pAppManager
== nullptr)
243 pAppManager
= impl_createApplicationBasicManager();
247 BasicManager
* ImplRepository::getApplicationBasicManager()
251 return GetSbData()->pAppBasMgr
.get();
254 void ImplRepository::setApplicationBasicManager( std::unique_ptr
<BasicManager
> _pBasicManager
)
258 GetSbData()->pAppBasMgr
= std::move(_pBasicManager
);
262 BasicManager
* ImplRepository::impl_createApplicationBasicManager()
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()) +
291 aAppBasic
.insertName( aFileName
);
292 pBasicManager
->SetStorageName( aAppBasic
.PathToFileName() );
295 rtl::Reference
<SfxScriptLibraryContainer
> pBasicCont
= new SfxScriptLibraryContainer( Reference
< XStorage
>() );
296 pBasicCont
->setBasicManager( pBasicManager
);
299 rtl::Reference
<SfxDialogLibraryContainer
> pDialogCont
= new SfxDialogLibraryContainer( Reference
< XStorage
>() );
301 LibraryContainerInfo
aInfo( pBasicCont
, pDialogCont
, static_cast< OldBasicPassword
* >( pBasicCont
.get() ) );
302 pBasicManager
->SetLibraryContainerInfo( aInfo
);
307 Reference
< XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
308 pBasicManager
->SetGlobalUNOConstant( "StarDesktop", css::uno::Any( Desktop::create(xContext
)));
310 // (BasicLibraries and DialogLibraries have automatically been added in SetLibraryContainerInfo)
313 impl_notifyCreationListeners( nullptr, *pBasicManager
);
316 return pBasicManager
;
320 void ImplRepository::registerCreationListener( BasicManagerCreationListener
& _rListener
)
324 m_aCreationListeners
.push_back( &_rListener
);
328 void ImplRepository::revokeCreationListener( BasicManagerCreationListener
& _rListener
)
332 CreationListeners::iterator pos
= std::find( m_aCreationListeners
.begin(), m_aCreationListeners
.end(), &_rListener
);
333 if ( pos
!= m_aCreationListeners
.end() )
334 m_aCreationListeners
.erase( pos
);
336 OSL_FAIL( "ImplRepository::revokeCreationListener: listener is not registered!" );
341 void ImplRepository::impl_notifyCreationListeners( const Reference
< XModel
>& _rxDocumentModel
, BasicManager
& _rManager
)
343 for (auto const& creationListener
: m_aCreationListeners
)
345 creationListener
->onBasicManagerCreated( _rxDocumentModel
, _rManager
);
350 StarBASIC
* ImplRepository::impl_getDefaultAppBasicLibrary()
352 BasicManager
* pAppManager
= getOrCreateApplicationBasicManager();
354 StarBASIC
* pAppBasic
= pAppManager
? pAppManager
->GetLib(0) : nullptr;
355 DBG_ASSERT( pAppBasic
!= nullptr, "impl_getApplicationBasic: unable to determine the default application's Basic library!" );
359 BasicManagerStore::iterator
ImplRepository::impl_getLocationForModel( const Reference
< XModel
>& _rxDocumentModel
)
361 Reference
< XInterface
> xNormalized( _rxDocumentModel
, UNO_QUERY
);
362 DBG_ASSERT( _rxDocumentModel
.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
364 return m_aStore
.try_emplace(xNormalized
).first
;
367 bool ImplRepository::impl_hasLocationForModel( const Reference
< XModel
>& _rxDocumentModel
) const
369 Reference
< XInterface
> xNormalized( _rxDocumentModel
, UNO_QUERY
);
370 DBG_ASSERT( _rxDocumentModel
.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
372 return m_aStore
.find(xNormalized
) != m_aStore
.end();
375 void ImplRepository::impl_initDocLibraryContainers_nothrow( const Reference
< XPersistentLibraryContainer
>& _rxBasicLibraries
, const Reference
< XPersistentLibraryContainer
>& _rxDialogLibraries
)
377 OSL_PRECOND( _rxBasicLibraries
.is() && _rxDialogLibraries
.is(),
378 "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" );
382 // ensure there's a standard library in the basic container
383 OUString
aStdLibName( "Standard" );
384 if ( !_rxBasicLibraries
->hasByName( aStdLibName
) )
386 _rxBasicLibraries
->createLibrary( aStdLibName
);
388 // as well as in the dialog container
389 if ( !_rxDialogLibraries
->hasByName( aStdLibName
) )
391 _rxDialogLibraries
->createLibrary( aStdLibName
);
394 catch( const Exception
& )
396 DBG_UNHANDLED_EXCEPTION("basic");
400 bool ImplRepository::impl_createManagerForModel( BasicManagerStore::iterator location
, const Reference
< XModel
>& _rxDocumentModel
)
402 auto & _out_rpBasicManager
= location
->second
;
404 StarBASIC
* pAppBasic
= impl_getDefaultAppBasicLibrary();
406 _out_rpBasicManager
= nullptr;
407 Reference
< XStorage
> xStorage
;
408 if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel
, xStorage
) )
410 m_aStore
.erase(location
);
411 // the document is not able to provide the storage it is based on.
414 Reference
< XPersistentLibraryContainer
> xBasicLibs
;
415 Reference
< XPersistentLibraryContainer
> xDialogLibs
;
416 if ( !impl_getDocumentLibraryContainers_nothrow( _rxDocumentModel
, xBasicLibs
, xDialogLibs
) )
418 m_aStore
.erase(location
);
419 // the document does not have BasicLibraries and DialogLibraries
425 // load BASIC-manager
426 SfxErrorContext
aErrContext( ERRCTX_SFX_LOADBASIC
,
427 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel
) );
428 OUString aAppBasicDir
= SvtPathOptions().GetBasicPath();
430 // Storage and BaseURL are only needed by binary documents!
431 tools::SvRef
<SotStorage
> xDummyStor
= new SotStorage( OUString() );
432 _out_rpBasicManager
.reset(new BasicManager( *xDummyStor
, OUString() /* TODO/LATER: xStorage */,
434 &aAppBasicDir
, true ));
435 if ( !_out_rpBasicManager
->GetErrors().empty() )
438 std::vector
<BasicError
>& aErrors
= _out_rpBasicManager
->GetErrors();
439 for(const auto& rError
: aErrors
)
441 // show message to user
442 if ( ErrorHandler::HandleError( rError
.GetErrorId() ) == DialogMask::ButtonsCancel
)
444 // user wants to break loading of BASIC-manager
445 _out_rpBasicManager
.reset();
454 if ( !xStorage
.is() )
456 // create new BASIC-manager
457 StarBASIC
* pBasic
= new StarBASIC( pAppBasic
);
458 pBasic
->SetFlag( SbxFlagBits::ExtSearch
);
459 _out_rpBasicManager
.reset(new BasicManager( pBasic
, nullptr, true ));
462 // knit the containers with the BasicManager
463 LibraryContainerInfo
aInfo( xBasicLibs
, xDialogLibs
, dynamic_cast< OldBasicPassword
* >( xBasicLibs
.get() ) );
464 OSL_ENSURE( aInfo
.mpOldBasicPassword
, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" );
465 _out_rpBasicManager
->SetLibraryContainerInfo( aInfo
);
467 // initialize the containers
468 impl_initDocLibraryContainers_nothrow( xBasicLibs
, xDialogLibs
);
470 // so that also dialogs etc. could be 'qualified' addressed
471 _out_rpBasicManager
->GetLib(0)->SetParent( pAppBasic
);
473 // global properties in the document's Basic
474 _out_rpBasicManager
->SetGlobalUNOConstant( "ThisComponent", css::uno::Any( _rxDocumentModel
) );
477 impl_notifyCreationListeners( _rxDocumentModel
, *_out_rpBasicManager
);
479 // register as listener for this model being disposed/closed
480 OSL_ENSURE( _rxDocumentModel
.is(), "ImplRepository::impl_createManagerForModel: the document must be an XComponent!" );
481 assert(impl_hasLocationForModel(_rxDocumentModel
));
482 startComponentListening( _rxDocumentModel
);
485 // startComponentListening may fail in a disposed _rxDocumentModel, in which case _out_rpBasicManager will be removed
486 // from the map and destroyed
487 if (impl_hasLocationForModel(_rxDocumentModel
))
490 // register as listener for the BasicManager being destroyed
491 StartListening( *_out_rpBasicManager
);
494 // #i104876: Library container must not be modified just after
495 // creation. This happens as side effect when creating default
496 // "Standard" libraries and needs to be corrected here
497 xBasicLibs
->setModified( false );
498 xDialogLibs
->setModified( false );
502 bool ImplRepository::impl_getDocumentStorage_nothrow( const Reference
< XModel
>& _rxDocument
, Reference
< XStorage
>& _out_rStorage
)
504 _out_rStorage
.clear();
507 Reference
< XStorageBasedDocument
> xStorDoc( _rxDocument
, UNO_QUERY_THROW
);
508 _out_rStorage
.set( xStorDoc
->getDocumentStorage() );
510 catch( const Exception
& )
512 DBG_UNHANDLED_EXCEPTION("basic");
519 bool ImplRepository::impl_getDocumentLibraryContainers_nothrow( const Reference
< XModel
>& _rxDocument
,
520 Reference
< XPersistentLibraryContainer
>& _out_rxBasicLibraries
, Reference
< XPersistentLibraryContainer
>& _out_rxDialogLibraries
)
522 _out_rxBasicLibraries
.clear();
523 _out_rxDialogLibraries
.clear();
526 Reference
< XEmbeddedScripts
> xScripts( _rxDocument
, UNO_QUERY_THROW
);
527 _out_rxBasicLibraries
.set( xScripts
->getBasicLibraries(), UNO_QUERY_THROW
);
528 _out_rxDialogLibraries
.set( xScripts
->getDialogLibraries(), UNO_QUERY_THROW
);
530 catch( const Exception
& )
532 DBG_UNHANDLED_EXCEPTION("basic");
534 return _out_rxBasicLibraries
.is() && _out_rxDialogLibraries
.is();
538 void ImplRepository::impl_removeFromRepository( const BasicManagerStore::iterator
& _pos
)
540 OSL_PRECOND( _pos
!= m_aStore
.end(), "ImplRepository::impl_removeFromRepository: invalid position!" );
542 std::unique_ptr
<BasicManager
> pManager
= std::move(_pos
->second
);
543 Reference
<XModel
> xModel(_pos
->first
, UNO_QUERY
);
545 // *first* remove from map (else Notify won't work properly)
546 m_aStore
.erase( _pos
);
548 EndListening( *pManager
);
552 stopComponentListening(xModel
);
556 void ImplRepository::_disposing( const css::lang::EventObject
& _rSource
)
560 Reference
< XInterface
> xNormalizedSource( _rSource
.Source
, UNO_QUERY
);
562 BasicManagerStore::iterator it
= std::find_if(m_aStore
.begin(), m_aStore
.end(),
563 [&xNormalizedSource
](BasicManagerStore::reference rEntry
) {
564 return rEntry
.first
.get() == xNormalizedSource
.get(); });
565 if (it
!= m_aStore
.end())
567 impl_removeFromRepository( it
);
571 OSL_FAIL( "ImplRepository::_disposing: where does this come from?" );
575 void ImplRepository::Notify( SfxBroadcaster
& _rBC
, const SfxHint
& _rHint
)
577 if ( _rHint
.GetId() != SfxHintId::Dying
)
581 BasicManager
* pManager
= dynamic_cast< BasicManager
* >( &_rBC
);
582 OSL_ENSURE( pManager
, "ImplRepository::Notify: where does this come from?" );
584 BasicManagerStore::iterator it
= std::find_if(m_aStore
.begin(), m_aStore
.end(),
585 [&pManager
](BasicManagerStore::reference rEntry
) { return rEntry
.second
.get() == pManager
; });
586 if (it
!= m_aStore
.end())
588 // a BasicManager which is still in our repository is being deleted.
589 // That's bad, since by definition, we *own* all instances in our
591 OSL_FAIL( "ImplRepository::Notify: nobody should tamper with the managers, except ourself!" );
592 m_aStore
.erase( it
);
596 BasicManager
* BasicManagerRepository::getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
)
598 return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel
);
601 BasicManager
* BasicManagerRepository::getApplicationBasicManager()
603 return ImplRepository::Instance().getOrCreateApplicationBasicManager();
606 void BasicManagerRepository::resetApplicationBasicManager()
608 ImplRepository::setApplicationBasicManager( nullptr );
611 void BasicManagerRepository::registerCreationListener( BasicManagerCreationListener
& _rListener
)
613 ImplRepository::Instance().registerCreationListener( _rListener
);
616 void BasicManagerRepository::revokeCreationListener( BasicManagerCreationListener
& _rListener
)
618 ImplRepository::Instance().revokeCreationListener( _rListener
);
624 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */