1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: basicmanagerrepository.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_basic.hxx"
33 #include <basic/basicmanagerrepository.hxx>
34 #include <basic/basmgr.hxx>
35 #include "scriptcont.hxx"
36 #include "dlgcont.hxx"
37 #include <basic/sbuno.hxx>
38 #include "sbintern.hxx"
40 /** === begin UNO includes === **/
41 #include <com/sun/star/beans/XPropertySet.hpp>
42 #include <com/sun/star/document/XStorageBasedDocument.hpp>
43 #include <com/sun/star/document/XEmbeddedScripts.hpp>
44 /** === end UNO includes === **/
45 #include <svtools/ehdl.hxx>
46 #include <svtools/sfxecode.hxx>
47 #include <svtools/pathoptions.hxx>
48 #include <svtools/smplhint.hxx>
49 #include <vcl/svapp.hxx>
50 #include <tools/debug.hxx>
51 #include <tools/diagnose_ex.h>
52 #include <tools/urlobj.hxx>
53 #include <comphelper/stl_types.hxx>
54 #include <comphelper/processfactory.hxx>
55 #include <comphelper/documentinfo.hxx>
56 #include <unotools/eventlisteneradapter.hxx>
58 #ifndef INCLUDED_OSL_DOUBLECHECKEDLOCKING_H
59 #include <rtl/instance.hxx>
64 //........................................................................
67 //........................................................................
69 /** === begin UNO using === **/
70 using ::com::sun::star::uno::Reference
;
71 using ::com::sun::star::frame::XModel
;
72 using ::com::sun::star::uno::XInterface
;
73 using ::com::sun::star::uno::UNO_QUERY
;
74 using ::com::sun::star::embed::XStorage
;
75 using ::com::sun::star::script::XPersistentLibraryContainer
;
76 using ::com::sun::star::uno::Any
;
77 using ::com::sun::star::lang::XMultiServiceFactory
;
78 using ::com::sun::star::uno::UNO_QUERY_THROW
;
79 using ::com::sun::star::beans::XPropertySet
;
80 using ::com::sun::star::uno::Exception
;
81 using ::com::sun::star::document::XStorageBasedDocument
;
82 using ::com::sun::star::lang::XComponent
;
83 using ::com::sun::star::document::XEmbeddedScripts
;
84 /** === end UNO using === **/
86 typedef BasicManager
* BasicManagerPointer
;
87 typedef ::std::map
< Reference
< XInterface
>, BasicManagerPointer
, ::comphelper::OInterfaceCompare
< XInterface
> > BasicManagerStore
;
89 typedef ::std::vector
< BasicManagerCreationListener
* > CreationListeners
;
91 //====================================================================
92 //= BasicManagerCleaner
93 //====================================================================
94 /// is the only instance which is allowed to delete a BasicManager instance
95 class BasicManagerCleaner
98 static void deleteBasicManager( BasicManager
*& _rpManager
)
105 //====================================================================
107 //====================================================================
108 class ImplRepository
: public ::utl::OEventListenerAdapter
, public SfxListener
111 friend struct CreateImplRepository
;
115 ::osl::Mutex m_aMutex
;
116 BasicManagerStore m_aStore
;
117 CreationListeners m_aCreationListeners
;
120 static ImplRepository
& Instance();
122 BasicManager
* getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
);
123 BasicManager
* getApplicationBasicManager( bool _bCreate
);
124 void setApplicationBasicManager( BasicManager
* _pBasicManager
);
125 void registerCreationListener( BasicManagerCreationListener
& _rListener
);
126 void revokeCreationListener( BasicManagerCreationListener
& _rListener
);
129 /** retrieves the location at which the BasicManager for the given model
132 If previously, the BasicManager for this model has never been requested,
133 then the model is added to the map, with an initial NULL BasicManager.
135 @param _rxDocumentModel
136 the model whose BasicManager's location is to be retrieved. Must not be <NULL/>.
142 impl_getLocationForModel( const Reference
< XModel
>& _rxDocumentModel
);
144 /** creates a new BasicManager instance for the given model
147 impl_createManagerForModel( const Reference
< XModel
>& _rxDocumentModel
);
149 /** creates the application-wide BasicManager
151 BasicManagerPointer
impl_createApplicationBasicManager();
153 /** notifies all listeners which expressed interest in the creation of BasicManager instances.
155 void impl_notifyCreationListeners(
156 const Reference
< XModel
>& _rxDocumentModel
,
157 BasicManager
& _rManager
160 /** retrieves the current storage of a given document
163 the document whose storage is to be retrieved.
166 takes the storage upon successful return. Note that this might be <NULL/> even
167 if <TRUE/> is returned. In this case, the document has not yet been saved.
170 <TRUE/> if the storage could be successfully retrieved (in which case
171 <arg>_out_rStorage</arg> might or might not be <NULL/>), <FALSE/> otherwise.
172 In the latter case, processing this document should stop.
174 bool impl_getDocumentStorage_nothrow( const Reference
< XModel
>& _rxDocument
, Reference
< XStorage
>& _out_rStorage
);
176 /** retrieves the containers for Basic and Dialog libraries for a given document
179 the document whose containers are to be retrieved.
181 @param _out_rxBasicLibraries
182 takes the basic library container upon successful return
184 @param _out_rxDialogLibraries
185 takes the dialog library container upon successful return
188 <TRUE/> if and only if both containers exist, and could successfully be retrieved
190 bool impl_getDocumentLibraryContainers_nothrow(
191 const Reference
< XModel
>& _rxDocument
,
192 Reference
< XPersistentLibraryContainer
>& _out_rxBasicLibraries
,
193 Reference
< XPersistentLibraryContainer
>& _out_rxDialogLibraries
196 /** initializes the given library containers, which belong to a document
198 void impl_initDocLibraryContainers_nothrow(
199 const Reference
< XPersistentLibraryContainer
>& _rxBasicLibraries
,
200 const Reference
< XPersistentLibraryContainer
>& _rxDialogLibraries
203 // OEventListenerAdapter overridables
204 virtual void _disposing( const ::com::sun::star::lang::EventObject
& _rSource
);
206 // SfxListener overridables
207 virtual void Notify( SfxBroadcaster
& _rBC
, const SfxHint
& _rHint
);
209 /** removes the Model/BasicManager pair given by iterator from our store
211 void impl_removeFromRepository( BasicManagerStore::iterator _pos
);
214 StarBASIC
* impl_getDefaultAppBasicLibrary();
217 //====================================================================
218 //= CreateImplRepository
219 //====================================================================
220 struct CreateImplRepository
222 ImplRepository
* operator()()
224 static ImplRepository
* pRepository
= new ImplRepository
;
230 //====================================================================
232 //====================================================================
233 //--------------------------------------------------------------------
234 ImplRepository::ImplRepository()
238 //--------------------------------------------------------------------
239 ImplRepository
& ImplRepository::Instance()
241 return *rtl_Instance
< ImplRepository
, CreateImplRepository
, ::osl::MutexGuard
, ::osl::GetGlobalMutex
>::
242 create( CreateImplRepository(), ::osl::GetGlobalMutex() );
245 //--------------------------------------------------------------------
246 BasicManager
* ImplRepository::getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
)
248 ::osl::MutexGuard
aGuard( m_aMutex
);
250 BasicManagerPointer
& pBasicManager
= impl_getLocationForModel( _rxDocumentModel
);
251 if ( pBasicManager
== NULL
)
252 pBasicManager
= impl_createManagerForModel( _rxDocumentModel
);
254 return pBasicManager
;
257 //--------------------------------------------------------------------
258 BasicManager
* ImplRepository::getApplicationBasicManager( bool _bCreate
)
260 ::osl::MutexGuard
aGuard( m_aMutex
);
262 BasicManager
* pAppManager
= GetSbData()->pAppBasMgr
;
263 if ( ( pAppManager
== NULL
) && _bCreate
)
264 pAppManager
= impl_createApplicationBasicManager();
269 //--------------------------------------------------------------------
270 void ImplRepository::setApplicationBasicManager( BasicManager
* _pBasicManager
)
272 ::osl::MutexGuard
aGuard( m_aMutex
);
274 BasicManager
* pPreviousManager
= getApplicationBasicManager( false );
275 BasicManagerCleaner::deleteBasicManager( pPreviousManager
);
277 GetSbData()->pAppBasMgr
= _pBasicManager
;
280 //--------------------------------------------------------------------
281 BasicManager
* ImplRepository::impl_createApplicationBasicManager()
283 ::osl::MutexGuard
aGuard( m_aMutex
);
284 OSL_PRECOND( getApplicationBasicManager( false ) == NULL
, "ImplRepository::impl_createApplicationBasicManager: there already is one!" );
286 // Determine Directory
287 SvtPathOptions aPathCFG
;
288 String
aAppBasicDir( aPathCFG
.GetBasicPath() );
289 if ( !aAppBasicDir
.Len() )
290 aPathCFG
.SetBasicPath( String::CreateFromAscii("$(prog)") );
292 // #58293# soffice.new search only in user dir => first dir
293 String aAppFirstBasicDir
= aAppBasicDir
.GetToken(1);
295 // Create basic and load it
296 // MT: #47347# AppBasicDir is now a PATH
297 INetURLObject
aAppBasic( SvtPathOptions().SubstituteVariable( String::CreateFromAscii("$(progurl)") ) );
298 aAppBasic
.insertName( Application::GetAppName() );
300 BasicManager
* pBasicManager
= new BasicManager( new StarBASIC
, &aAppBasicDir
);
301 setApplicationBasicManager( pBasicManager
);
303 // Als Destination das erste Dir im Pfad:
304 String
aFileName( aAppBasic
.getName() );
305 aAppBasic
= INetURLObject( aAppBasicDir
.GetToken(1) );
306 DBG_ASSERT( aAppBasic
.GetProtocol() != INET_PROT_NOT_VALID
, "Invalid URL!" );
307 aAppBasic
.insertName( aFileName
);
308 pBasicManager
->SetStorageName( aAppBasic
.PathToFileName() );
311 SfxScriptLibraryContainer
* pBasicCont
= new SfxScriptLibraryContainer( Reference
< XStorage
>() );
312 Reference
< XPersistentLibraryContainer
> xBasicCont( pBasicCont
);
313 pBasicCont
->setBasicManager( pBasicManager
);
316 SfxDialogLibraryContainer
* pDialogCont
= new SfxDialogLibraryContainer( Reference
< XStorage
>() );
317 Reference
< XPersistentLibraryContainer
> xDialogCont( pDialogCont
);
319 LibraryContainerInfo
aInfo( xBasicCont
, xDialogCont
, static_cast< OldBasicPassword
* >( pBasicCont
) );
320 pBasicManager
->SetLibraryContainerInfo( aInfo
);
325 Reference
< XMultiServiceFactory
> xSMgr
= ::comphelper::getProcessServiceFactory();
326 pBasicManager
->SetGlobalUNOConstant(
328 makeAny( xSMgr
->createInstance( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.frame.Desktop" ) ) ) )
331 // (BasicLibraries and DialogLibraries have automatically been added in SetLibraryContainerInfo)
334 impl_notifyCreationListeners( NULL
, *pBasicManager
);
337 return pBasicManager
;
340 //--------------------------------------------------------------------
341 void ImplRepository::registerCreationListener( BasicManagerCreationListener
& _rListener
)
343 ::osl::MutexGuard
aGuard( m_aMutex
);
344 m_aCreationListeners
.push_back( &_rListener
);
347 //--------------------------------------------------------------------
348 void ImplRepository::revokeCreationListener( BasicManagerCreationListener
& _rListener
)
350 ::osl::MutexGuard
aGuard( m_aMutex
);
351 CreationListeners::iterator pos
= ::std::find( m_aCreationListeners
.begin(), m_aCreationListeners
.end(), &_rListener
);
352 if ( pos
!= m_aCreationListeners
.end() )
353 m_aCreationListeners
.erase( pos
);
355 DBG_ERROR( "ImplRepository::revokeCreationListener: listener is not registered!" );
359 //--------------------------------------------------------------------
360 void ImplRepository::impl_notifyCreationListeners( const Reference
< XModel
>& _rxDocumentModel
, BasicManager
& _rManager
)
362 for ( CreationListeners::const_iterator loop
= m_aCreationListeners
.begin();
363 loop
!= m_aCreationListeners
.end();
367 (*loop
)->onBasicManagerCreated( _rxDocumentModel
, _rManager
);
371 //--------------------------------------------------------------------
372 StarBASIC
* ImplRepository::impl_getDefaultAppBasicLibrary()
374 BasicManager
* pAppManager
= getApplicationBasicManager( true );
376 StarBASIC
* pAppBasic
= pAppManager
? pAppManager
->GetLib(0) : NULL
;
377 DBG_ASSERT( pAppBasic
!= NULL
, "impl_getApplicationBasic: unable to determine the default application's Basic library!" );
381 //--------------------------------------------------------------------
382 BasicManagerPointer
& ImplRepository::impl_getLocationForModel( const Reference
< XModel
>& _rxDocumentModel
)
384 Reference
< XInterface
> xNormalized( _rxDocumentModel
, UNO_QUERY
);
385 DBG_ASSERT( xNormalized
.is(), "ImplRepository::impl_getLocationForModel: invalid model!" );
387 BasicManagerPointer
& location
= m_aStore
[ xNormalized
];
391 //--------------------------------------------------------------------
392 void ImplRepository::impl_initDocLibraryContainers_nothrow( const Reference
< XPersistentLibraryContainer
>& _rxBasicLibraries
, const Reference
< XPersistentLibraryContainer
>& _rxDialogLibraries
)
394 OSL_PRECOND( _rxBasicLibraries
.is() && _rxDialogLibraries
.is(),
395 "ImplRepository::impl_initDocLibraryContainers_nothrow: illegal library containers, this will crash!" );
399 // ensure there's a standard library in the basic container
400 ::rtl::OUString
aStdLibName( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) );
401 if ( !_rxBasicLibraries
->hasByName( aStdLibName
) )
402 _rxBasicLibraries
->createLibrary( aStdLibName
);
403 // as well as in the dialog container
404 if ( !_rxDialogLibraries
->hasByName( aStdLibName
) )
405 _rxDialogLibraries
->createLibrary( aStdLibName
);
407 catch( const Exception
& )
409 DBG_UNHANDLED_EXCEPTION();
413 //--------------------------------------------------------------------
414 BasicManagerPointer
ImplRepository::impl_createManagerForModel( const Reference
< XModel
>& _rxDocumentModel
)
416 StarBASIC
* pAppBasic
= impl_getDefaultAppBasicLibrary();
418 BasicManager
* pBasicManager( NULL
);
419 Reference
< XStorage
> xStorage
;
420 if ( !impl_getDocumentStorage_nothrow( _rxDocumentModel
, xStorage
) )
421 // the document is not able to provide the storage it is based on.
422 return pBasicManager
;
424 Reference
< XPersistentLibraryContainer
> xBasicLibs
;
425 Reference
< XPersistentLibraryContainer
> xDialogLibs
;
426 if ( !impl_getDocumentLibraryContainers_nothrow( _rxDocumentModel
, xBasicLibs
, xDialogLibs
) )
427 // the document does not have BasicLibraries and DialogLibraries
428 return pBasicManager
;
432 // load BASIC-manager
433 SfxErrorContext
aErrContext( ERRCTX_SFX_LOADBASIC
,
434 ::comphelper::DocumentInfo::getDocumentTitle( _rxDocumentModel
) );
435 String aAppBasicDir
= SvtPathOptions().GetBasicPath();
437 // Storage and BaseURL are only needed by binary documents!
438 SotStorageRef xDummyStor
= new SotStorage( ::rtl::OUString() );
439 pBasicManager
= new BasicManager( *xDummyStor
, String() /* TODO/LATER: xStorage */,
441 &aAppBasicDir
, TRUE
);
442 if ( pBasicManager
->HasErrors() )
445 BasicError
* pErr
= pBasicManager
->GetFirstError();
448 // show message to user
449 if ( ERRCODE_BUTTON_CANCEL
== ErrorHandler::HandleError( pErr
->GetErrorId() ) )
451 // user wants to break loading of BASIC-manager
452 BasicManagerCleaner::deleteBasicManager( pBasicManager
);
456 pErr
= pBasicManager
->GetNextError();
462 if ( !xStorage
.is() )
464 // create new BASIC-manager
465 StarBASIC
* pBasic
= new StarBASIC( pAppBasic
);
466 pBasic
->SetFlag( SBX_EXTSEARCH
);
467 pBasicManager
= new BasicManager( pBasic
, NULL
, TRUE
);
470 // knit the containers with the BasicManager
471 LibraryContainerInfo
aInfo( xBasicLibs
, xDialogLibs
, dynamic_cast< OldBasicPassword
* >( xBasicLibs
.get() ) );
472 OSL_ENSURE( aInfo
.mpOldBasicPassword
, "ImplRepository::impl_createManagerForModel: wrong BasicLibraries implementation!" );
473 pBasicManager
->SetLibraryContainerInfo( aInfo
);
474 //pBasicCont->setBasicManager( pBasicManager );
475 // that's not needed anymore today. The containers will retrieve their associated
476 // BasicManager from the BasicManagerRepository, when needed.
478 // initialize the containers
479 impl_initDocLibraryContainers_nothrow( xBasicLibs
, xDialogLibs
);
481 // damit auch Dialoge etc. 'qualifiziert' angesprochen werden k"onnen
482 pBasicManager
->GetLib(0)->SetParent( pAppBasic
);
484 // global properties in the document's Basic
485 pBasicManager
->SetGlobalUNOConstant( "ThisComponent", makeAny( _rxDocumentModel
) );
488 impl_notifyCreationListeners( _rxDocumentModel
, *pBasicManager
);
490 // register as listener for this model being disposed/closed
491 Reference
< XComponent
> xDocumentComponent( _rxDocumentModel
, UNO_QUERY
);
492 OSL_ENSURE( xDocumentComponent
.is(), "ImplRepository::impl_createManagerForModel: the document must be an XComponent!" );
493 startComponentListening( xDocumentComponent
);
495 // register as listener for the BasicManager being destroyed
496 StartListening( *pBasicManager
);
498 return pBasicManager
;
501 //--------------------------------------------------------------------
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();
518 //--------------------------------------------------------------------
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();
534 return _out_rxBasicLibraries
.is() && _out_rxDialogLibraries
.is();
537 //--------------------------------------------------------------------
538 void ImplRepository::impl_removeFromRepository( BasicManagerStore::iterator _pos
)
540 OSL_PRECOND( _pos
!= m_aStore
.end(), "ImplRepository::impl_removeFromRepository: invalid position!" );
542 BasicManager
* pManager
= _pos
->second
;
544 // *first* remove from map (else Notify won't work properly)
545 m_aStore
.erase( _pos
);
547 // *then* delete the BasicManager
548 EndListening( *pManager
);
549 BasicManagerCleaner::deleteBasicManager( pManager
);
552 //--------------------------------------------------------------------
553 void ImplRepository::_disposing( const ::com::sun::star::lang::EventObject
& _rSource
)
555 ::osl::MutexGuard
aGuard( m_aMutex
);
557 Reference
< XInterface
> xNormalizedSource( _rSource
.Source
, UNO_QUERY
);
558 #if OSL_DEBUG_LEVEL > 0
562 for ( BasicManagerStore::iterator loop
= m_aStore
.begin();
563 loop
!= m_aStore
.end();
567 if ( loop
->first
.get() == xNormalizedSource
.get() )
569 impl_removeFromRepository( loop
);
570 #if OSL_DEBUG_LEVEL > 0
577 OSL_ENSURE( bFound
, "ImplRepository::_disposing: where does this come from?" );
580 //--------------------------------------------------------------------
581 void ImplRepository::Notify( SfxBroadcaster
& _rBC
, const SfxHint
& _rHint
)
583 const SfxSimpleHint
* pSimpleHint
= dynamic_cast< const SfxSimpleHint
* >( &_rHint
);
584 if ( !pSimpleHint
|| ( pSimpleHint
->GetId() != SFX_HINT_DYING
) )
588 BasicManager
* pManager
= dynamic_cast< BasicManager
* >( &_rBC
);
589 OSL_ENSURE( pManager
, "ImplRepository::Notify: where does this come from?" );
591 for ( BasicManagerStore::iterator loop
= m_aStore
.begin();
592 loop
!= m_aStore
.end();
596 if ( loop
->second
== pManager
)
598 // a BasicManager which is still in our repository is being deleted.
599 // That's bad, since by definition, we *own* all instances in our
601 OSL_ENSURE( false, "ImplRepository::Notify: nobody should tamper with the managers, except ourself!" );
602 m_aStore
.erase( loop
);
608 //====================================================================
609 //= BasicManagerRepository
610 //====================================================================
611 //--------------------------------------------------------------------
612 BasicManager
* BasicManagerRepository::getDocumentBasicManager( const Reference
< XModel
>& _rxDocumentModel
)
614 return ImplRepository::Instance().getDocumentBasicManager( _rxDocumentModel
);
617 //--------------------------------------------------------------------
618 BasicManager
* BasicManagerRepository::getApplicationBasicManager( bool _bCreate
)
620 return ImplRepository::Instance().getApplicationBasicManager( _bCreate
);
623 //--------------------------------------------------------------------
624 void BasicManagerRepository::resetApplicationBasicManager()
626 return ImplRepository::Instance().setApplicationBasicManager( NULL
);
629 //--------------------------------------------------------------------
630 void BasicManagerRepository::registerCreationListener( BasicManagerCreationListener
& _rListener
)
632 ImplRepository::Instance().registerCreationListener( _rListener
);
635 //--------------------------------------------------------------------
636 void BasicManagerRepository::revokeCreationListener( BasicManagerCreationListener
& _rListener
)
638 ImplRepository::Instance().revokeCreationListener( _rListener
);
641 //........................................................................
643 //........................................................................