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>
26 #include <com/sun/star/document/XStorageBasedDocument.hpp>
27 #include <com/sun/star/document/XEmbeddedScripts.hpp>
28 #include <com/sun/star/frame/Desktop.hpp>
29 #include <svtools/ehdl.hxx>
30 #include <svtools/sfxecode.hxx>
31 #include <unotools/pathoptions.hxx>
32 #include <svl/hint.hxx>
33 #include <vcl/svapp.hxx>
34 #include <tools/debug.hxx>
35 #include <tools/diagnose_ex.h>
36 #include <tools/urlobj.hxx>
37 #include <comphelper/processfactory.hxx>
38 #include <comphelper/documentinfo.hxx>
39 #include <unotools/eventlisteneradapter.hxx>
41 #include <osl/getglobalmutex.hxx>
42 #include <rtl/instance.hxx>
43 #include <sot/storage.hxx>
50 using ::com::sun::star::uno::Reference
;
51 using ::com::sun::star::uno::XComponentContext
;
52 using ::com::sun::star::frame::XModel
;
53 using ::com::sun::star::frame::Desktop
;
54 using ::com::sun::star::uno::XInterface
;
55 using ::com::sun::star::uno::UNO_QUERY
;
56 using ::com::sun::star::embed::XStorage
;
57 using ::com::sun::star::script::XPersistentLibraryContainer
;
58 using ::com::sun::star::uno::UNO_QUERY_THROW
;
59 using ::com::sun::star::uno::Exception
;
60 using ::com::sun::star::document::XStorageBasedDocument
;
61 using ::com::sun::star::document::XEmbeddedScripts
;
63 typedef std::map
< Reference
< XInterface
>, std::unique_ptr
<BasicManager
> > BasicManagerStore
;
65 typedef std::vector
< BasicManagerCreationListener
* > CreationListeners
;
67 class ImplRepository
: public ::utl::OEventListenerAdapter
, public SfxListener
70 friend struct CreateImplRepository
;
74 BasicManagerStore m_aStore
;
75 CreationListeners m_aCreationListeners
;
78 static ImplRepository
& Instance();
80 BasicManager
* getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
);
81 BasicManager
* getOrCreateApplicationBasicManager();
82 static BasicManager
* getApplicationBasicManager();
83 static void setApplicationBasicManager( std::unique_ptr
<BasicManager
> _pBasicManager
);
84 void registerCreationListener( BasicManagerCreationListener
& _rListener
);
85 void revokeCreationListener( BasicManagerCreationListener
& _rListener
);
88 /** retrieves the location at which the BasicManager for the given model
91 If previously, the BasicManager for this model has never been requested,
92 then the model is added to the map, with an initial NULL BasicManager.
94 @param _rxDocumentModel
95 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
100 std::unique_ptr
<BasicManager
>&
101 impl_getLocationForModel( const Reference
< XModel
>& _rxDocumentModel
);
103 /** tests if there is a location set at which the BasicManager for the given model
106 @param _rxDocumentModel
107 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
112 bool impl_hasLocationForModel( const Reference
< XModel
>& _rxDocumentModel
) const;
114 /** creates a new BasicManager instance for the given model
116 @param _out_rpBasicManager
117 reference to the pointer variable that will hold the new
120 @param _rxDocumentModel
121 the model whose BasicManager will be created. Must not be <NULL/>.
123 bool impl_createManagerForModel(
124 std::unique_ptr
<BasicManager
>& _out_rpBasicManager
,
125 const Reference
< XModel
>& _rxDocumentModel
);
127 /** creates the application-wide BasicManager
129 BasicManager
* impl_createApplicationBasicManager();
131 /** notifies all listeners which expressed interest in the creation of BasicManager instances.
133 void impl_notifyCreationListeners(
134 const Reference
< XModel
>& _rxDocumentModel
,
135 BasicManager
& _rManager
138 /** retrieves the current storage of a given document
141 the document whose storage is to be retrieved.
144 takes the storage upon successful return. Note that this might be <NULL/> even
145 if <TRUE/> is returned. In this case, the document has not yet been saved.
148 <TRUE/> if the storage could be successfully retrieved (in which case
149 <arg>_out_rStorage</arg> might or might not be <NULL/>), <FALSE/> otherwise.
150 In the latter case, processing this document should stop.
152 static bool impl_getDocumentStorage_nothrow( const Reference
< XModel
>& _rxDocument
, Reference
< XStorage
>& _out_rStorage
);
154 /** retrieves the containers for Basic and Dialog libraries for a given document
157 the document whose containers are to be retrieved.
159 @param _out_rxBasicLibraries
160 takes the basic library container upon successful return
162 @param _out_rxDialogLibraries
163 takes the dialog library container upon successful return
166 <TRUE/> if and only if both containers exist, and could successfully be retrieved
168 static bool impl_getDocumentLibraryContainers_nothrow(
169 const Reference
< XModel
>& _rxDocument
,
170 Reference
< XPersistentLibraryContainer
>& _out_rxBasicLibraries
,
171 Reference
< XPersistentLibraryContainer
>& _out_rxDialogLibraries
174 /** initializes the given library containers, which belong to a document
176 static void impl_initDocLibraryContainers_nothrow(
177 const Reference
< XPersistentLibraryContainer
>& _rxBasicLibraries
,
178 const Reference
< XPersistentLibraryContainer
>& _rxDialogLibraries
181 // OEventListenerAdapter overridables
182 virtual void _disposing( const css::lang::EventObject
& _rSource
) override
;
184 // SfxListener overridables
185 virtual void Notify( SfxBroadcaster
& _rBC
, const SfxHint
& _rHint
) override
;
187 /** removes the Model/BasicManager pair given by iterator from our store
189 void impl_removeFromRepository( const BasicManagerStore::iterator
& _pos
);
192 StarBASIC
* impl_getDefaultAppBasicLibrary();
196 struct CreateImplRepository
198 ImplRepository
* operator()()
200 static ImplRepository
* pRepository
= new ImplRepository
;
206 ImplRepository::ImplRepository()
211 ImplRepository
& ImplRepository::Instance()
213 return *rtl_Instance
< ImplRepository
, CreateImplRepository
, ::osl::MutexGuard
, ::osl::GetGlobalMutex
>::
214 create( CreateImplRepository(), ::osl::GetGlobalMutex() );
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 std::unique_ptr
<BasicManager
>& pBasicManager
= impl_getLocationForModel( _rxDocumentModel
);
230 if (pBasicManager
!= nullptr)
231 return pBasicManager
.get();
232 if (impl_createManagerForModel(pBasicManager
, _rxDocumentModel
))
233 return pBasicManager
.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 SfxScriptLibraryContainer
* pBasicCont
= new SfxScriptLibraryContainer( Reference
< XStorage
>() );
296 Reference
< XPersistentLibraryContainer
> xBasicCont( pBasicCont
);
297 pBasicCont
->setBasicManager( pBasicManager
);
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
);
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)
315 impl_notifyCreationListeners( nullptr, *pBasicManager
);
318 return pBasicManager
;
322 void ImplRepository::registerCreationListener( BasicManagerCreationListener
& _rListener
)
326 m_aCreationListeners
.push_back( &_rListener
);
330 void ImplRepository::revokeCreationListener( BasicManagerCreationListener
& _rListener
)
334 CreationListeners::iterator pos
= std::find( m_aCreationListeners
.begin(), m_aCreationListeners
.end(), &_rListener
);
335 if ( pos
!= m_aCreationListeners
.end() )
336 m_aCreationListeners
.erase( pos
);
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!" );
361 std::unique_ptr
<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 std::unique_ptr
<BasicManager
>& location
= m_aStore
[ xNormalized
];
370 bool ImplRepository::impl_hasLocationForModel( const Reference
< XModel
>& _rxDocumentModel
) const
372 Reference
< XInterface
> xNormalized( _rxDocumentModel
, UNO_QUERY
);
373 DBG_ASSERT( _rxDocumentModel
.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
375 return m_aStore
.find(xNormalized
) != m_aStore
.end();
378 void ImplRepository::impl_initDocLibraryContainers_nothrow( const Reference
< XPersistentLibraryContainer
>& _rxBasicLibraries
, const Reference
< XPersistentLibraryContainer
>& _rxDialogLibraries
)
380 OSL_PRECOND( _rxBasicLibraries
.is() && _rxDialogLibraries
.is(),
381 "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" );
385 // ensure there's a standard library in the basic container
386 OUString
aStdLibName( "Standard" );
387 if ( !_rxBasicLibraries
->hasByName( aStdLibName
) )
389 _rxBasicLibraries
->createLibrary( aStdLibName
);
391 // as well as in the dialog container
392 if ( !_rxDialogLibraries
->hasByName( aStdLibName
) )
394 _rxDialogLibraries
->createLibrary( aStdLibName
);
397 catch( const Exception
& )
399 DBG_UNHANDLED_EXCEPTION("basic");
403 bool ImplRepository::impl_createManagerForModel( std::unique_ptr
<BasicManager
>& _out_rpBasicManager
, const Reference
< XModel
>& _rxDocumentModel
)
405 StarBASIC
* pAppBasic
= impl_getDefaultAppBasicLibrary();
407 _out_rpBasicManager
= nullptr;
408 Reference
< XStorage
> xStorage
;
409 if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel
, xStorage
) )
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
) )
417 // the document does not have BasicLibraries and DialogLibraries
422 // load BASIC-manager
423 SfxErrorContext
aErrContext( ERRCTX_SFX_LOADBASIC
,
424 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel
) );
425 OUString aAppBasicDir
= SvtPathOptions().GetBasicPath();
427 // Storage and BaseURL are only needed by binary documents!
428 tools::SvRef
<SotStorage
> xDummyStor
= new SotStorage( OUString() );
429 _out_rpBasicManager
.reset(new BasicManager( *xDummyStor
, OUString() /* TODO/LATER: xStorage */,
431 &aAppBasicDir
, true ));
432 if ( !_out_rpBasicManager
->GetErrors().empty() )
435 std::vector
<BasicError
>& aErrors
= _out_rpBasicManager
->GetErrors();
436 for(const auto& rError
: aErrors
)
438 // show message to user
439 if ( ErrorHandler::HandleError( rError
.GetErrorId() ) == DialogMask::ButtonsCancel
)
441 // user wants to break loading of BASIC-manager
442 _out_rpBasicManager
.reset();
451 if ( !xStorage
.is() )
453 // create new BASIC-manager
454 StarBASIC
* pBasic
= new StarBASIC( pAppBasic
);
455 pBasic
->SetFlag( SbxFlagBits::ExtSearch
);
456 _out_rpBasicManager
.reset(new BasicManager( pBasic
, nullptr, true ));
459 // knit the containers with the BasicManager
460 LibraryContainerInfo
aInfo( xBasicLibs
, xDialogLibs
, dynamic_cast< OldBasicPassword
* >( xBasicLibs
.get() ) );
461 OSL_ENSURE( aInfo
.mpOldBasicPassword
, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" );
462 _out_rpBasicManager
->SetLibraryContainerInfo( aInfo
);
464 // initialize the containers
465 impl_initDocLibraryContainers_nothrow( xBasicLibs
, xDialogLibs
);
467 // so that also dialogs etc. could be 'qualified' addressed
468 _out_rpBasicManager
->GetLib(0)->SetParent( pAppBasic
);
470 // global properties in the document's Basic
471 _out_rpBasicManager
->SetGlobalUNOConstant( "ThisComponent", css::uno::Any( _rxDocumentModel
) );
474 impl_notifyCreationListeners( _rxDocumentModel
, *_out_rpBasicManager
);
476 // register as listener for this model being disposed/closed
477 OSL_ENSURE( _rxDocumentModel
.is(), "ImplRepository::impl_createManagerForModel: the document must be an XComponent!" );
478 assert(impl_hasLocationForModel(_rxDocumentModel
));
479 startComponentListening( _rxDocumentModel
);
482 // startComponentListening may fail in a disposed _rxDocumentModel, in which case _out_rpBasicManager will be removed
483 // from the map and destroyed
484 if (impl_hasLocationForModel(_rxDocumentModel
))
487 // register as listener for the BasicManager being destroyed
488 StartListening( *_out_rpBasicManager
);
491 // #i104876: Library container must not be modified just after
492 // creation. This happens as side effect when creating default
493 // "Standard" libraries and needs to be corrected here
494 xBasicLibs
->setModified( false );
495 xDialogLibs
->setModified( false );
499 bool ImplRepository::impl_getDocumentStorage_nothrow( const Reference
< XModel
>& _rxDocument
, Reference
< XStorage
>& _out_rStorage
)
501 _out_rStorage
.clear();
504 Reference
< XStorageBasedDocument
> xStorDoc( _rxDocument
, UNO_QUERY_THROW
);
505 _out_rStorage
.set( xStorDoc
->getDocumentStorage() );
507 catch( const Exception
& )
509 DBG_UNHANDLED_EXCEPTION("basic");
516 bool ImplRepository::impl_getDocumentLibraryContainers_nothrow( const Reference
< XModel
>& _rxDocument
,
517 Reference
< XPersistentLibraryContainer
>& _out_rxBasicLibraries
, Reference
< XPersistentLibraryContainer
>& _out_rxDialogLibraries
)
519 _out_rxBasicLibraries
.clear();
520 _out_rxDialogLibraries
.clear();
523 Reference
< XEmbeddedScripts
> xScripts( _rxDocument
, UNO_QUERY_THROW
);
524 _out_rxBasicLibraries
.set( xScripts
->getBasicLibraries(), UNO_QUERY_THROW
);
525 _out_rxDialogLibraries
.set( xScripts
->getDialogLibraries(), UNO_QUERY_THROW
);
527 catch( const Exception
& )
529 DBG_UNHANDLED_EXCEPTION("basic");
531 return _out_rxBasicLibraries
.is() && _out_rxDialogLibraries
.is();
535 void ImplRepository::impl_removeFromRepository( const BasicManagerStore::iterator
& _pos
)
537 OSL_PRECOND( _pos
!= m_aStore
.end(), "ImplRepository::impl_removeFromRepository: invalid position!" );
539 std::unique_ptr
<BasicManager
> pManager
= std::move(_pos
->second
);
540 Reference
<XModel
> xModel(_pos
->first
, UNO_QUERY
);
542 // *first* remove from map (else Notify won't work properly)
543 m_aStore
.erase( _pos
);
545 EndListening( *pManager
);
549 stopComponentListening(xModel
);
553 void ImplRepository::_disposing( const css::lang::EventObject
& _rSource
)
557 Reference
< XInterface
> xNormalizedSource( _rSource
.Source
, UNO_QUERY
);
559 BasicManagerStore::iterator it
= std::find_if(m_aStore
.begin(), m_aStore
.end(),
560 [&xNormalizedSource
](BasicManagerStore::reference rEntry
) {
561 return rEntry
.first
.get() == xNormalizedSource
.get(); });
562 if (it
!= m_aStore
.end())
564 impl_removeFromRepository( it
);
568 OSL_FAIL( "ImplRepository::_disposing: where does this come from?" );
572 void ImplRepository::Notify( SfxBroadcaster
& _rBC
, const SfxHint
& _rHint
)
574 if ( _rHint
.GetId() != SfxHintId::Dying
)
578 BasicManager
* pManager
= dynamic_cast< BasicManager
* >( &_rBC
);
579 OSL_ENSURE( pManager
, "ImplRepository::Notify: where does this come from?" );
581 BasicManagerStore::iterator it
= std::find_if(m_aStore
.begin(), m_aStore
.end(),
582 [&pManager
](BasicManagerStore::reference rEntry
) { return rEntry
.second
.get() == pManager
; });
583 if (it
!= m_aStore
.end())
585 // a BasicManager which is still in our repository is being deleted.
586 // That's bad, since by definition, we *own* all instances in our
588 OSL_FAIL( "ImplRepository::Notify: nobody should tamper with the managers, except ourself!" );
589 m_aStore
.erase( it
);
593 BasicManager
* BasicManagerRepository::getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
)
595 return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel
);
598 BasicManager
* BasicManagerRepository::getApplicationBasicManager()
600 return ImplRepository::Instance().getOrCreateApplicationBasicManager();
603 void BasicManagerRepository::resetApplicationBasicManager()
605 ImplRepository::setApplicationBasicManager( nullptr );
608 void BasicManagerRepository::registerCreationListener( BasicManagerCreationListener
& _rListener
)
610 ImplRepository::Instance().registerCreationListener( _rListener
);
613 void BasicManagerRepository::revokeCreationListener( BasicManagerCreationListener
& _rListener
)
615 ImplRepository::Instance().revokeCreationListener( _rListener
);
621 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */