/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include // InvocationToAllListenerMapper #include #include #include #include #include #include #include #include namespace com::sun::star::lang { class XMultiServiceFactory; } using namespace com::sun::star::uno; using namespace com::sun::star::lang; using namespace com::sun::star::beans; using namespace com::sun::star::script; using namespace com::sun::star::reflection; using namespace cppu; namespace comp_EventAttacher { // class InvocationToAllListenerMapper // helper class to map XInvocation to XAllListener namespace { class InvocationToAllListenerMapper : public WeakImplHelper< XInvocation > { public: InvocationToAllListenerMapper( const Reference< XIdlClass >& ListenerType, const Reference< XAllListener >& AllListener, Any Helper ); // XInvocation virtual Reference< XIntrospectionAccess > SAL_CALL getIntrospection() override; virtual Any SAL_CALL invoke(const OUString& FunctionName, const Sequence< Any >& Params, Sequence< sal_Int16 >& OutParamIndex, Sequence< Any >& OutParam) override; virtual void SAL_CALL setValue(const OUString& PropertyName, const Any& Value) override; virtual Any SAL_CALL getValue(const OUString& PropertyName) override; virtual sal_Bool SAL_CALL hasMethod(const OUString& Name) override; virtual sal_Bool SAL_CALL hasProperty(const OUString& Name) override; private: Reference< XAllListener > m_xAllListener; Reference< XIdlClass > m_xListenerType; Any m_Helper; }; } // Function to replace AllListenerAdapterService::createAllListerAdapter static Reference< XInterface > createAllListenerAdapter ( const Reference< XInvocationAdapterFactory2 >& xInvocationAdapterFactory, const Reference< XIdlClass >& xListenerType, const Reference< XAllListener >& xListener, const Any& Helper ) { Reference< XInterface > xAdapter; if( xInvocationAdapterFactory.is() && xListenerType.is() && xListener.is() ) { Reference< XInvocation > xInvocationToAllListenerMapper = new InvocationToAllListenerMapper(xListenerType, xListener, Helper); Type aListenerType( xListenerType->getTypeClass(), xListenerType->getName()); Sequence arg2 { aListenerType }; xAdapter = xInvocationAdapterFactory->createAdapter( xInvocationToAllListenerMapper, arg2 ); } return xAdapter; } // InvocationToAllListenerMapper InvocationToAllListenerMapper::InvocationToAllListenerMapper ( const Reference< XIdlClass >& ListenerType, const Reference< XAllListener >& AllListener, Any Helper ) : m_xAllListener( AllListener ) , m_xListenerType( ListenerType ) , m_Helper(std::move( Helper )) { } Reference< XIntrospectionAccess > SAL_CALL InvocationToAllListenerMapper::getIntrospection() { return Reference< XIntrospectionAccess >(); } Any SAL_CALL InvocationToAllListenerMapper::invoke(const OUString& FunctionName, const Sequence< Any >& Params, Sequence< sal_Int16 >& , Sequence< Any >& ) { Any aRet; // Check if to firing or approveFiring has to be called Reference< XIdlMethod > xMethod = m_xListenerType->getMethod( FunctionName ); bool bApproveFiring = false; if( !xMethod.is() ) return aRet; Reference< XIdlClass > xReturnType = xMethod->getReturnType(); Sequence< Reference< XIdlClass > > aExceptionSeq = xMethod->getExceptionTypes(); if( ( xReturnType.is() && xReturnType->getTypeClass() != TypeClass_VOID ) || aExceptionSeq.hasElements() ) { bApproveFiring = true; } else { Sequence< ParamInfo > aParamSeq = xMethod->getParameterInfos(); sal_uInt32 nParamCount = aParamSeq.getLength(); if( nParamCount > 1 ) { const ParamInfo* pInfo = aParamSeq.getConstArray(); for( sal_uInt32 i = 0 ; i < nParamCount ; i++ ) { if( pInfo[ i ].aMode != ParamMode_IN ) { bApproveFiring = true; break; } } } } AllEventObject aAllEvent; aAllEvent.Source = getXWeak(); aAllEvent.Helper = m_Helper; aAllEvent.ListenerType = Type(m_xListenerType->getTypeClass(), m_xListenerType->getName()); aAllEvent.MethodName = FunctionName; aAllEvent.Arguments = Params; if( bApproveFiring ) aRet = m_xAllListener->approveFiring( aAllEvent ); else m_xAllListener->firing( aAllEvent ); return aRet; } void SAL_CALL InvocationToAllListenerMapper::setValue(const OUString& , const Any& ) { } Any SAL_CALL InvocationToAllListenerMapper::getValue(const OUString& ) { return Any(); } sal_Bool SAL_CALL InvocationToAllListenerMapper::hasMethod(const OUString& Name) { Reference< XIdlMethod > xMethod = m_xListenerType->getMethod( Name ); return xMethod.is(); } sal_Bool SAL_CALL InvocationToAllListenerMapper::hasProperty(const OUString& Name) { Reference< XIdlField > xField = m_xListenerType->getField( Name ); return xField.is(); } // class EventAttacherImpl // represents an implementation of the EventAttacher service namespace { class EventAttacherImpl : public WeakImplHelper < XEventAttacher2, XInitialization, XServiceInfo > { public: explicit EventAttacherImpl( const Reference< XComponentContext >& ); // XServiceInfo virtual OUString SAL_CALL getImplementationName( ) override; virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; virtual Sequence< OUString > SAL_CALL getSupportedServiceNames( ) override; // XInitialization virtual void SAL_CALL initialize( const Sequence< Any >& aArguments ) override; // methods of XEventAttacher virtual Reference< XEventListener > SAL_CALL attachListener(const Reference< XInterface >& xObject, const Reference< XAllListener >& AllListener, const Any& Helper, const OUString& ListenerType, const OUString& AddListenerParam) override; virtual Reference< XEventListener > SAL_CALL attachSingleEventListener(const Reference< XInterface >& xObject, const Reference< XAllListener >& AllListener, const Any& Helper, const OUString& ListenerType, const OUString& AddListenerParam, const OUString& EventMethod) override; virtual void SAL_CALL removeListener(const Reference< XInterface >& xObject, const OUString& ListenerType, const OUString& AddListenerParam, const Reference< XEventListener >& aToRemoveListener) override; // XEventAttacher2 virtual Sequence< Reference > SAL_CALL attachMultipleEventListeners( const Reference& xObject, const Sequence& aListeners ) override; // used by FilterAllListener_Impl /// @throws Exception Reference< XTypeConverter > getConverter(); friend class FilterAllListenerImpl; private: static Reference attachListenerForTarget( const Reference& xAccess, const Reference& xInvocationAdapterFactory, const Reference& xAllListener, const Any& aObject, const Any& aHelper, const OUString& aListenerType, const OUString& aAddListenerParam ); Sequence< Reference > attachListeners( const Reference& xObject, const Sequence< Reference >& AllListeners, const Sequence& aListeners ); private: std::mutex m_aMutex; Reference< XComponentContext > m_xContext; // Save Services Reference< XIntrospection > m_xIntrospection; Reference< XIdlReflection > m_xReflection; Reference< XTypeConverter > m_xConverter; Reference< XInvocationAdapterFactory2 > m_xInvocationAdapterFactory; // needed services /// @throws Exception Reference< XIntrospection > getIntrospection(); /// @throws Exception Reference< XIdlReflection > getReflection(); /// @throws Exception Reference< XInvocationAdapterFactory2 > getInvocationAdapterService(); }; } EventAttacherImpl::EventAttacherImpl( const Reference< XComponentContext >& rxContext ) : m_xContext( rxContext ) { } /// @throws Exception OUString SAL_CALL EventAttacherImpl::getImplementationName( ) { return u"com.sun.star.comp.EventAttacher"_ustr; } sal_Bool SAL_CALL EventAttacherImpl::supportsService( const OUString& ServiceName ) { return cppu::supportsService(this, ServiceName); } Sequence SAL_CALL EventAttacherImpl::getSupportedServiceNames( ) { return { u"com.sun.star.script.EventAttacher"_ustr }; } void SAL_CALL EventAttacherImpl::initialize(const Sequence< Any >& Arguments) { // get services from the argument list for( const Any& arg : Arguments ) { if( arg.getValueType().getTypeClass() != TypeClass_INTERFACE ) throw IllegalArgumentException(); // InvocationAdapter service ? Reference< XInvocationAdapterFactory2 > xALAS; arg >>= xALAS; if( xALAS.is() ) { std::scoped_lock aGuard( m_aMutex ); m_xInvocationAdapterFactory = xALAS; } // Introspection service ? Reference< XIntrospection > xI; arg >>= xI; if( xI.is() ) { std::scoped_lock aGuard( m_aMutex ); m_xIntrospection = xI; } // Reflection service ? Reference< XIdlReflection > xIdlR; arg >>= xIdlR; if( xIdlR.is() ) { std::scoped_lock aGuard( m_aMutex ); m_xReflection = xIdlR; } // Converter Service ? Reference< XTypeConverter > xC; arg >>= xC; if( xC.is() ) { std::scoped_lock aGuard( m_aMutex ); m_xConverter = xC; } // no right interface if( !xALAS.is() && !xI.is() && !xIdlR.is() && !xC.is() ) throw IllegalArgumentException(); } } //*** Private helper methods *** Reference< XIntrospection > EventAttacherImpl::getIntrospection() { std::scoped_lock aGuard( m_aMutex ); if( !m_xIntrospection.is() ) { m_xIntrospection = theIntrospection::get( m_xContext ); } return m_xIntrospection; } //*** Private helper methods *** Reference< XIdlReflection > EventAttacherImpl::getReflection() { std::scoped_lock aGuard( m_aMutex ); if( !m_xReflection.is() ) { m_xReflection = theCoreReflection::get(m_xContext); } return m_xReflection; } //*** Private helper methods *** Reference< XInvocationAdapterFactory2 > EventAttacherImpl::getInvocationAdapterService() { std::scoped_lock aGuard( m_aMutex ); if( !m_xInvocationAdapterFactory.is() ) { m_xInvocationAdapterFactory = InvocationAdapterFactory::create(m_xContext); } return m_xInvocationAdapterFactory; } //*** Private helper methods *** Reference< XTypeConverter > EventAttacherImpl::getConverter() { std::scoped_lock aGuard( m_aMutex ); if( !m_xConverter.is() ) { m_xConverter = Converter::create(m_xContext); } return m_xConverter; } namespace { // Implementation of an EventAttacher-related AllListeners, which brings // a few Events to a general AllListener class FilterAllListenerImpl : public WeakImplHelper< XAllListener > { public: FilterAllListenerImpl( EventAttacherImpl * pEA_, OUString EventMethod_, const Reference< XAllListener >& AllListener_ ); // XAllListener virtual void SAL_CALL firing(const AllEventObject& Event) override; virtual Any SAL_CALL approveFiring(const AllEventObject& Event) override; // XEventListener virtual void SAL_CALL disposing(const EventObject& Source) override; private: // convert /// @throws CannotConvertException /// @throws RuntimeException void convertToEventReturn( Any & rRet, const Type& rRetType ); EventAttacherImpl * m_pEA; OUString m_EventMethod; Reference< XAllListener > m_AllListener; }; } FilterAllListenerImpl::FilterAllListenerImpl( EventAttacherImpl * pEA_, OUString EventMethod_, const Reference< XAllListener >& AllListener_ ) : m_pEA( pEA_ ) , m_EventMethod(std::move( EventMethod_ )) , m_AllListener( AllListener_ ) { } void SAL_CALL FilterAllListenerImpl::firing(const AllEventObject& Event) { if( Event.MethodName == m_EventMethod && m_AllListener.is() ) m_AllListener->firing( Event ); } // Convert to the standard event return void FilterAllListenerImpl::convertToEventReturn( Any & rRet, const Type & rRetType ) { // no return value? Set to the specified values if( rRet.getValueType().getTypeClass() == TypeClass_VOID ) { switch( rRetType.getTypeClass() ) { case TypeClass_INTERFACE: { rRet <<= Reference< XInterface >(); } break; case TypeClass_BOOLEAN: rRet <<= true; break; case TypeClass_STRING: rRet <<= OUString(); break; case TypeClass_FLOAT: rRet <<= float(0); break; case TypeClass_DOUBLE: rRet <<= 0.0; break; case TypeClass_BYTE: rRet <<= sal_uInt8( 0 ); break; case TypeClass_SHORT: rRet <<= sal_Int16( 0 ); break; case TypeClass_LONG: rRet <<= sal_Int32( 0 ); break; case TypeClass_UNSIGNED_SHORT: rRet <<= sal_uInt16( 0 ); break; case TypeClass_UNSIGNED_LONG: rRet <<= sal_uInt32( 0 ); break; default: break; } } else if( !rRet.getValueType().equals( rRetType ) ) { Reference< XTypeConverter > xConverter = m_pEA->getConverter(); if( !xConverter.is() ) throw CannotConvertException(); // TODO TypeConversionException rRet = xConverter->convertTo( rRet, rRetType ); } } Any SAL_CALL FilterAllListenerImpl::approveFiring( const AllEventObject& Event ) { Any aRet; if( Event.MethodName == m_EventMethod && m_AllListener.is() ) aRet = m_AllListener->approveFiring( Event ); else { // Convert to the standard event return try { Reference< XIdlClass > xListenerType = m_pEA->getReflection()-> forName( Event.ListenerType.getTypeName() ); Reference< XIdlMethod > xMeth = xListenerType->getMethod( Event.MethodName ); if( xMeth.is() ) { Reference< XIdlClass > xRetType = xMeth->getReturnType(); Type aRetType( xRetType->getTypeClass(), xRetType->getName() ); convertToEventReturn( aRet, aRetType ); } } catch( const CannotConvertException& ) { css::uno::Any anyEx = cppu::getCaughtException(); throw InvocationTargetException( OUString(), Reference< XInterface >(), anyEx ); } } return aRet; } void FilterAllListenerImpl::disposing(const EventObject& ) { // TODO: ??? } Reference< XEventListener > EventAttacherImpl::attachListener ( const Reference< XInterface >& xObject, const Reference< XAllListener >& AllListener, const Any& Helper, const OUString& ListenerType, const OUString& AddListenerParam ) { if( !xObject.is() || !AllListener.is() ) throw IllegalArgumentException(); Reference< XInvocationAdapterFactory2 > xInvocationAdapterFactory = getInvocationAdapterService(); if( !xInvocationAdapterFactory.is() ) throw ServiceNotRegisteredException(); Reference< XIdlReflection > xReflection = getReflection(); if( !xReflection.is() ) throw ServiceNotRegisteredException(); // Sign in, Call the fitting addListener method // First Introspection, as the Methods can be analyzed in the same way // For better performance it is implemented here again or make the Impl-Method // of the Introspection configurable for this purpose. Reference< XIntrospection > xIntrospection = getIntrospection(); if( !xIntrospection.is() ) return Reference(); // Inspect Introspection Any aObjAny( &xObject, cppu::UnoType::get()); Reference< XIntrospectionAccess > xAccess = xIntrospection->inspect( aObjAny ); if( !xAccess.is() ) return Reference(); return attachListenerForTarget( xAccess, xInvocationAdapterFactory, AllListener, aObjAny, Helper, ListenerType, AddListenerParam); } Reference EventAttacherImpl::attachListenerForTarget( const Reference& xAccess, const Reference& xInvocationAdapterFactory, const Reference& xAllListener, const Any& aObject, const Any& aHelper, const OUString& aListenerType, const OUString& aAddListenerParam) { Reference< XEventListener > xRet; // Construct the name of the addListener-Method. sal_Int32 nIndex = aListenerType.lastIndexOf('.'); // set index to the interface name without package name if( nIndex == -1 ) // not found nIndex = 0; else nIndex++; OUString aListenerName = (!aListenerType.isEmpty() && aListenerType[nIndex] == 'X') ? aListenerType.copy(nIndex+1) : aListenerType; OUString aAddListenerName = "add" + aListenerName; // Send Methods to the correct addListener-Method const Sequence< Reference< XIdlMethod > > aMethodSeq = xAccess->getMethods( MethodConcept::LISTENER ); for (const Reference< XIdlMethod >& rxMethod : aMethodSeq) { // Is it the correct method? OUString aMethName = rxMethod->getName(); if (aAddListenerName != aMethName) continue; Sequence< Reference< XIdlClass > > params = rxMethod->getParameterTypes(); sal_uInt32 nParamCount = params.getLength(); Reference< XIdlClass > xListenerType; if( nParamCount == 1 ) xListenerType = params.getConstArray()[0]; else if( nParamCount == 2 ) xListenerType = params.getConstArray()[1]; // Request Adapter for the actual Listener type Reference< XInterface > xAdapter = createAllListenerAdapter( xInvocationAdapterFactory, xListenerType, xAllListener, aHelper ); if( !xAdapter.is() ) throw CannotCreateAdapterException(); xRet.set( xAdapter, UNO_QUERY ); // Just the Listener as parameter? if( nParamCount == 1 ) { Sequence< Any > args( 1 ); args.getArray()[0] <<= xAdapter; try { rxMethod->invoke( aObject, args ); } catch( const InvocationTargetException& ) { throw IntrospectionException(); } } // Else, pass the other parameter now else if( nParamCount == 2 ) { Sequence< Any > args( 2 ); Any* pAnys = args.getArray(); // Check the type of the 1st parameter Reference< XIdlClass > xParamClass = params.getConstArray()[0]; if( xParamClass->getTypeClass() == TypeClass_STRING ) { pAnys[0] <<= aAddListenerParam; } // 2nd Parameter == Listener? TODO: Test! pAnys[1] <<= xAdapter; // TODO: Convert String -> ? // else try { rxMethod->invoke( aObject, args ); } catch( const InvocationTargetException& ) { throw IntrospectionException(); } } break; // else... // Anything else is not supported } return xRet; } Sequence< Reference > EventAttacherImpl::attachListeners( const Reference& xObject, const Sequence< Reference >& AllListeners, const Sequence& aListeners ) { sal_Int32 nCount = aListeners.getLength(); if (nCount != AllListeners.getLength()) // This is a prerequisite! throw RuntimeException(); if (!xObject.is()) throw IllegalArgumentException(); Reference< XInvocationAdapterFactory2 > xInvocationAdapterFactory = getInvocationAdapterService(); if( !xInvocationAdapterFactory.is() ) throw ServiceNotRegisteredException(); Reference< XIdlReflection > xReflection = getReflection(); if( !xReflection.is() ) throw ServiceNotRegisteredException(); // Sign in, Call the fitting addListener method // First Introspection, as the Methods can be analyzed in the same way // For better performance it is implemented here again or make the Impl-Method // of the Introspection configurable for this purpose. Reference< XIntrospection > xIntrospection = getIntrospection(); if( !xIntrospection.is() ) return Sequence< Reference >(); // Inspect Introspection Any aObjAny( &xObject, cppu::UnoType::get() ); Reference xAccess = xIntrospection->inspect(aObjAny); if (!xAccess.is()) return Sequence< Reference >(); Sequence< Reference > aRet(nCount); Reference* pArray = aRet.getArray(); for (sal_Int32 i = 0; i < nCount; ++i) { pArray[i] = attachListenerForTarget( xAccess, xInvocationAdapterFactory, AllListeners[ i ], aObjAny, aListeners[i].Helper, aListeners[i].ListenerType, aListeners[i].AddListenerParam); } return aRet; } // XEventAttacher Reference< XEventListener > EventAttacherImpl::attachSingleEventListener ( const Reference< XInterface >& xObject, const Reference< XAllListener >& AllListener, const Any& Helper, const OUString& ListenerType, const OUString& AddListenerParam, const OUString& EventMethod ) { // Subscribe FilterListener Reference aFilterListener = new FilterAllListenerImpl(this, EventMethod, AllListener); return attachListener( xObject, aFilterListener, Helper, ListenerType, AddListenerParam); } // XEventAttacher void EventAttacherImpl::removeListener ( const Reference< XInterface >& xObject, const OUString& ListenerType, const OUString& AddListenerParam, const Reference< XEventListener >& aToRemoveListener ) { if( !xObject.is() || !aToRemoveListener.is() ) throw IllegalArgumentException(); Reference< XIdlReflection > xReflection = getReflection(); if( !xReflection.is() ) throw IntrospectionException(); // Sign off, Call the fitting removeListener method // First Introspection, as the Methods can be analyzed in the same way // For better performance it is implemented here again or make the Impl-Method // of the Introspection configurable for this purpose. Reference< XIntrospection > xIntrospection = getIntrospection(); if( !xIntrospection.is() ) throw IntrospectionException(); //Inspect Introspection Any aObjAny( &xObject, cppu::UnoType::get()); Reference< XIntrospectionAccess > xAccess = xIntrospection->inspect( aObjAny ); if( !xAccess.is() ) throw IntrospectionException(); // Create name of the removeListener-Method OUString aRemoveListenerName; OUString aListenerName( ListenerType ); sal_Int32 nIndex = aListenerName.lastIndexOf( '.' ); // set index to the interface name without package name if( nIndex == -1 ) // not found nIndex = 0; else nIndex++; if( aListenerName[nIndex] == 'X' ) // erase X from the interface name aListenerName = aListenerName.copy( nIndex +1 ); aRemoveListenerName = "remove" + aListenerName; // Search methods for the correct removeListener method Sequence< Reference< XIdlMethod > > aMethodSeq = xAccess->getMethods( MethodConcept::LISTENER ); sal_uInt32 i, nLen = aMethodSeq.getLength(); const Reference< XIdlMethod >* pMethods = aMethodSeq.getConstArray(); for( i = 0 ; i < nLen ; i++ ) { // Call Method const Reference< XIdlMethod >& rxMethod = pMethods[i]; // Is it the right method? if( aRemoveListenerName == rxMethod->getName() ) { Sequence< Reference< XIdlClass > > params = rxMethod->getParameterTypes(); sal_uInt32 nParamCount = params.getLength(); // Just the Listener as parameter? if( nParamCount == 1 ) { Sequence< Any > args( 1 ); args.getArray()[0] <<= aToRemoveListener; try { rxMethod->invoke( aObjAny, args ); } catch( const InvocationTargetException& ) { throw IntrospectionException(); } } // Else pass the other parameter else if( nParamCount == 2 ) { Sequence< Any > args( 2 ); Any* pAnys = args.getArray(); // Check the type of the 1st parameter Reference< XIdlClass > xParamClass = params.getConstArray()[0]; if( xParamClass->getTypeClass() == TypeClass_STRING ) pAnys[0] <<= AddListenerParam; // 2nd parameter == Listener? TODO: Test! pAnys[1] <<= aToRemoveListener; // TODO: Convert String -> ? // else try { rxMethod->invoke( aObjAny, args ); } catch( const InvocationTargetException& ) { throw IntrospectionException(); } } break; } } } Sequence< Reference > EventAttacherImpl::attachMultipleEventListeners( const Reference& xObject, const Sequence& aListeners ) { sal_Int32 nCount = aListeners.getLength(); Sequence< Reference > aFilterListeners(nCount); auto aFilterListenersRange = asNonConstRange(aFilterListeners); for (sal_Int32 i = 0; i < nCount; ++i) { aFilterListenersRange[i] = new FilterAllListenerImpl(this, aListeners[i].EventMethod, aListeners[i].AllListener); } return attachListeners(xObject, aFilterListeners, aListeners); } } extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* eventattacher_EventAttacher( css::uno::XComponentContext* context, css::uno::Sequence const& ) { return cppu::acquire(new comp_EventAttacher::EventAttacherImpl(context)); } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */