LanguageTool: don't crash if REST protocol isn't set
[LibreOffice.git] / basic / source / basmgr / basicmanagerrepository.cxx
bloba70c49eb114980cc70660bdc86b6a80383f072a5
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 rtl::Reference<SfxScriptLibraryContainer> pBasicCont = new SfxScriptLibraryContainer( Reference< XStorage >() );
296 pBasicCont->setBasicManager( pBasicManager );
298 // Dialog container
299 rtl::Reference<SfxDialogLibraryContainer> pDialogCont = new SfxDialogLibraryContainer( Reference< XStorage >() );
301 LibraryContainerInfo aInfo( pBasicCont, pDialogCont, static_cast< OldBasicPassword* >( pBasicCont.get() ) );
302 pBasicManager->SetLibraryContainerInfo( aInfo );
304 // global constants
306 // StarDesktop
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)
312 // notify
313 impl_notifyCreationListeners( nullptr, *pBasicManager );
315 // outta here
316 return pBasicManager;
320 void ImplRepository::registerCreationListener( BasicManagerCreationListener& _rListener )
322 SolarMutexGuard g;
324 m_aCreationListeners.push_back( &_rListener );
328 void ImplRepository::revokeCreationListener( BasicManagerCreationListener& _rListener )
330 SolarMutexGuard g;
332 CreationListeners::iterator pos = std::find( m_aCreationListeners.begin(), m_aCreationListeners.end(), &_rListener );
333 if ( pos != m_aCreationListeners.end() )
334 m_aCreationListeners.erase( pos );
335 else {
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!" );
356 return pAppBasic;
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.
412 return false;
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
420 return false;
423 if ( xStorage.is() )
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 */,
433 pAppBasic,
434 &aAppBasicDir, true ));
435 if ( !_out_rpBasicManager->GetErrors().empty() )
437 // handle errors
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();
446 xStorage.clear();
447 break;
453 // not loaded?
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 ) );
476 // notify
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 );
484 bool bOk = false;
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))
489 bOk = true;
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 );
499 return bOk;
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");
513 return false;
515 return true;
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 );
550 assert(xModel.is());
551 if (xModel.is())
552 stopComponentListening(xModel);
556 void ImplRepository::_disposing( const css::lang::EventObject& _rSource )
558 SolarMutexGuard g;
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 );
568 return;
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 )
578 // not interested in
579 return;
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
590 // repository.
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 );
621 } // namespace basic
624 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */