LibreOffice Module basic (master) 1
basicmanagerrepository.cxx
Go to the documentation of this file.
1/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2/*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
21#include <basic/basmgr.hxx>
22#include <scriptcont.hxx>
23#include <dlgcont.hxx>
24#include <sbintern.hxx>
25#include <sbxbase.hxx>
26
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 <o3tl/string_view.hxx>
31#include <svtools/ehdl.hxx>
32#include <svtools/sfxecode.hxx>
34#include <svl/hint.hxx>
35#include <vcl/svapp.hxx>
36#include <tools/debug.hxx>
38#include <tools/urlobj.hxx>
42
43#include <sot/storage.hxx>
44
45#include <map>
46#include <mutex>
47
48
49namespace basic
50{
51 using ::com::sun::star::uno::Reference;
52 using ::com::sun::star::uno::XComponentContext;
53 using ::com::sun::star::frame::XModel;
54 using ::com::sun::star::frame::Desktop;
55 using ::com::sun::star::uno::XInterface;
56 using ::com::sun::star::uno::UNO_QUERY;
57 using ::com::sun::star::embed::XStorage;
58 using ::com::sun::star::script::XPersistentLibraryContainer;
59 using ::com::sun::star::uno::UNO_QUERY_THROW;
60 using ::com::sun::star::uno::Exception;
61 using ::com::sun::star::document::XStorageBasedDocument;
62 using ::com::sun::star::document::XEmbeddedScripts;
63
64 typedef std::map< Reference< XInterface >, std::unique_ptr<BasicManager> > BasicManagerStore;
65
66 typedef std::vector< BasicManagerCreationListener* > CreationListeners;
67
69 {
70 private:
73
74 private:
77
78 public:
79 static ImplRepository& Instance();
80
84 static void setApplicationBasicManager( std::unique_ptr<BasicManager> _pBasicManager );
87
88 private:
101 BasicManagerStore::iterator
102 impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel );
103
113 bool impl_hasLocationForModel( const Reference< XModel >& _rxDocumentModel ) const;
114
125 BasicManagerStore::iterator location,
126 const Reference< XModel >& _rxDocumentModel );
127
131
135 const Reference< XModel >& _rxDocumentModel,
136 BasicManager& _rManager
137 );
138
153 static bool impl_getDocumentStorage_nothrow( const Reference< XModel >& _rxDocument, Reference< XStorage >& _out_rStorage );
154
170 const Reference< XModel >& _rxDocument,
171 Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries,
172 Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries
173 );
174
178 const Reference< XPersistentLibraryContainer >& _rxBasicLibraries,
179 const Reference< XPersistentLibraryContainer >& _rxDialogLibraries
180 );
181
182 // OEventListenerAdapter overridables
183 virtual void _disposing( const css::lang::EventObject& _rSource ) override;
184
185 // SfxListener overridables
186 virtual void Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint ) override;
187
190 void impl_removeFromRepository( const BasicManagerStore::iterator& _pos );
191
192 private:
194 };
195
197 {
198 }
199
201 {
202 // Avoid double-delete of managers when they are destroyed in our dtor, and start notify us
203 for (auto& it : m_aStore)
204 EndListening(*it.second);
205 }
206
208 {
210 {
211 static std::mutex aMutex;
212 std::unique_lock aGuard(aMutex);
213 if (!repository)
214 repository = new ImplRepository;
215 }
216 return *static_cast<ImplRepository*>(repository.get());
217 }
218
220 {
222
223 /* #163556# (DR) - This function may be called recursively while
224 constructing the Basic manager and loading the Basic storage. By
225 passing the map entry received from impl_getLocationForModel() to
226 the function impl_createManagerForModel(), the new Basic manager
227 will be put immediately into the map of existing Basic managers,
228 thus a recursive call of this function will find and return it
229 without creating another instance.
230 */
231 auto const loc = impl_getLocationForModel( _rxDocumentModel );
232 if (loc->second != nullptr)
233 return loc->second.get();
234 if (impl_createManagerForModel(loc, _rxDocumentModel))
235 return loc->second.get();
236 return nullptr;
237 }
238
240 {
242
243 BasicManager* pAppManager = GetSbData()->pAppBasMgr.get();
244 if (pAppManager == nullptr)
246 return pAppManager;
247 }
248
250 {
252
253 return GetSbData()->pAppBasMgr.get();
254 }
255
256 void ImplRepository::setApplicationBasicManager( std::unique_ptr<BasicManager> _pBasicManager )
257 {
259
260 GetSbData()->pAppBasMgr = std::move(_pBasicManager);
261 }
262
263
265 {
267
268 OSL_PRECOND(getApplicationBasicManager() == nullptr, "ImplRepository::impl_createApplicationBasicManager: there already is one!");
269
270 // Determine Directory
271 SvtPathOptions aPathCFG;
272 OUString aAppBasicDir( aPathCFG.GetBasicPath() );
273 if ( aAppBasicDir.isEmpty() )
274 {
275 aPathCFG.SetBasicPath("$(prog)");
276 }
277
278 // Create basic and load it
279 // AppBasicDir is now a PATH
280 INetURLObject aAppBasic( SvtPathOptions().SubstituteVariable("$(progurl)") );
281 aAppBasic.insertName( Application::GetAppName() );
282
283 BasicManager* pBasicManager = new BasicManager( new StarBASIC, &aAppBasicDir );
284 setApplicationBasicManager( std::unique_ptr<BasicManager>(pBasicManager) );
285
286 // The first dir in the path as destination:
287 OUString aFileName( aAppBasic.getName() );
288 aAppBasic = INetURLObject( o3tl::getToken(aAppBasicDir, 1, ';') );
289 DBG_ASSERT(aAppBasic.GetProtocol() != INetProtocol::NotValid,
290 OString("Invalid URL: \"" +
291 OUStringToOString(aAppBasicDir, osl_getThreadTextEncoding()) +
292 "\"").getStr());
293 aAppBasic.insertName( aFileName );
294 pBasicManager->SetStorageName( aAppBasic.PathToFileName() );
295
296 // Basic container
298 pBasicCont->setBasicManager( pBasicManager );
299
300 // Dialog container
302
303 LibraryContainerInfo aInfo( pBasicCont, pDialogCont, pBasicCont.get() );
304 pBasicManager->SetLibraryContainerInfo( aInfo );
305
306 // global constants
307
308 // StarDesktop
309 Reference< XComponentContext > xContext = ::comphelper::getProcessComponentContext();
310 pBasicManager->SetGlobalUNOConstant( "StarDesktop", css::uno::Any( Desktop::create(xContext)));
311
312 // (BasicLibraries and DialogLibraries have automatically been added in SetLibraryContainerInfo)
313
314 // notify
315 impl_notifyCreationListeners( nullptr, *pBasicManager );
316
317 // outta here
318 return pBasicManager;
319 }
320
321
323 {
325
326 m_aCreationListeners.push_back( &_rListener );
327 }
328
329
331 {
333
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!" );
339 }
340 }
341
342
344 {
345 for (auto const& creationListener : m_aCreationListeners)
346 {
347 creationListener->onBasicManagerCreated( _rxDocumentModel, _rManager );
348 }
349 }
350
351
353 {
355
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;
359 }
360
361 BasicManagerStore::iterator ImplRepository::impl_getLocationForModel( const Reference< XModel >& _rxDocumentModel )
362 {
363 Reference< XInterface > xNormalized( _rxDocumentModel, UNO_QUERY );
364 DBG_ASSERT( _rxDocumentModel.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
365
366 return m_aStore.try_emplace(xNormalized).first;
367 }
368
370 {
371 Reference< XInterface > xNormalized( _rxDocumentModel, UNO_QUERY );
372 DBG_ASSERT( _rxDocumentModel.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
373
374 return m_aStore.find(xNormalized) != m_aStore.end();
375 }
376
378 {
379 OSL_PRECOND( _rxBasicLibraries.is() && _rxDialogLibraries.is(),
380 "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" );
381
382 try
383 {
384 // ensure there's a standard library in the basic container
385 static constexpr OUStringLiteral aStdLibName( u"Standard" );
386 if ( !_rxBasicLibraries->hasByName( aStdLibName ) )
387 {
388 _rxBasicLibraries->createLibrary( aStdLibName );
389 }
390 // as well as in the dialog container
391 if ( !_rxDialogLibraries->hasByName( aStdLibName ) )
392 {
393 _rxDialogLibraries->createLibrary( aStdLibName );
394 }
395 }
396 catch( const Exception& )
397 {
399 }
400 }
401
402 bool ImplRepository::impl_createManagerForModel( BasicManagerStore::iterator location, const Reference< XModel >& _rxDocumentModel )
403 {
404 auto & _out_rpBasicManager = location->second;
405
407
408 _out_rpBasicManager = nullptr;
409 Reference< XStorage > xStorage;
410 if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel, xStorage ) )
411 {
412 m_aStore.erase(location);
413 // the document is not able to provide the storage it is based on.
414 return false;
415 }
418 if ( !impl_getDocumentLibraryContainers_nothrow( _rxDocumentModel, xBasicLibs, xDialogLibs ) )
419 {
420 m_aStore.erase(location);
421 // the document does not have BasicLibraries and DialogLibraries
422 return false;
423 }
424
425 if ( xStorage.is() )
426 {
427 // load BASIC-manager
429 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel ) );
430 OUString aAppBasicDir = SvtPathOptions().GetBasicPath();
431
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, u"" /* TODO/LATER: xStorage */,
435 pAppBasic,
436 &aAppBasicDir, true ));
437 if ( !_out_rpBasicManager->GetErrors().empty() )
438 {
439 // handle errors
440 std::vector<BasicError>& aErrors = _out_rpBasicManager->GetErrors();
441 for(const auto& rError : aErrors)
442 {
443 // show message to user
444 if ( ErrorHandler::HandleError( rError.GetErrorId() ) == DialogMask::ButtonsCancel )
445 {
446 // user wants to break loading of BASIC-manager
447 _out_rpBasicManager.reset();
448 xStorage.clear();
449 break;
450 }
451 }
452 }
453 }
454
455 // not loaded?
456 if ( !xStorage.is() )
457 {
458 // create new BASIC-manager
459 StarBASIC* pBasic = new StarBASIC( pAppBasic );
461 _out_rpBasicManager.reset(new BasicManager( pBasic, nullptr, true ));
462 }
463
464 // knit the containers with the BasicManager
465 LibraryContainerInfo aInfo( xBasicLibs, xDialogLibs, dynamic_cast< SfxScriptLibraryContainer* >( xBasicLibs.get() ) );
466 OSL_ENSURE( aInfo.mpOldBasicPassword, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" );
467 _out_rpBasicManager->SetLibraryContainerInfo( aInfo );
468
469 // initialize the containers
470 impl_initDocLibraryContainers_nothrow( xBasicLibs, xDialogLibs );
471
472 // so that also dialogs etc. could be 'qualified' addressed
473 _out_rpBasicManager->GetLib(0)->SetParent( pAppBasic );
474
475 // global properties in the document's Basic
476 _out_rpBasicManager->SetGlobalUNOConstant( "ThisComponent", css::uno::Any( _rxDocumentModel ) );
477
478 // notify
479 impl_notifyCreationListeners( _rxDocumentModel, *_out_rpBasicManager );
480
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 );
485
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))
490 {
491 bOk = true;
492 // register as listener for the BasicManager being destroyed
493 StartListening( *_out_rpBasicManager );
494 }
495
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;
502 }
503
505 {
506 _out_rStorage.clear();
507 try
508 {
509 Reference< XStorageBasedDocument > xStorDoc( _rxDocument, UNO_QUERY_THROW );
510 _out_rStorage.set( xStorDoc->getDocumentStorage() );
511 }
512 catch( const Exception& )
513 {
515 return false;
516 }
517 return true;
518 }
519
520
522 Reference< XPersistentLibraryContainer >& _out_rxBasicLibraries, Reference< XPersistentLibraryContainer >& _out_rxDialogLibraries )
523 {
524 _out_rxBasicLibraries.clear();
525 _out_rxDialogLibraries.clear();
526 try
527 {
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 );
531 }
532 catch( const Exception& )
533 {
535 }
536 return _out_rxBasicLibraries.is() && _out_rxDialogLibraries.is();
537 }
538
539
540 void ImplRepository::impl_removeFromRepository( const BasicManagerStore::iterator& _pos )
541 {
542 OSL_PRECOND( _pos != m_aStore.end(), "ImplRepository::impl_removeFromRepository: invalid position!" );
543
544 std::unique_ptr<BasicManager> pManager = std::move(_pos->second);
545 Reference<XModel> xModel(_pos->first, UNO_QUERY);
546
547 // *first* remove from map (else Notify won't work properly)
548 m_aStore.erase( _pos );
549
550 EndListening( *pManager );
551
552 assert(xModel.is());
553 if (xModel.is())
555 }
556
557
558 void ImplRepository::_disposing( const css::lang::EventObject& _rSource )
559 {
561
562 Reference< XInterface > xNormalizedSource( _rSource.Source, UNO_QUERY );
563
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())
568 {
570 return;
571 }
572
573 OSL_FAIL( "ImplRepository::_disposing: where does this come from?" );
574 }
575
576
577 void ImplRepository::Notify( SfxBroadcaster& _rBC, const SfxHint& _rHint )
578 {
579 if ( _rHint.GetId() != SfxHintId::Dying )
580 // not interested in
581 return;
582
583 BasicManager* pManager = dynamic_cast< BasicManager* >( &_rBC );
584 OSL_ENSURE( pManager, "ImplRepository::Notify: where does this come from?" );
585
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())
589 {
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 );
595 }
596 }
597
599 {
600 return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel );
601 }
602
604 {
606 }
607
609 {
611 }
612
614 {
616 }
617
619 {
621 }
622
623} // namespace basic
624
625
626/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
SbxAppData & GetSbxData_Impl()
Definition: basrdll.cxx:123
static OUString GetAppName()
StarBASIC * GetLib(sal_uInt16 nLib) const
Definition: basmgr.cxx:1169
void SetLibraryContainerInfo(const LibraryContainerInfo &rInfo)
announces the library containers which belong to this BasicManager
Definition: basmgr.cxx:526
void SetGlobalUNOConstant(const OUString &rName, const css::uno::Any &_rValue, css::uno::Any *pOldValue=nullptr)
sets a global constant in the basic library, referring to some UNO object, to a new value.
Definition: basmgr.cxx:1373
void SetStorageName(const OUString &rName)
Definition: basmgr.hxx:132
static DialogMask HandleError(ErrCode nId, weld::Window *pParent=nullptr, DialogMask nMask=DialogMask::MAX)
OUString getName(sal_Int32 nIndex=LAST_SEGMENT, bool bIgnoreFinalSlash=true, DecodeMechanism eMechanism=DecodeMechanism::ToIUri, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
bool insertName(std::u16string_view rTheName, bool bAppendFinalSlash=false, sal_Int32 nIndex=LAST_SEGMENT, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8)
OUString PathToFileName() const
INetProtocol GetProtocol() const
void SetFlag(SbxFlagBits n)
Definition: sbxcore.hxx:108
SfxHintId GetId() const
void StartListening(SfxBroadcaster &rBroadcaster, DuplicateHandling eDuplicateHanding=DuplicateHandling::Unexpected)
void EndListening(SfxBroadcaster &rBroadcaster, bool bRemoveAllDuplicates=false)
void SetBasicPath(const OUString &rPath)
const OUString & GetBasicPath() const
specifies a callback for instances which are interested in BasicManagers created by the BasicManagerR...
static void resetApplicationBasicManager()
resets the application-wide BasicManager to <NULL>
static BasicManager * getApplicationBasicManager()
returns the application-wide BasicManager
static void registerCreationListener(BasicManagerCreationListener &_rListener)
registers a BasicManagerCreationListener instance which is notified whenever the repository creates a...
static void revokeCreationListener(BasicManagerCreationListener &_rListener)
revokes a BasicManagerCreationListener instance which has previously been registered to be notified a...
static BasicManager * getDocumentBasicManager(const css::uno::Reference< css::frame::XModel > &_rxDocumentModel)
returns the BasicManager belonging to the given document
static BasicManager * getApplicationBasicManager()
BasicManager * getOrCreateApplicationBasicManager()
bool impl_createManagerForModel(BasicManagerStore::iterator location, const Reference< XModel > &_rxDocumentModel)
creates a new BasicManager instance for the given model
static void setApplicationBasicManager(std::unique_ptr< BasicManager > _pBasicManager)
static bool impl_getDocumentStorage_nothrow(const Reference< XModel > &_rxDocument, Reference< XStorage > &_out_rStorage)
retrieves the current storage of a given document
virtual void Notify(SfxBroadcaster &_rBC, const SfxHint &_rHint) override
void registerCreationListener(BasicManagerCreationListener &_rListener)
BasicManager * getDocumentBasicManager(const Reference< XModel > &_rxDocumentModel)
static void impl_initDocLibraryContainers_nothrow(const Reference< XPersistentLibraryContainer > &_rxBasicLibraries, const Reference< XPersistentLibraryContainer > &_rxDialogLibraries)
initializes the given library containers, which belong to a document
static bool impl_getDocumentLibraryContainers_nothrow(const Reference< XModel > &_rxDocument, Reference< XPersistentLibraryContainer > &_out_rxBasicLibraries, Reference< XPersistentLibraryContainer > &_out_rxDialogLibraries)
retrieves the containers for Basic and Dialog libraries for a given document
void revokeCreationListener(BasicManagerCreationListener &_rListener)
static ImplRepository & Instance()
CreationListeners m_aCreationListeners
BasicManager * impl_createApplicationBasicManager()
creates the application-wide BasicManager
virtual void _disposing(const css::lang::EventObject &_rSource) override
void impl_removeFromRepository(const BasicManagerStore::iterator &_pos)
removes the Model/BasicManager pair given by iterator from our store
StarBASIC * impl_getDefaultAppBasicLibrary()
BasicManagerStore::iterator impl_getLocationForModel(const Reference< XModel > &_rxDocumentModel)
retrieves the location at which the BasicManager for the given model is stored.
void impl_notifyCreationListeners(const Reference< XModel > &_rxDocumentModel, BasicManager &_rManager)
notifies all listeners which expressed interest in the creation of BasicManager instances.
bool impl_hasLocationForModel(const Reference< XModel > &_rxDocumentModel) const
tests if there is a location set at which the BasicManager for the given model is stored.
T * get() const
void startComponentListening(const css::uno::Reference< css::lang::XComponent > &_rxComp)
void stopComponentListening(const css::uno::Reference< css::lang::XComponent > &_rxComp)
#define DBG_ASSERT(sCon, aError)
#define DBG_UNHANDLED_EXCEPTION(...)
float u
std::map< Reference< XInterface >, std::unique_ptr< BasicManager > > BasicManagerStore
std::vector< BasicManagerCreationListener * > CreationListeners
@ Exception
std::basic_string_view< charT, traits > getToken(std::basic_string_view< charT, traits > sv, charT delimiter, std::size_t &position)
OString OUStringToOString(std::u16string_view str, ConnectionSettings const *settings)
std::mutex aMutex
SbiGlobals * GetSbData()
Definition: sbintern.cxx:26
#define ERRCTX_SFX_LOADBASIC
basic::SfxScriptLibraryContainer * mpOldBasicPassword
Definition: basmgr.hxx:76
tools::SvRef< SvRefBase > mrImplRepository
Definition: sbxbase.hxx:42
Reference< XModel > xModel
size_t pos