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 <basic/sbuno.hxx>
25 #include "sbintern.hxx"
27 #include <com/sun/star/beans/XPropertySet.hpp>
28 #include <com/sun/star/document/XStorageBasedDocument.hpp>
29 #include <com/sun/star/document/XEmbeddedScripts.hpp>
30 #include <com/sun/star/frame/Desktop.hpp>
31 #include <svtools/ehdl.hxx>
32 #include <svtools/sfxecode.hxx>
33 #include <unotools/pathoptions.hxx>
34 #include <svl/smplhint.hxx>
35 #include <vcl/svapp.hxx>
36 #include <tools/debug.hxx>
37 #include <tools/diagnose_ex.h>
38 #include <tools/urlobj.hxx>
39 #include <comphelper/stl_types.hxx>
40 #include <comphelper/processfactory.hxx>
41 #include <comphelper/documentinfo.hxx>
42 #include <unotools/eventlisteneradapter.hxx>
44 #include <osl/getglobalmutex.hxx>
45 #include <rtl/instance.hxx>
46 #include <rtl/strbuf.hxx>
55 using ::com::sun::star::uno::Reference
;
56 using ::com::sun::star::uno::XComponentContext
;
57 using ::com::sun::star::frame::XModel
;
58 using ::com::sun::star::frame::Desktop
;
59 using ::com::sun::star::uno::XInterface
;
60 using ::com::sun::star::uno::UNO_QUERY
;
61 using ::com::sun::star::embed::XStorage
;
62 using ::com::sun::star::script::XPersistentLibraryContainer
;
63 using ::com::sun::star::uno::Any
;
64 using ::com::sun::star::lang::XMultiServiceFactory
;
65 using ::com::sun::star::uno::UNO_QUERY_THROW
;
66 using ::com::sun::star::beans::XPropertySet
;
67 using ::com::sun::star::uno::Exception
;
68 using ::com::sun::star::document::XStorageBasedDocument
;
69 using ::com::sun::star::lang::XComponent
;
70 using ::com::sun::star::document::XEmbeddedScripts
;
72 typedef ::std::map
< Reference
< XInterface
>, BasicManager
*, ::comphelper::OInterfaceCompare
< XInterface
> > BasicManagerStore
;
74 typedef ::std::vector
< BasicManagerCreationListener
* > CreationListeners
;
76 class ImplRepository
: public ::utl::OEventListenerAdapter
, public SfxListener
79 friend struct CreateImplRepository
;
83 BasicManagerStore m_aStore
;
84 CreationListeners m_aCreationListeners
;
87 static ImplRepository
& Instance();
89 BasicManager
* getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
);
90 BasicManager
* getApplicationBasicManager( bool _bCreate
);
91 void setApplicationBasicManager( BasicManager
* _pBasicManager
);
92 void registerCreationListener( BasicManagerCreationListener
& _rListener
);
93 void revokeCreationListener( BasicManagerCreationListener
& _rListener
);
96 /** retrieves the location at which the BasicManager for the given model
99 If previously, the BasicManager for this model has never been requested,
100 then the model is added to the map, with an initial NULL BasicManager.
102 @param _rxDocumentModel
103 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
109 impl_getLocationForModel( const Reference
< XModel
>& _rxDocumentModel
);
111 /** creates a new BasicManager instance for the given model
113 @param _out_rpBasicManager
114 reference to the pointer variable that will hold the new
117 @param _rxDocumentModel
118 the model whose BasicManager will be created. Must not be <NULL/>.
120 void impl_createManagerForModel(
121 BasicManager
*& _out_rpBasicManager
,
122 const Reference
< XModel
>& _rxDocumentModel
);
124 /** creates the application-wide BasicManager
126 BasicManager
* impl_createApplicationBasicManager();
128 /** notifies all listeners which expressed interest in the creation of BasicManager instances.
130 void impl_notifyCreationListeners(
131 const Reference
< XModel
>& _rxDocumentModel
,
132 BasicManager
& _rManager
135 /** retrieves the current storage of a given document
138 the document whose storage is to be retrieved.
141 takes the storage upon successful return. Note that this might be <NULL/> even
142 if <TRUE/> is returned. In this case, the document has not yet been saved.
145 <TRUE/> if the storage could be successfully retrieved (in which case
146 <arg>_out_rStorage</arg> might or might not be <NULL/>), <FALSE/> otherwise.
147 In the latter case, processing this document should stop.
149 static bool impl_getDocumentStorage_nothrow( const Reference
< XModel
>& _rxDocument
, Reference
< XStorage
>& _out_rStorage
);
151 /** retrieves the containers for Basic and Dialog libraries for a given document
154 the document whose containers are to be retrieved.
156 @param _out_rxBasicLibraries
157 takes the basic library container upon successful return
159 @param _out_rxDialogLibraries
160 takes the dialog library container upon successful return
163 <TRUE/> if and only if both containers exist, and could successfully be retrieved
165 static bool impl_getDocumentLibraryContainers_nothrow(
166 const Reference
< XModel
>& _rxDocument
,
167 Reference
< XPersistentLibraryContainer
>& _out_rxBasicLibraries
,
168 Reference
< XPersistentLibraryContainer
>& _out_rxDialogLibraries
171 /** initializes the given library containers, which belong to a document
173 static void impl_initDocLibraryContainers_nothrow(
174 const Reference
< XPersistentLibraryContainer
>& _rxBasicLibraries
,
175 const Reference
< XPersistentLibraryContainer
>& _rxDialogLibraries
178 // OEventListenerAdapter overridables
179 virtual void _disposing( const ::com::sun::star::lang::EventObject
& _rSource
) SAL_OVERRIDE
;
181 // SfxListener overridables
182 virtual void Notify( SfxBroadcaster
& _rBC
, const SfxHint
& _rHint
) SAL_OVERRIDE
;
184 /** removes the Model/BasicManager pair given by iterator from our store
186 void impl_removeFromRepository( BasicManagerStore::iterator _pos
);
189 StarBASIC
* impl_getDefaultAppBasicLibrary();
193 struct CreateImplRepository
195 ImplRepository
* operator()()
197 static ImplRepository
* pRepository
= new ImplRepository
;
203 ImplRepository::ImplRepository()
208 ImplRepository
& ImplRepository::Instance()
210 return *rtl_Instance
< ImplRepository
, CreateImplRepository
, ::osl::MutexGuard
, ::osl::GetGlobalMutex
>::
211 create( CreateImplRepository(), ::osl::GetGlobalMutex() );
215 BasicManager
* ImplRepository::getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
)
219 /* #163556# (DR) - This function may be called recursively while
220 constructing the Basic manager and loading the Basic storage. By
221 passing the map entry received from impl_getLocationForModel() to
222 the function impl_createManagerForModel(), the new Basic manager
223 will be put immediately into the map of existing Basic managers,
224 thus a recursive call of this function will find and return it
225 without creating another instance.
227 BasicManager
*& pBasicManager
= impl_getLocationForModel( _rxDocumentModel
);
228 if ( pBasicManager
== NULL
)
229 impl_createManagerForModel( pBasicManager
, _rxDocumentModel
);
231 return pBasicManager
;
235 BasicManager
* ImplRepository::getApplicationBasicManager( bool _bCreate
)
239 BasicManager
* pAppManager
= GetSbData()->pAppBasMgr
;
240 if ( ( pAppManager
== NULL
) && _bCreate
)
241 pAppManager
= impl_createApplicationBasicManager();
247 void ImplRepository::setApplicationBasicManager( BasicManager
* _pBasicManager
)
251 BasicManager
* pPreviousManager
= getApplicationBasicManager( false );
252 delete pPreviousManager
;
254 GetSbData()->pAppBasMgr
= _pBasicManager
;
258 BasicManager
* ImplRepository::impl_createApplicationBasicManager()
262 OSL_PRECOND( getApplicationBasicManager( false ) == NULL
, "ImplRepository::impl_createApplicationBasicManager: there already is one!" );
264 // Determine Directory
265 SvtPathOptions aPathCFG
;
266 OUString
aAppBasicDir( aPathCFG
.GetBasicPath() );
267 if ( aAppBasicDir
.isEmpty() )
269 aPathCFG
.SetBasicPath(OUString("$(prog)"));
272 // Create basic and load it
273 // AppBasicDir is now a PATH
274 INetURLObject
aAppBasic( SvtPathOptions().SubstituteVariable(OUString("$(progurl)")) );
275 aAppBasic
.insertName( Application::GetAppName() );
277 BasicManager
* pBasicManager
= new BasicManager( new StarBASIC
, &aAppBasicDir
);
278 setApplicationBasicManager( pBasicManager
);
280 // The first dir in the path as destination:
281 OUString
aFileName( aAppBasic
.getName() );
282 aAppBasic
= INetURLObject( aAppBasicDir
.getToken(1, ';') );
283 DBG_ASSERT(aAppBasic
.GetProtocol() != INetProtocol::NotValid
,
284 OString("Invalid URL: \"" +
285 OUStringToOString(aAppBasicDir
, osl_getThreadTextEncoding()) +
287 aAppBasic
.insertName( aFileName
);
288 pBasicManager
->SetStorageName( aAppBasic
.PathToFileName() );
291 SfxScriptLibraryContainer
* pBasicCont
= new SfxScriptLibraryContainer( Reference
< XStorage
>() );
292 Reference
< XPersistentLibraryContainer
> xBasicCont( pBasicCont
);
293 pBasicCont
->setBasicManager( pBasicManager
);
296 SfxDialogLibraryContainer
* pDialogCont
= new SfxDialogLibraryContainer( Reference
< XStorage
>() );
297 Reference
< XPersistentLibraryContainer
> xDialogCont( pDialogCont
);
299 LibraryContainerInfo
aInfo( xBasicCont
, xDialogCont
, static_cast< OldBasicPassword
* >( pBasicCont
) );
300 pBasicManager
->SetLibraryContainerInfo( aInfo
);
305 Reference
< XComponentContext
> xContext
= ::comphelper::getProcessComponentContext();
306 pBasicManager
->SetGlobalUNOConstant( "StarDesktop", makeAny( Desktop::create(xContext
)));
308 // (BasicLibraries and DialogLibraries have automatically been added in SetLibraryContainerInfo)
311 impl_notifyCreationListeners( NULL
, *pBasicManager
);
314 return pBasicManager
;
318 void ImplRepository::registerCreationListener( BasicManagerCreationListener
& _rListener
)
322 m_aCreationListeners
.push_back( &_rListener
);
326 void ImplRepository::revokeCreationListener( BasicManagerCreationListener
& _rListener
)
330 CreationListeners::iterator pos
= ::std::find( m_aCreationListeners
.begin(), m_aCreationListeners
.end(), &_rListener
);
331 if ( pos
!= m_aCreationListeners
.end() )
332 m_aCreationListeners
.erase( pos
);
334 OSL_FAIL( "ImplRepository::revokeCreationListener: listener is not registered!" );
339 void ImplRepository::impl_notifyCreationListeners( const Reference
< XModel
>& _rxDocumentModel
, BasicManager
& _rManager
)
341 for ( CreationListeners::const_iterator loop
= m_aCreationListeners
.begin();
342 loop
!= m_aCreationListeners
.end();
346 (*loop
)->onBasicManagerCreated( _rxDocumentModel
, _rManager
);
351 StarBASIC
* ImplRepository::impl_getDefaultAppBasicLibrary()
353 BasicManager
* pAppManager
= getApplicationBasicManager( true );
355 StarBASIC
* pAppBasic
= pAppManager
? pAppManager
->GetLib(0) : NULL
;
356 DBG_ASSERT( pAppBasic
!= NULL
, "impl_getApplicationBasic: unable to determine the default application's Basic library!" );
361 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 BasicManager
*& location
= m_aStore
[ xNormalized
];
371 void ImplRepository::impl_initDocLibraryContainers_nothrow( const Reference
< XPersistentLibraryContainer
>& _rxBasicLibraries
, const Reference
< XPersistentLibraryContainer
>& _rxDialogLibraries
)
373 OSL_PRECOND( _rxBasicLibraries
.is() && _rxDialogLibraries
.is(),
374 "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" );
378 // ensure there's a standard library in the basic container
379 OUString
aStdLibName( "Standard" );
380 if ( !_rxBasicLibraries
->hasByName( aStdLibName
) )
382 _rxBasicLibraries
->createLibrary( aStdLibName
);
384 // as well as in the dialog container
385 if ( !_rxDialogLibraries
->hasByName( aStdLibName
) )
387 _rxDialogLibraries
->createLibrary( aStdLibName
);
390 catch( const Exception
& )
392 DBG_UNHANDLED_EXCEPTION();
397 void ImplRepository::impl_createManagerForModel( BasicManager
*& _out_rpBasicManager
, const Reference
< XModel
>& _rxDocumentModel
)
399 StarBASIC
* pAppBasic
= impl_getDefaultAppBasicLibrary();
401 _out_rpBasicManager
= 0;
402 Reference
< XStorage
> xStorage
;
403 if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel
, xStorage
) )
405 // the document is not able to provide the storage it is based on.
408 Reference
< XPersistentLibraryContainer
> xBasicLibs
;
409 Reference
< XPersistentLibraryContainer
> xDialogLibs
;
410 if ( !impl_getDocumentLibraryContainers_nothrow( _rxDocumentModel
, xBasicLibs
, xDialogLibs
) )
411 // the document does not have BasicLibraries and DialogLibraries
416 // load BASIC-manager
417 SfxErrorContext
aErrContext( ERRCTX_SFX_LOADBASIC
,
418 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel
) );
419 OUString aAppBasicDir
= SvtPathOptions().GetBasicPath();
421 // Storage and BaseURL are only needed by binary documents!
422 tools::SvRef
<SotStorage
> xDummyStor
= new SotStorage( OUString() );
423 _out_rpBasicManager
= new BasicManager( *xDummyStor
, OUString() /* TODO/LATER: xStorage */,
425 &aAppBasicDir
, true );
426 if ( !_out_rpBasicManager
->GetErrors().empty() )
429 std::vector
<BasicError
>& aErrors
= _out_rpBasicManager
->GetErrors();
430 for(std::vector
<BasicError
>::const_iterator i
= aErrors
.begin(); i
!= aErrors
.end(); ++i
)
432 // show message to user
433 if ( ERRCODE_BUTTON_CANCEL
== ErrorHandler::HandleError( i
->GetErrorId() ) )
435 // user wants to break loading of BASIC-manager
436 delete _out_rpBasicManager
;
437 _out_rpBasicManager
= NULL
;
446 if ( !xStorage
.is() )
448 // create new BASIC-manager
449 StarBASIC
* pBasic
= new StarBASIC( pAppBasic
);
450 pBasic
->SetFlag( SBX_EXTSEARCH
);
451 _out_rpBasicManager
= new BasicManager( pBasic
, NULL
, true );
454 // knit the containers with the BasicManager
455 LibraryContainerInfo
aInfo( xBasicLibs
, xDialogLibs
, dynamic_cast< OldBasicPassword
* >( xBasicLibs
.get() ) );
456 OSL_ENSURE( aInfo
.mpOldBasicPassword
, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" );
457 _out_rpBasicManager
->SetLibraryContainerInfo( aInfo
);
459 // initialize the containers
460 impl_initDocLibraryContainers_nothrow( xBasicLibs
, xDialogLibs
);
462 // so that also dialogs etc. could be 'qualified' addressed
463 _out_rpBasicManager
->GetLib(0)->SetParent( pAppBasic
);
465 // global properties in the document's Basic
466 _out_rpBasicManager
->SetGlobalUNOConstant( "ThisComponent", makeAny( _rxDocumentModel
) );
469 impl_notifyCreationListeners( _rxDocumentModel
, *_out_rpBasicManager
);
471 // register as listener for this model being disposed/closed
472 OSL_ENSURE( _rxDocumentModel
.is(), "ImplRepository::impl_createManagerForModel: the document must be an XComponent!" );
473 startComponentListening( _rxDocumentModel
);
475 // register as listener for the BasicManager being destroyed
476 StartListening( *_out_rpBasicManager
);
478 // #i104876: Library container must not be modified just after
479 // creation. This happens as side effect when creating default
480 // "Standard" libraries and needs to be corrected here
481 xBasicLibs
->setModified( sal_False
);
482 xDialogLibs
->setModified( sal_False
);
487 bool ImplRepository::impl_getDocumentStorage_nothrow( const Reference
< XModel
>& _rxDocument
, Reference
< XStorage
>& _out_rStorage
)
489 _out_rStorage
.clear();
492 Reference
< XStorageBasedDocument
> xStorDoc( _rxDocument
, UNO_QUERY_THROW
);
493 _out_rStorage
.set( xStorDoc
->getDocumentStorage() );
495 catch( const Exception
& )
497 DBG_UNHANDLED_EXCEPTION();
504 bool ImplRepository::impl_getDocumentLibraryContainers_nothrow( const Reference
< XModel
>& _rxDocument
,
505 Reference
< XPersistentLibraryContainer
>& _out_rxBasicLibraries
, Reference
< XPersistentLibraryContainer
>& _out_rxDialogLibraries
)
507 _out_rxBasicLibraries
.clear();
508 _out_rxDialogLibraries
.clear();
511 Reference
< XEmbeddedScripts
> xScripts( _rxDocument
, UNO_QUERY_THROW
);
512 _out_rxBasicLibraries
.set( xScripts
->getBasicLibraries(), UNO_QUERY_THROW
);
513 _out_rxDialogLibraries
.set( xScripts
->getDialogLibraries(), UNO_QUERY_THROW
);
515 catch( const Exception
& )
517 DBG_UNHANDLED_EXCEPTION();
519 return _out_rxBasicLibraries
.is() && _out_rxDialogLibraries
.is();
523 void ImplRepository::impl_removeFromRepository( BasicManagerStore::iterator _pos
)
525 OSL_PRECOND( _pos
!= m_aStore
.end(), "ImplRepository::impl_removeFromRepository: invalid position!" );
527 BasicManager
* pManager
= _pos
->second
;
529 // *first* remove from map (else Notify won't work properly)
530 m_aStore
.erase( _pos
);
532 // *then* delete the BasicManager
533 EndListening( *pManager
);
538 void ImplRepository::_disposing( const ::com::sun::star::lang::EventObject
& _rSource
)
542 Reference
< XInterface
> xNormalizedSource( _rSource
.Source
, UNO_QUERY
);
543 #if OSL_DEBUG_LEVEL > 0
547 for ( BasicManagerStore::iterator loop
= m_aStore
.begin();
548 loop
!= m_aStore
.end();
552 if ( loop
->first
.get() == xNormalizedSource
.get() )
554 impl_removeFromRepository( loop
);
555 #if OSL_DEBUG_LEVEL > 0
562 OSL_ENSURE( bFound
, "ImplRepository::_disposing: where does this come from?" );
566 void ImplRepository::Notify( SfxBroadcaster
& _rBC
, const SfxHint
& _rHint
)
568 const SfxSimpleHint
* pSimpleHint
= dynamic_cast< const SfxSimpleHint
* >( &_rHint
);
569 if ( !pSimpleHint
|| ( pSimpleHint
->GetId() != SFX_HINT_DYING
) )
573 BasicManager
* pManager
= dynamic_cast< BasicManager
* >( &_rBC
);
574 OSL_ENSURE( pManager
, "ImplRepository::Notify: where does this come from?" );
576 for ( BasicManagerStore::iterator loop
= m_aStore
.begin();
577 loop
!= m_aStore
.end();
581 if ( loop
->second
== pManager
)
583 // a BasicManager which is still in our repository is being deleted.
584 // That's bad, since by definition, we *own* all instances in our
586 OSL_FAIL( "ImplRepository::Notify: nobody should tamper with the managers, except ourself!" );
587 m_aStore
.erase( loop
);
594 BasicManager
* BasicManagerRepository::getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
)
596 return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel
);
600 BasicManager
* BasicManagerRepository::getApplicationBasicManager( bool _bCreate
)
602 return ImplRepository::Instance().getApplicationBasicManager( _bCreate
);
606 void BasicManagerRepository::resetApplicationBasicManager()
608 return ImplRepository::Instance().setApplicationBasicManager( NULL
);
612 void BasicManagerRepository::registerCreationListener( BasicManagerCreationListener
& _rListener
)
614 ImplRepository::Instance().registerCreationListener( _rListener
);
618 void BasicManagerRepository::revokeCreationListener( BasicManagerCreationListener
& _rListener
)
620 ImplRepository::Instance().revokeCreationListener( _rListener
);
627 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */