diff options
Diffstat (limited to 'svx/source/form/fmscriptingenv.cxx')
-rw-r--r-- | svx/source/form/fmscriptingenv.cxx | 522 |
1 files changed, 522 insertions, 0 deletions
diff --git a/svx/source/form/fmscriptingenv.cxx b/svx/source/form/fmscriptingenv.cxx new file mode 100644 index 000000000000..3e8aa88a2612 --- /dev/null +++ b/svx/source/form/fmscriptingenv.cxx @@ -0,0 +1,522 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2010 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +// MARKER(update_precomp.py): autogen include statement, do not remove +#include "precompiled_svx.hxx" +#include "fmscriptingenv.hxx" +#include "svx/fmmodel.hxx" + +/** === begin UNO includes === **/ +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/script/XScriptListener.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/reflection/XInterfaceMethodTypeDescription.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +/** === end UNO includes === **/ + +#include <tools/diagnose_ex.h> +#include <cppuhelper/implbase1.hxx> +#include <comphelper/implementationreference.hxx> +#include <comphelper/componentcontext.hxx> +#include <comphelper/processfactory.hxx> +#include <vcl/svapp.hxx> +#include <vos/mutex.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/app.hxx> +#include <basic/basmgr.hxx> + +#include <boost/shared_ptr.hpp> + +//........................................................................ +namespace svxform +{ +//........................................................................ + + /** === begin UNO using === **/ + using ::com::sun::star::uno::Reference; + using ::com::sun::star::script::XEventAttacherManager; + using ::com::sun::star::lang::IllegalArgumentException; + using ::com::sun::star::script::XScriptListener; + using ::com::sun::star::script::ScriptEvent; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::lang::EventObject; + using ::com::sun::star::reflection::InvocationTargetException; + using ::com::sun::star::uno::Any; + using ::com::sun::star::container::XHierarchicalNameAccess; + using ::com::sun::star::reflection::XInterfaceMethodTypeDescription; + using ::com::sun::star::uno::UNO_QUERY_THROW; + using ::com::sun::star::lang::DisposedException; + using ::com::sun::star::uno::RuntimeException; + using ::com::sun::star::uno::Exception; + using ::com::sun::star::uno::Sequence; + using ::com::sun::star::uno::XInterface; + /** === end UNO using === **/ + + class FormScriptingEnvironment; + + //==================================================================== + //= FormScriptListener + //==================================================================== + typedef ::cppu::WeakImplHelper1 < XScriptListener + > FormScriptListener_Base; + + /** implements the XScriptListener interface, is used by FormScriptingEnvironment + */ + class FormScriptListener :public FormScriptListener_Base + { + private: + ::osl::Mutex m_aMutex; + ::rtl::Reference< FormScriptingEnvironment > m_pScriptExecutor; + + public: + FormScriptListener( const ::rtl::Reference< FormScriptingEnvironment >& _pScriptExecutor ); + + // XScriptListener + virtual void SAL_CALL firing( const ScriptEvent& aEvent ) throw (RuntimeException); + virtual Any SAL_CALL approveFiring( const ScriptEvent& aEvent ) throw (InvocationTargetException, RuntimeException); + // XEventListener + virtual void SAL_CALL disposing( const EventObject& Source ) throw (RuntimeException); + + // lifetime control + void SAL_CALL dispose(); + + protected: + ~FormScriptListener(); + + private: + /** determines whether calling a given method at a given listener interface can be done asynchronously + + @param _rListenerType + the name of the UNO type whose method is to be checked + @param _rMethodName + the name of the method at the interface determined by _rListenerType + + @return + <TRUE/> if and only if the method is declared <code>oneway</code>, i.e. can be called asynchronously + */ + bool impl_allowAsynchronousCall_nothrow( const ::rtl::OUString& _rListenerType, const ::rtl::OUString& _rMethodName ) const; + + /** determines whether the instance is already disposed + */ + bool impl_isDisposed_nothrow() const { return !m_pScriptExecutor.is(); } + + /** fires the given script event in a thread-safe manner + + This methods calls our script executor's doFireScriptEvent, with previously releasing the given mutex guard, + but ensuring that our script executor is not deleted between this release and the actual call. + + @param _rGuard + a clearable guard to our mutex. Must be the only active guard to our mutex. + @param _rEvent + the event to fire + @param _pSyncronousResult + a place to take a possible result of the script call. + + @precond + m_pScriptExecutor is not <NULL/>. + */ + void impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard& _rGuard, const ScriptEvent& _rEvent, Any* _pSyncronousResult ); + + private: + DECL_LINK( OnAsyncScriptEvent, ScriptEvent* ); + }; + + //==================================================================== + //= FormScriptingEnvironment + //==================================================================== + class FormScriptingEnvironment : public IFormScriptingEnvironment + { + private: + typedef ::comphelper::ImplementationReference< FormScriptListener, XScriptListener > ListenerImplementation; + + private: + ::osl::Mutex m_aMutex; + oslInterlockedCount m_refCount; + ListenerImplementation m_pScriptListener; + FmFormModel& m_rFormModel; + bool m_bDisposed; + + public: + FormScriptingEnvironment( FmFormModel& _rModel ); + virtual ~FormScriptingEnvironment(); + + // callback for FormScriptListener + void doFireScriptEvent( const ScriptEvent& _rEvent, Any* _pSyncronousResult ); + + // IFormScriptingEnvironment + virtual void registerEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager ); + virtual void revokeEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager ); + virtual void dispose(); + + // IReference + virtual oslInterlockedCount SAL_CALL acquire(); + virtual oslInterlockedCount SAL_CALL release(); + + private: + void impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister ); + + private: + FormScriptingEnvironment(); // never implemented + FormScriptingEnvironment( const FormScriptingEnvironment& ); // never implemented + FormScriptingEnvironment& operator=( const FormScriptingEnvironment& ); // never implemented + }; + + //==================================================================== + //= FormScriptListener + //==================================================================== + //-------------------------------------------------------------------- + FormScriptListener::FormScriptListener( const ::rtl::Reference< FormScriptingEnvironment >& _pScriptExecutor ) + :m_pScriptExecutor( _pScriptExecutor ) + { + } + + //-------------------------------------------------------------------- + FormScriptListener::~FormScriptListener() + { + } + + //-------------------------------------------------------------------- + bool FormScriptListener::impl_allowAsynchronousCall_nothrow( const ::rtl::OUString& _rListenerType, const ::rtl::OUString& _rMethodName ) const + { + bool bAllowAsynchronousCall = false; + try + { + ::comphelper::ComponentContext aContext( ::comphelper::getProcessServiceFactory() ); + Reference< XHierarchicalNameAccess > xTypeDescriptions( aContext.getSingleton( "com.sun.star.reflection.theTypeDescriptionManager" ), UNO_QUERY_THROW ); + + ::rtl::OUString sMethodDescription( _rListenerType ); + sMethodDescription += ::rtl::OUString::createFromAscii( "::" ); + sMethodDescription += _rMethodName; + + Reference< XInterfaceMethodTypeDescription > xMethod( xTypeDescriptions->getByHierarchicalName( sMethodDescription ), UNO_QUERY_THROW ); + bAllowAsynchronousCall = xMethod->isOneway(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + return bAllowAsynchronousCall; + } + + //-------------------------------------------------------------------- + void FormScriptListener::impl_doFireScriptEvent_nothrow( ::osl::ClearableMutexGuard& _rGuard, const ScriptEvent& _rEvent, Any* _pSyncronousResult ) + { + OSL_PRECOND( m_pScriptExecutor.is(), "FormScriptListener::impl_doFireScriptEvent_nothrow: this will crash!" ); + + ::rtl::Reference< FormScriptingEnvironment > pExecutor( m_pScriptExecutor ); + _rGuard.clear(); + pExecutor->doFireScriptEvent( _rEvent, _pSyncronousResult ); + } + + //-------------------------------------------------------------------- + void SAL_CALL FormScriptListener::firing( const ScriptEvent& _rEvent ) throw (RuntimeException) + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + static const ::rtl::OUString vbaInterOp = + ::rtl::OUString::createFromAscii("VBAInterop"); + if ( _rEvent.ScriptType.equals(vbaInterOp) ) + return; // not handled here + + if ( impl_isDisposed_nothrow() ) + return; + + if ( !impl_allowAsynchronousCall_nothrow( _rEvent.ListenerType.getTypeName(), _rEvent.MethodName ) ) + { + impl_doFireScriptEvent_nothrow( aGuard, _rEvent, NULL ); + return; + } + + acquire(); + Application::PostUserEvent( LINK( this, FormScriptListener, OnAsyncScriptEvent ), new ScriptEvent( _rEvent ) ); + } + + //-------------------------------------------------------------------- + Any SAL_CALL FormScriptListener::approveFiring( const ScriptEvent& _rEvent ) throw (InvocationTargetException, RuntimeException) + { + Any aResult; + + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + if ( !impl_isDisposed_nothrow() ) + impl_doFireScriptEvent_nothrow( aGuard, _rEvent, &aResult ); + + return aResult; + } + + //-------------------------------------------------------------------- + void SAL_CALL FormScriptListener::disposing( const EventObject& /*Source*/ ) throw (RuntimeException) + { + // not interested in + } + + //-------------------------------------------------------------------- + void SAL_CALL FormScriptListener::dispose() + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_pScriptExecutor = NULL; + } + + //-------------------------------------------------------------------- + IMPL_LINK( FormScriptListener, OnAsyncScriptEvent, ScriptEvent*, _pEvent ) + { + OSL_PRECOND( _pEvent != NULL, "FormScriptListener::OnAsyncScriptEvent: invalid event!" ); + if ( !_pEvent ) + return 1L; + + { + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + if ( !impl_isDisposed_nothrow() ) + impl_doFireScriptEvent_nothrow( aGuard, *_pEvent, NULL ); + } + + delete _pEvent; + // we acquired ourself immediately before posting the event + release(); + return 0L; + } + + //==================================================================== + //= FormScriptingEnvironment + //==================================================================== + //-------------------------------------------------------------------- + FormScriptingEnvironment::FormScriptingEnvironment( FmFormModel& _rModel ) + :m_refCount( 0 ) + ,m_pScriptListener( NULL ) + ,m_rFormModel( _rModel ) + ,m_bDisposed( false ) + { + m_pScriptListener = ListenerImplementation( new FormScriptListener( this ) ); + // note that this is a cyclic reference between the FormScriptListener and the FormScriptingEnvironment + // This cycle is broken up when our instance is disposed. + } + + //-------------------------------------------------------------------- + FormScriptingEnvironment::~FormScriptingEnvironment() + { + } + + //-------------------------------------------------------------------- + void FormScriptingEnvironment::impl_registerOrRevoke_throw( const Reference< XEventAttacherManager >& _rxManager, bool _bRegister ) + { + ::osl::MutexGuard aGuard( m_aMutex ); + + if ( !_rxManager.is() ) + throw IllegalArgumentException(); + if ( m_bDisposed ) + throw DisposedException(); + + try + { + if ( _bRegister ) + _rxManager->addScriptListener( m_pScriptListener.getRef() ); + else + _rxManager->removeScriptListener( m_pScriptListener.getRef() ); + } + catch( const RuntimeException& ) { throw; } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION(); + } + } + + //-------------------------------------------------------------------- + void FormScriptingEnvironment::registerEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager ) + { + impl_registerOrRevoke_throw( _rxManager, true ); + } + + //-------------------------------------------------------------------- + void FormScriptingEnvironment::revokeEventAttacherManager( const Reference< XEventAttacherManager >& _rxManager ) + { + impl_registerOrRevoke_throw( _rxManager, false ); + } + + //-------------------------------------------------------------------- + oslInterlockedCount SAL_CALL FormScriptingEnvironment::acquire() + { + return osl_incrementInterlockedCount( &m_refCount ); + } + + //-------------------------------------------------------------------- + oslInterlockedCount SAL_CALL FormScriptingEnvironment::release() + { + if ( 0 == osl_decrementInterlockedCount( &m_refCount ) ) + { + delete this; + return 0; + } + return m_refCount; + } + + //-------------------------------------------------------------------- + IFormScriptingEnvironment::~IFormScriptingEnvironment() + { + } + + //-------------------------------------------------------------------- + namespace + { + //................................................................ + //. NewStyleUNOScript + //................................................................ + class SAL_NO_VTABLE IScript + { + public: + virtual void invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult ) = 0; + + virtual ~IScript() { } + }; + typedef ::boost::shared_ptr< IScript > PScript; + + //................................................................ + //. NewStyleUNOScript + //................................................................ + class NewStyleUNOScript : public IScript + { + SfxObjectShell& m_rObjectShell; + const ::rtl::OUString m_sScriptCode; + + public: + NewStyleUNOScript( SfxObjectShell& _rObjectShell, const ::rtl::OUString& _rScriptCode ) + :m_rObjectShell( _rObjectShell ) + ,m_sScriptCode( _rScriptCode ) + { + } + + // IScript + virtual void invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult ); + }; + + //................................................................ + void NewStyleUNOScript::invoke( const Sequence< Any >& _rArguments, Any& _rSynchronousResult ) + { + Sequence< sal_Int16 > aOutArgsIndex; + Sequence< Any > aOutArgs; + + m_rObjectShell.CallXScript( m_sScriptCode, _rArguments, _rSynchronousResult, aOutArgsIndex, aOutArgs ); + } + } + + //-------------------------------------------------------------------- + void FormScriptingEnvironment::doFireScriptEvent( const ScriptEvent& _rEvent, Any* _pSyncronousResult ) + { + ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() ); + ::osl::ClearableMutexGuard aGuard( m_aMutex ); + + if ( m_bDisposed ) + return; + + // SfxObjectShellRef is good here since the model controls the lifetime of the object + SfxObjectShellRef xObjectShell = m_rFormModel.GetObjectShell(); + if( !xObjectShell.Is() ) + return; + + // the script to execute + PScript pScript; + + if ( !_rEvent.ScriptType.equalsAscii( "StarBasic" ) ) + { + pScript.reset( new NewStyleUNOScript( *xObjectShell, _rEvent.ScriptCode ) ); + } + else + { + ::rtl::OUString sScriptCode = _rEvent.ScriptCode; + ::rtl::OUString sMacroLocation; + + // is there a location in the script name ("application" or "document")? + sal_Int32 nPrefixLen = sScriptCode.indexOf( ':' ); + DBG_ASSERT( 0 <= nPrefixLen, "FormScriptingEnvironment::doFireScriptEvent: Basic script name in old format encountered!" ); + + if ( 0 <= nPrefixLen ) + { + // and it has such a prefix + sMacroLocation = sScriptCode.copy( 0, nPrefixLen ); + DBG_ASSERT( 0 == sMacroLocation.compareToAscii( "document" ) + || 0 == sMacroLocation.compareToAscii( "application" ), + "FormScriptingEnvironment::doFireScriptEvent: invalid (unknown) prefix!" ); + + // strip the prefix: the SfxObjectShell::CallScript knows nothing about such prefixes + sScriptCode = sScriptCode.copy( nPrefixLen + 1 ); + } + + if ( !sMacroLocation.getLength() ) + { + // legacy format: use the app-wide Basic, if it has a respective method, otherwise fall back to the doc's Basic + if ( SFX_APP()->GetBasicManager()->HasMacro( sScriptCode ) ) + sMacroLocation = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "application" ) ); + else + sMacroLocation = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "document" ) ); + } + + ::rtl::OUStringBuffer aScriptURI; + aScriptURI.appendAscii( "vnd.sun.star.script:" ); + aScriptURI.append( sScriptCode ); + aScriptURI.appendAscii( "?language=Basic" ); + aScriptURI.appendAscii( "&location=" ); + aScriptURI.append( sMacroLocation ); + + const ::rtl::OUString sScriptURI( aScriptURI.makeStringAndClear() ); + pScript.reset( new NewStyleUNOScript( *xObjectShell, sScriptURI ) ); + } + + OSL_ENSURE( pScript.get(), "FormScriptingEnvironment::doFireScriptEvent: no script to execute!" ); + if ( !pScript.get() ) + // this is an internal error in the above code + throw RuntimeException(); + + aGuard.clear(); + aSolarGuard.clear(); + + Any aIgnoreResult; + pScript->invoke( _rEvent.Arguments, _pSyncronousResult ? *_pSyncronousResult : aIgnoreResult ); + pScript.reset(); + + { + // object shells are not thread safe, so guard the destruction + ::vos::OGuard aSolarGuarsReset( Application::GetSolarMutex() ); + xObjectShell = NULL; + } + } + + //-------------------------------------------------------------------- + void FormScriptingEnvironment::dispose() + { + ::osl::MutexGuard aGuard( m_aMutex ); + m_bDisposed = true; + m_pScriptListener->dispose(); + } + + //-------------------------------------------------------------------- + PFormScriptingEnvironment createDefaultFormScriptingEnvironment( FmFormModel& _rModel ) + { + return new FormScriptingEnvironment( _rModel ); + } + +//........................................................................ +} // namespace svxform +//........................................................................ + |