diff options
Diffstat (limited to 'stoc/source')
48 files changed, 21146 insertions, 0 deletions
diff --git a/stoc/source/corereflection/base.hxx b/stoc/source/corereflection/base.hxx new file mode 100644 index 000000000..16d42213d --- /dev/null +++ b/stoc/source/corereflection/base.hxx @@ -0,0 +1,424 @@ +/* -*- 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 . + */ +// #define TEST_LIST_CLASSES + +#ifndef INCLUDED_STOC_SOURCE_COREREFLECTION_BASE_HXX +#define INCLUDED_STOC_SOURCE_COREREFLECTION_BASE_HXX + +#include <sal/config.h> + +#include <o3tl/any.hxx> +#include <osl/mutex.hxx> +#include <uno/mapping.hxx> +#include <uno/dispatcher.h> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/component.hxx> +#include <rtl/ustring.hxx> +#include <rtl/ref.hxx> + +#include "lrucache.hxx" + +#ifdef TEST_LIST_CLASSES +#include <vector> +#include <algorithm> +#endif +#include <unordered_map> +#include <memory> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> + +#include <com/sun/star/reflection/XIdlReflection.hpp> + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::reflection { class XIdlClass; } +namespace com::sun::star::reflection { class XIdlField; } +namespace com::sun::star::reflection { class XIdlMethod; } + +namespace stoc_corefl +{ + +#ifdef TEST_LIST_CLASSES +extern std::vector<OUString> g_aClassNames; +#endif + + +::osl::Mutex & getMutexAccess(); + + +inline bool td_equals( typelib_TypeDescription * pTD, typelib_TypeDescriptionReference * pType ) +{ + return (pTD->pWeakRef == pType || + (pTD->pTypeName->length == pType->pTypeName->length && + rtl_ustr_compare( pTD->pTypeName->buffer, pType->pTypeName->buffer ) == 0)); +} + +typedef std::unordered_map< OUString, css::uno::WeakReference< css::reflection::XIdlField > > OUString2Field; +typedef std::unordered_map< OUString, css::uno::WeakReference< css::reflection::XIdlMethod > > OUString2Method; + + +class IdlReflectionServiceImpl + : public ::cppu::OComponentHelper + , public css::reflection::XIdlReflection + , public css::container::XHierarchicalNameAccess + , public css::lang::XServiceInfo +{ + ::osl::Mutex _aComponentMutex; + css::uno::Reference< css::container::XHierarchicalNameAccess > _xTDMgr; + + // caching + LRU_CacheAnyByOUString _aElements; + + css::uno::Mapping _aCpp2Uno; + css::uno::Mapping _aUno2Cpp; + + inline css::uno::Reference< css::reflection::XIdlClass > constructClass( typelib_TypeDescription * pTypeDescr ); + +public: + /// @throws css::uno::RuntimeException + const css::uno::Mapping & getCpp2Uno(); + /// @throws css::uno::RuntimeException + const css::uno::Mapping & getUno2Cpp(); + /// @throws css::uno::RuntimeException + uno_Interface * mapToUno( const css::uno::Any & rObj, typelib_InterfaceTypeDescription * pTo ); + + // ctor/ dtor + explicit IdlReflectionServiceImpl( const css::uno::Reference< css::uno::XComponentContext > & xContext ); + virtual ~IdlReflectionServiceImpl() override; + + // XInterface + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // some XComponent part from OComponentHelper + virtual void SAL_CALL dispose() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) override; + virtual css::uno::Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XIdlReflection + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL forName( const OUString & rTypeName ) override; + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getType( const css::uno::Any & rObj ) override; + + // XHierarchicalNameAccess + virtual css::uno::Any SAL_CALL getByHierarchicalName( const OUString & rName ) override; + virtual sal_Bool SAL_CALL hasByHierarchicalName( const OUString & rName ) override; + + /// @throws css::uno::RuntimeException + css::uno::Reference< css::reflection::XIdlClass > forType( typelib_TypeDescription * pTypeDescr ); + /// @throws css::uno::RuntimeException + css::uno::Reference< css::reflection::XIdlClass > forType( typelib_TypeDescriptionReference * pRef ); +}; + + +class IdlClassImpl + : public ::cppu::WeakImplHelper< css::reflection::XIdlClass > +{ + rtl::Reference<IdlReflectionServiceImpl> + m_xReflection; + + OUString _aName; + css::uno::TypeClass _eTypeClass; + + typelib_TypeDescription * _pTypeDescr; + +public: + typelib_TypeDescription * getTypeDescr() const + { return _pTypeDescr; } + IdlReflectionServiceImpl * getReflection() const + { return m_xReflection.get(); } + + // Ctor + IdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ); + virtual ~IdlClassImpl() override; + + // XIdlClassImpl default implementation + virtual css::uno::TypeClass SAL_CALL getTypeClass() override; + virtual OUString SAL_CALL getName() override; + virtual sal_Bool SAL_CALL equals( const css::uno::Reference< css::reflection::XIdlClass >& xType ) override; + + virtual sal_Bool SAL_CALL isAssignableFrom( const css::uno::Reference< css::reflection::XIdlClass > & xType ) override; + virtual void SAL_CALL createObject( css::uno::Any & rObj ) override; + + // def impl ???? + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getClasses() override; + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getClass( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getInterfaces() override; + + // structs, interfaces + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getSuperclasses() override; + // structs + virtual css::uno::Reference< css::reflection::XIdlField > SAL_CALL getField( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > SAL_CALL getFields() override; + // interfaces + virtual css::uno::Uik SAL_CALL getUik() override; + virtual css::uno::Reference< css::reflection::XIdlMethod > SAL_CALL getMethod( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlMethod > > SAL_CALL getMethods() override; + // array + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getComponentType() override; + virtual css::uno::Reference< css::reflection::XIdlArray > SAL_CALL getArray() override; +}; + + +class InterfaceIdlClassImpl + : public IdlClassImpl +{ + typedef std::pair< OUString, typelib_TypeDescription * > MemberInit; + + css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > _xSuperClasses; + + std::unique_ptr<MemberInit[]> _pSortedMemberInit; // first methods, then attributes + OUString2Field _aName2Field; + OUString2Method _aName2Method; + sal_Int32 _nMethods; + sal_Int32 _nAttributes; + + void initMembers(); + +public: + typelib_InterfaceTypeDescription * getTypeDescr() const + { return reinterpret_cast<typelib_InterfaceTypeDescription *>(IdlClassImpl::getTypeDescr()); } + + // ctor/ dtor + InterfaceIdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr ) + , _nMethods( 0 ) + , _nAttributes( 0 ) + {} + virtual ~InterfaceIdlClassImpl() override; + + // IdlClassImpl modifications + virtual sal_Bool SAL_CALL isAssignableFrom( const css::uno::Reference< css::reflection::XIdlClass > & xType ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getSuperclasses() override; + virtual css::uno::Uik SAL_CALL getUik() override; + virtual css::uno::Reference< css::reflection::XIdlMethod > SAL_CALL getMethod( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlMethod > > SAL_CALL getMethods() override; + virtual css::uno::Reference< css::reflection::XIdlField > SAL_CALL getField( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > SAL_CALL getFields() override; + virtual void SAL_CALL createObject( css::uno::Any & rObj ) override; +}; + + +class CompoundIdlClassImpl + : public IdlClassImpl +{ + css::uno::Reference< css::reflection::XIdlClass > + _xSuperClass; + std::optional< css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > > + m_xFields; + OUString2Field _aName2Field; + +public: + typelib_CompoundTypeDescription * getTypeDescr() const + { return reinterpret_cast<typelib_CompoundTypeDescription *>(IdlClassImpl::getTypeDescr()); } + + // ctor/ dtor + CompoundIdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr ) + {} + virtual ~CompoundIdlClassImpl() override; + + // IdlClassImpl modifications + virtual sal_Bool SAL_CALL isAssignableFrom( const css::uno::Reference< css::reflection::XIdlClass > & xType ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlClass > > SAL_CALL getSuperclasses() override; + virtual css::uno::Reference< css::reflection::XIdlField > SAL_CALL getField( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > SAL_CALL getFields() override; +}; + + +class ArrayIdlClassImpl + : public IdlClassImpl + , public css::reflection::XIdlArray +{ +public: + typelib_IndirectTypeDescription * getTypeDescr() const + { return reinterpret_cast<typelib_IndirectTypeDescription *>(IdlClassImpl::getTypeDescr()); } + + // ctor + ArrayIdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr ) + {} + + virtual css::uno::Any SAL_CALL queryInterface( const css::uno::Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual css::uno::Sequence< css::uno::Type > SAL_CALL getTypes() override; + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // IdlClassImpl modifications + virtual sal_Bool SAL_CALL isAssignableFrom( const css::uno::Reference< css::reflection::XIdlClass > & xType ) override; + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getComponentType() override; + virtual css::uno::Reference< css::reflection::XIdlArray > SAL_CALL getArray() override; + + // XIdlArray + virtual void SAL_CALL realloc( css::uno::Any & rArray, sal_Int32 nLen ) override; + virtual sal_Int32 SAL_CALL getLen( const css::uno::Any & rArray ) override; + virtual css::uno::Any SAL_CALL get( const css::uno::Any & rArray, sal_Int32 nIndex ) override; + virtual void SAL_CALL set( css::uno::Any & rArray, sal_Int32 nIndex, const css::uno::Any & rNewValue ) override; +}; + + +class EnumIdlClassImpl + : public IdlClassImpl +{ + std::optional< css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > > m_xFields; + OUString2Field _aName2Field; + +public: + typelib_EnumTypeDescription * getTypeDescr() const + { return reinterpret_cast<typelib_EnumTypeDescription *>(IdlClassImpl::getTypeDescr()); } + + // ctor/ dtor + EnumIdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : IdlClassImpl( pReflection, rName, eTypeClass, pTypeDescr ) + {} + virtual ~EnumIdlClassImpl() override; + + // IdlClassImpl modifications + virtual css::uno::Reference< css::reflection::XIdlField > SAL_CALL getField( const OUString & rName ) override; + virtual css::uno::Sequence< css::uno::Reference< css::reflection::XIdlField > > SAL_CALL getFields() override; + virtual void SAL_CALL createObject( css::uno::Any & rObj ) override; +}; + + +class IdlMemberImpl + : public ::cppu::WeakImplHelper< css::reflection::XIdlMember > +{ + rtl::Reference<IdlReflectionServiceImpl> + m_xReflection; + OUString _aName; + + typelib_TypeDescription * _pTypeDescr; + typelib_TypeDescription * _pDeclTypeDescr; + +protected: + css::uno::Reference< css::reflection::XIdlClass > _xDeclClass; + +public: + IdlReflectionServiceImpl * getReflection() const + { return m_xReflection.get(); } + typelib_TypeDescription * getTypeDescr() const + { return _pTypeDescr; } + typelib_TypeDescription * getDeclTypeDescr() const + { return _pDeclTypeDescr; } + + // ctor/ dtor + IdlMemberImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr ); + virtual ~IdlMemberImpl() override; + + // XIdlMember + virtual css::uno::Reference< css::reflection::XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; +}; + + +// coerces to type descr pTo else queries for it: the interface pointer is returned via rDest +// ## type to XidlClass coercion possible +inline bool extract( + const css::uno::Any & rObj, typelib_InterfaceTypeDescription * pTo, + css::uno::Reference< css::uno::XInterface > & rDest, + IdlReflectionServiceImpl * pRefl ) +{ + rDest.clear(); + if (nullptr != pTo) + { + if (! rObj.hasValue()) + return true; + if (rObj.getValueTypeClass() == css::uno::TypeClass_INTERFACE) + { + return ::uno_type_assignData( + &rDest, pTo->aBase.pWeakRef, + const_cast< void * >( rObj.getValue() ), rObj.getValueTypeRef(), + reinterpret_cast< uno_QueryInterfaceFunc >(css::uno::cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(css::uno::cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(css::uno::cpp_release) ); + } + else if (auto t = o3tl::tryAccess<css::uno::Type>(rObj)) + { + rDest = pRefl->forType( t->getTypeLibType() ); + return rDest.is(); + } + } + return false; +} + +inline bool coerce_assign( + void * pDest, typelib_TypeDescription * pTD, const css::uno::Any & rSource, + IdlReflectionServiceImpl * pRefl ) +{ + if (pTD->eTypeClass == typelib_TypeClass_INTERFACE) + { + css::uno::Reference< css::uno::XInterface > xVal; + if (extract( rSource, reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD), xVal, pRefl )) + { + if (*static_cast<css::uno::XInterface **>(pDest)) + (*static_cast<css::uno::XInterface **>(pDest))->release(); + *static_cast<css::uno::XInterface **>(pDest) = xVal.get(); + if (*static_cast<css::uno::XInterface **>(pDest)) + (*static_cast<css::uno::XInterface **>(pDest))->acquire(); + return true; + } + return false; + } + else if (pTD->eTypeClass == typelib_TypeClass_ANY) + { + return uno_assignData( + pDest, pTD, + const_cast<css::uno::Any *>(&rSource), pTD, + reinterpret_cast< uno_QueryInterfaceFunc >(css::uno::cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(css::uno::cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(css::uno::cpp_release) ); + } + else + { + return uno_type_assignData( + pDest, pTD->pWeakRef, + const_cast<void *>(rSource.getValue()), rSource.getValueTypeRef(), + reinterpret_cast< uno_QueryInterfaceFunc >(css::uno::cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(css::uno::cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(css::uno::cpp_release) ); + } +} + +} + + +#endif // INCLUDED_STOC_SOURCE_COREREFLECTION_BASE_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crarray.cxx b/stoc/source/corereflection/crarray.cxx new file mode 100644 index 000000000..8a402c489 --- /dev/null +++ b/stoc/source/corereflection/crarray.cxx @@ -0,0 +1,200 @@ +/* -*- 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 <typelib/typedescription.h> +#include <uno/data.h> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> + +#include "base.hxx" + +using namespace css::lang; +using namespace css::reflection; +using namespace css::uno; + +namespace stoc_corefl +{ + +// XInterface + +Any ArrayIdlClassImpl::queryInterface( const Type & rType ) +{ + Any aRet( ::cppu::queryInterface( rType, static_cast< XIdlArray * >( this ) ) ); + return (aRet.hasValue() ? aRet : IdlClassImpl::queryInterface( rType )); +} + +void ArrayIdlClassImpl::acquire() noexcept +{ + IdlClassImpl::acquire(); +} + +void ArrayIdlClassImpl::release() noexcept +{ + IdlClassImpl::release(); +} + +// XTypeProvider + +Sequence< Type > ArrayIdlClassImpl::getTypes() +{ + static cppu::OTypeCollection s_aTypes( + cppu::UnoType<XIdlArray>::get(), + IdlClassImpl::getTypes() ); + + return s_aTypes.getTypes(); +} + +Sequence< sal_Int8 > ArrayIdlClassImpl::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XIdlArray + +void ArrayIdlClassImpl::realloc( Any & rArray, sal_Int32 nLen ) +{ + TypeClass eTC = rArray.getValueTypeClass(); + if (eTC != TypeClass_SEQUENCE) + { + throw IllegalArgumentException( + "expected sequence, but found " + rArray.getValueType().getTypeName(), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); + } + if (nLen < 0) + { + throw IllegalArgumentException( + "negative length given!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 1 ); + } + + uno_Sequence ** ppSeq = const_cast<uno_Sequence **>(static_cast<uno_Sequence * const *>(rArray.getValue())); + uno_sequence_realloc( ppSeq, &getTypeDescr()->aBase, + nLen, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + rArray.pData = ppSeq; +} + +sal_Int32 ArrayIdlClassImpl::getLen( const Any & rArray ) +{ + TypeClass eTC = rArray.getValueTypeClass(); + if (eTC != TypeClass_SEQUENCE) + { + throw IllegalArgumentException( + "expected sequence, but found " + rArray.getValueType().getTypeName(), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); + } + + return (*static_cast<uno_Sequence * const *>(rArray.getValue()))->nElements; +} + +Any ArrayIdlClassImpl::get( const Any & rArray, sal_Int32 nIndex ) +{ + TypeClass eTC = rArray.getValueTypeClass(); + if (eTC != TypeClass_SEQUENCE) + { + throw IllegalArgumentException( + "expected sequence, but found " + rArray.getValueType().getTypeName(), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); + } + + uno_Sequence * pSeq = *static_cast<uno_Sequence * const *>(rArray.getValue()); + if (pSeq->nElements <= nIndex) + { + throw ArrayIndexOutOfBoundsException( + "illegal index given, index " + OUString::number(nIndex) + " is < " + OUString::number(pSeq->nElements), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); + } + + Any aRet; + typelib_TypeDescription * pElemTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pElemTypeDescr, getTypeDescr()->pType ); + uno_any_destruct( &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_construct( &aRet, &pSeq->elements[nIndex * pElemTypeDescr->nSize], + pElemTypeDescr, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire) ); + TYPELIB_DANGER_RELEASE( pElemTypeDescr ); + return aRet; +} + + +void ArrayIdlClassImpl::set( Any & rArray, sal_Int32 nIndex, const Any & rNewValue ) +{ + TypeClass eTC = rArray.getValueTypeClass(); + if (eTC != TypeClass_SEQUENCE) + { + throw IllegalArgumentException( + "expected sequence, but found " + rArray.getValueType().getTypeName(), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); + } + + uno_Sequence * pSeq = *static_cast<uno_Sequence * const *>(rArray.getValue()); + if (pSeq->nElements <= nIndex) + { + throw ArrayIndexOutOfBoundsException( + "illegal index given, index " + OUString::number(nIndex) + " is < " + OUString::number(pSeq->nElements), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); + } + + uno_Sequence ** ppSeq = const_cast<uno_Sequence **>(static_cast<uno_Sequence * const *>(rArray.getValue())); + uno_sequence_reference2One( + ppSeq, &getTypeDescr()->aBase, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + rArray.pData = ppSeq; + pSeq = *ppSeq; + + typelib_TypeDescription * pElemTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pElemTypeDescr, getTypeDescr()->pType ); + + if (! coerce_assign( &pSeq->elements[nIndex * pElemTypeDescr->nSize], + pElemTypeDescr, rNewValue, getReflection() )) + { + TYPELIB_DANGER_RELEASE( pElemTypeDescr ); + throw IllegalArgumentException( + "sequence element is not assignable by given value!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 2 ); + } + TYPELIB_DANGER_RELEASE( pElemTypeDescr ); +} + +// ArrayIdlClassImpl + +sal_Bool ArrayIdlClassImpl::isAssignableFrom( const Reference< XIdlClass > & xType ) +{ + return (xType.is() && + (equals( xType ) || + (xType->getTypeClass() == getTypeClass() && // must be sequence|array + getComponentType()->isAssignableFrom( xType->getComponentType() )))); +} + +Reference< XIdlClass > ArrayIdlClassImpl::getComponentType() +{ + return getReflection()->forType( getTypeDescr()->pType ); +} + +Reference< XIdlArray > ArrayIdlClassImpl::getArray() +{ + return this; +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crbase.cxx b/stoc/source/corereflection/crbase.cxx new file mode 100644 index 000000000..75e802c96 --- /dev/null +++ b/stoc/source/corereflection/crbase.cxx @@ -0,0 +1,248 @@ +/* -*- 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 <uno/any2.h> + +#include "base.hxx" + +using namespace css::reflection; +using namespace css::uno; + +namespace stoc_corefl +{ + +#ifdef TEST_LIST_CLASSES +ClassNameVector g_aClassNames; +#endif + + +::osl::Mutex & getMutexAccess() +{ + static osl::Mutex s_aMutex; + + return s_aMutex; +} + + +IdlClassImpl::IdlClassImpl( IdlReflectionServiceImpl * pReflection, + const OUString & rName, typelib_TypeClass eTypeClass, + typelib_TypeDescription * pTypeDescr ) + : m_xReflection( pReflection ) + , _aName( rName ) + , _eTypeClass( static_cast<TypeClass>(eTypeClass) ) + , _pTypeDescr( pTypeDescr ) +{ + if (_pTypeDescr) + { + typelib_typedescription_acquire( _pTypeDescr ); + if (! _pTypeDescr->bComplete) + typelib_typedescription_complete( &_pTypeDescr ); + } + +#ifdef TEST_LIST_CLASSES + ClassNameVector::const_iterator iFind( std::find( g_aClassNames.begin(), g_aClassNames.end(), _aName ) ); + OSL_ENSURE( iFind == g_aClassNames.end(), "### idl class already exists!" ); + g_aClassNames.insert(g_aClassNames.begin(), _aName); +#endif +} + +IdlClassImpl::~IdlClassImpl() +{ + if (_pTypeDescr) + typelib_typedescription_release( _pTypeDescr ); + m_xReflection.clear(); + +#ifdef TEST_LIST_CLASSES + ClassNameVector::iterator iFind( std::find( g_aClassNames.begin(), g_aClassNames.end(), _aName ) ); + OSL_ENSURE( iFind != g_aClassNames.end(), "### idl class does not exist!" ); + g_aClassNames.erase( iFind ); +#endif +} + +// XIdlClassImpl default implementation + +TypeClass IdlClassImpl::getTypeClass() +{ + return _eTypeClass; +} + +OUString IdlClassImpl::getName() +{ + return _aName; +} + +sal_Bool IdlClassImpl::equals( const Reference< XIdlClass >& xType ) +{ + return (xType.is() && + (xType->getTypeClass() == _eTypeClass) && (xType->getName() == _aName)); +} + +const bool s_aAssignableFromTab[11][11] = +{ + /* from CH, BO, BY, SH, US, LO, UL, HY, UH, FL, DO */ +/* TypeClass_CHAR */ { true, false, false, false, false, false, false, false, false, false, false }, +/* TypeClass_BOOLEAN */ { false, true, false, false, false, false, false, false, false, false, false }, +/* TypeClass_BYTE */ { false, false, true, false, false, false, false, false, false, false, false }, +/* TypeClass_SHORT */ { false, false, true, true, true, false, false, false, false, false, false }, +/* TypeClass_UNSIGNED_SHORT */ { false, false, true, true, true, false, false, false, false, false, false }, +/* TypeClass_LONG */ { false, false, true, true, true, true, true, false, false, false, false }, +/* TypeClass_UNSIGNED_LONG */ { false, false, true, true, true, true, true, false, false, false, false }, +/* TypeClass_HYPER */ { false, false, true, true, true, true, true, true, true, false, false }, +/* TypeClass_UNSIGNED_HYPER */ { false, false, true, true, true, true, true, true, true, false, false }, +/* TypeClass_FLOAT */ { false, false, true, true, true, true, true, true, true, true, false }, +/* TypeClass_DOUBLE */ { false, false, true, true, true, true, true, true, true, true, true } +}; + +sal_Bool IdlClassImpl::isAssignableFrom( const Reference< XIdlClass > & xType ) +{ + TypeClass eAssign = getTypeClass(); + if (equals( xType ) || eAssign == TypeClass_ANY) // default shot + { + return true; + } + else + { + TypeClass eFrom = xType->getTypeClass(); + if (eAssign > TypeClass_VOID && eAssign < TypeClass_STRING && + eFrom > TypeClass_VOID && eFrom < TypeClass_STRING) + { + return s_aAssignableFromTab[static_cast<int>(eAssign)-1][static_cast<int>(eFrom)-1]; + } + } + return false; +} + +void IdlClassImpl::createObject( Any & rObj ) +{ + rObj.clear(); + uno_any_destruct( &rObj, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_construct( &rObj, nullptr, getTypeDescr(), nullptr ); +} + +// what TODO ???? + +Sequence< Reference< XIdlClass > > IdlClassImpl::getClasses() +{ + OSL_FAIL( "### unexpected use!" ); + return Sequence< Reference< XIdlClass > >(); +} + +Reference< XIdlClass > IdlClassImpl::getClass( const OUString & ) +{ + OSL_FAIL( "### unexpected use!" ); + return Reference< XIdlClass >(); +} + +Sequence< Reference< XIdlClass > > IdlClassImpl::getInterfaces() +{ +// OSL_FAIL( "### unexpected use!" ); + return Sequence< Reference< XIdlClass > >(); +} + +// structs, interfaces + +Sequence< Reference< XIdlClass > > IdlClassImpl::getSuperclasses() +{ + return Sequence< Reference< XIdlClass > >(); +} +// structs + +Reference< XIdlField > IdlClassImpl::getField( const OUString & ) +{ + return Reference< XIdlField >(); +} + +Sequence< Reference< XIdlField > > IdlClassImpl::getFields() +{ + return Sequence< Reference< XIdlField > >(); +} +// interfaces + +Uik IdlClassImpl::getUik() +{ + return Uik(); +} + +Reference< XIdlMethod > IdlClassImpl::getMethod( const OUString & ) +{ + return Reference< XIdlMethod >(); +} + +Sequence< Reference< XIdlMethod > > IdlClassImpl::getMethods() +{ + return Sequence< Reference< XIdlMethod > >(); +} +// array + +Reference< XIdlClass > IdlClassImpl::getComponentType() +{ + return Reference< XIdlClass >(); +} + +Reference< XIdlArray > IdlClassImpl::getArray() +{ + return Reference< XIdlArray >(); +} + + +IdlMemberImpl::IdlMemberImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, + typelib_TypeDescription * pDeclTypeDescr ) + : m_xReflection( pReflection ) + , _aName( rName ) + , _pTypeDescr( pTypeDescr ) + , _pDeclTypeDescr( pDeclTypeDescr ) +{ + typelib_typedescription_acquire( _pTypeDescr ); + if (! _pTypeDescr->bComplete) + typelib_typedescription_complete( &_pTypeDescr ); + typelib_typedescription_acquire( _pDeclTypeDescr ); + if (! _pDeclTypeDescr->bComplete) + typelib_typedescription_complete( &_pDeclTypeDescr ); +} + +IdlMemberImpl::~IdlMemberImpl() +{ + typelib_typedescription_release( _pDeclTypeDescr ); + typelib_typedescription_release( _pTypeDescr ); +} + +// XIdlMember + +Reference< XIdlClass > IdlMemberImpl::getDeclaringClass() +{ + if (! _xDeclClass.is()) + { + Reference< XIdlClass > xDeclClass( getReflection()->forType( getDeclTypeDescr() ) ); + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xDeclClass.is()) + _xDeclClass = xDeclClass; + } + return _xDeclClass; +} + +OUString IdlMemberImpl::getName() +{ + return _aName; +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crcomp.cxx b/stoc/source/corereflection/crcomp.cxx new file mode 100644 index 000000000..51cf4a6f0 --- /dev/null +++ b/stoc/source/corereflection/crcomp.cxx @@ -0,0 +1,358 @@ +/* -*- 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 <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> + +#include <com/sun/star/reflection/XIdlField.hpp> +#include <com/sun/star/reflection/XIdlField2.hpp> +#include <com/sun/star/uno/TypeClass.hpp> + +#include "base.hxx" + +using namespace css::lang; +using namespace css::reflection; +using namespace css::uno; + +namespace stoc_corefl +{ + +namespace { + +class IdlCompFieldImpl + : public IdlMemberImpl + , public XIdlField + , public XIdlField2 +{ + sal_Int32 _nOffset; + +public: + IdlCompFieldImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr, + sal_Int32 nOffset ) + : IdlMemberImpl( pReflection, rName, pTypeDescr, pDeclTypeDescr ) + , _nOffset( nOffset ) + {} + + // XInterface + virtual Any SAL_CALL queryInterface( const Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual Sequence< Type > SAL_CALL getTypes() override; + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XIdlMember + virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; + // XIdlField + virtual Reference< XIdlClass > SAL_CALL getType() override; + virtual FieldAccessMode SAL_CALL getAccessMode() override; + virtual Any SAL_CALL get( const Any & rObj ) override; + virtual void SAL_CALL set( const Any & rObj, const Any & rValue ) override; + // XIdlField2: getType, getAccessMode and get are equal to XIdlField + virtual void SAL_CALL set( Any & rObj, const Any & rValue ) override; +}; + +} + +// XInterface + +Any IdlCompFieldImpl::queryInterface( const Type & rType ) +{ + Any aRet( ::cppu::queryInterface( rType, + static_cast< XIdlField * >( this ), + static_cast< XIdlField2 * >( this ) ) ); + return (aRet.hasValue() ? aRet : IdlMemberImpl::queryInterface( rType )); +} + +void IdlCompFieldImpl::acquire() noexcept +{ + IdlMemberImpl::acquire(); +} + +void IdlCompFieldImpl::release() noexcept +{ + IdlMemberImpl::release(); +} + +// XTypeProvider + +Sequence< Type > IdlCompFieldImpl::getTypes() +{ + static cppu::OTypeCollection s_aTypes( + cppu::UnoType<XIdlField2>::get(), + cppu::UnoType<XIdlField>::get(), + IdlMemberImpl::getTypes() ); + + return s_aTypes.getTypes(); +} + +Sequence< sal_Int8 > IdlCompFieldImpl::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XIdlMember + +Reference< XIdlClass > IdlCompFieldImpl::getDeclaringClass() +{ + if (! _xDeclClass.is()) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xDeclClass.is()) + { + typelib_CompoundTypeDescription * pTD = + reinterpret_cast<typelib_CompoundTypeDescription *>(getDeclTypeDescr()); + while (pTD) + { + typelib_TypeDescriptionReference ** ppTypeRefs = pTD->ppTypeRefs; + for ( sal_Int32 nPos = pTD->nMembers; nPos--; ) + { + if (td_equals( getTypeDescr(), ppTypeRefs[nPos] )) + { + _xDeclClass = getReflection()->forType( &pTD->aBase ); + return _xDeclClass; + } + } + pTD = pTD->pBaseTypeDescription; + } + } + } + return _xDeclClass; +} + +OUString IdlCompFieldImpl::getName() +{ + return IdlMemberImpl::getName(); +} + +// XIdlField + +Reference< XIdlClass > IdlCompFieldImpl::getType() +{ + return getReflection()->forType( getTypeDescr() ); +} + +FieldAccessMode IdlCompFieldImpl::getAccessMode() +{ + return FieldAccessMode_READWRITE; +} + +Any IdlCompFieldImpl::get( const Any & rObj ) +{ + if (rObj.getValueTypeClass() == css::uno::TypeClass_STRUCT || + rObj.getValueTypeClass() == css::uno::TypeClass_EXCEPTION) + { + typelib_TypeDescription * pObjTD = nullptr; + TYPELIB_DANGER_GET( &pObjTD, rObj.getValueTypeRef() ); + + typelib_TypeDescription * pTD = pObjTD; + typelib_TypeDescription * pDeclTD = getDeclTypeDescr(); + while (pTD && !typelib_typedescription_equals( pTD, pDeclTD )) + pTD = &reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->pBaseTypeDescription->aBase; + + OSL_ENSURE( pTD, "### illegal object type!" ); + if (pTD) + { + TYPELIB_DANGER_RELEASE( pObjTD ); + Any aRet; + uno_any_destruct( + &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_construct( + &aRet, const_cast<char *>(static_cast<char const *>(rObj.getValue()) + _nOffset), getTypeDescr(), + reinterpret_cast< uno_AcquireFunc >(cpp_acquire) ); + return aRet; + } + TYPELIB_DANGER_RELEASE( pObjTD ); + } + throw IllegalArgumentException( + "expected struct or exception, got " + rObj.getValueType().getTypeName(), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); +} + +void IdlCompFieldImpl::set( const Any & rObj, const Any & rValue ) +{ + if (rObj.getValueTypeClass() == css::uno::TypeClass_STRUCT || + rObj.getValueTypeClass() == css::uno::TypeClass_EXCEPTION) + { + typelib_TypeDescription * pObjTD = nullptr; + TYPELIB_DANGER_GET( &pObjTD, rObj.getValueTypeRef() ); + + typelib_TypeDescription * pTD = pObjTD; + typelib_TypeDescription * pDeclTD = getDeclTypeDescr(); + while (pTD && !typelib_typedescription_equals( pTD, pDeclTD )) + pTD = &reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->pBaseTypeDescription->aBase; + + OSL_ENSURE( pTD, "### illegal object type!" ); + if (pTD) + { + TYPELIB_DANGER_RELEASE( pObjTD ); + if (!coerce_assign( const_cast<char *>(static_cast<char const *>(rObj.getValue()) + _nOffset), getTypeDescr(), rValue, getReflection() )) + { + throw IllegalArgumentException( + "cannot assign value to destination", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 1 ); + } + return; + } + TYPELIB_DANGER_RELEASE( pObjTD ); + } + throw IllegalArgumentException( + "expected struct or exception, got " + rObj.getValueType().getTypeName(), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); +} + + +void IdlCompFieldImpl::set( Any & rObj, const Any & rValue ) +{ + if (rObj.getValueTypeClass() == css::uno::TypeClass_STRUCT || + rObj.getValueTypeClass() == css::uno::TypeClass_EXCEPTION) + { + typelib_TypeDescription * pObjTD = nullptr; + TYPELIB_DANGER_GET( &pObjTD, rObj.getValueTypeRef() ); + + typelib_TypeDescription * pTD = pObjTD; + typelib_TypeDescription * pDeclTD = getDeclTypeDescr(); + while (pTD && !typelib_typedescription_equals( pTD, pDeclTD )) + pTD = &reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->pBaseTypeDescription->aBase; + + OSL_ENSURE( pTD, "### illegal object type!" ); + if (pTD) + { + TYPELIB_DANGER_RELEASE( pObjTD ); + if (!coerce_assign( const_cast<char *>(static_cast<char const *>(rObj.getValue()) + _nOffset), getTypeDescr(), rValue, getReflection() )) + { + throw IllegalArgumentException( + "cannot assign to destination", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 1 ); + } + return; + } + TYPELIB_DANGER_RELEASE( pObjTD ); + } + throw IllegalArgumentException( + "expected struct or exception, got " + rObj.getValueType().getTypeName(), + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); +} + + +CompoundIdlClassImpl::~CompoundIdlClassImpl() +{ +} + + +sal_Bool CompoundIdlClassImpl::isAssignableFrom( const Reference< XIdlClass > & xType ) +{ + if (xType.is()) + { + TypeClass eTC = xType->getTypeClass(); + if (eTC == TypeClass_STRUCT || eTC == TypeClass_EXCEPTION) + { + if (equals( xType )) + return true; + else + { + const Sequence< Reference< XIdlClass > > & rSeq = xType->getSuperclasses(); + if (rSeq.hasElements()) + { + OSL_ENSURE( rSeq.getLength() == 1, "### unexpected len of super classes!" ); + return isAssignableFrom( rSeq[0] ); + } + } + } + } + return false; +} + +Sequence< Reference< XIdlClass > > CompoundIdlClassImpl::getSuperclasses() +{ + if (! _xSuperClass.is()) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xSuperClass.is()) + { + typelib_CompoundTypeDescription * pCompTypeDescr = getTypeDescr()->pBaseTypeDescription; + if (pCompTypeDescr) + _xSuperClass = getReflection()->forType( &pCompTypeDescr->aBase ); + } + } + if (_xSuperClass.is()) + return Sequence< Reference< XIdlClass > >( &_xSuperClass, 1 ); + else + return Sequence< Reference< XIdlClass > >(); +} + +Reference< XIdlField > CompoundIdlClassImpl::getField( const OUString & rName ) +{ + if (! m_xFields) + getFields(); // init fields + + const OUString2Field::const_iterator iFind( _aName2Field.find( rName ) ); + if (iFind != _aName2Field.end()) + return Reference< XIdlField >( (*iFind).second ); + else + return Reference< XIdlField >(); +} + +Sequence< Reference< XIdlField > > CompoundIdlClassImpl::getFields() +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xFields) + { + sal_Int32 nAll = 0; + typelib_CompoundTypeDescription * pCompTypeDescr = getTypeDescr(); + for ( ; pCompTypeDescr; pCompTypeDescr = pCompTypeDescr->pBaseTypeDescription ) + nAll += pCompTypeDescr->nMembers; + + Sequence< Reference< XIdlField > > aFields( nAll ); + Reference< XIdlField > * pSeq = aFields.getArray(); + + for ( pCompTypeDescr = getTypeDescr(); pCompTypeDescr; + pCompTypeDescr = pCompTypeDescr->pBaseTypeDescription ) + { + typelib_TypeDescriptionReference ** ppTypeRefs = pCompTypeDescr->ppTypeRefs; + rtl_uString ** ppNames = pCompTypeDescr->ppMemberNames; + sal_Int32 * pMemberOffsets = pCompTypeDescr->pMemberOffsets; + + for ( sal_Int32 nPos = pCompTypeDescr->nMembers; nPos--; ) + { + typelib_TypeDescription * pTD = nullptr; + TYPELIB_DANGER_GET( &pTD, ppTypeRefs[nPos] ); + OSL_ENSURE( pTD, "### cannot get field in struct!" ); + if (pTD) + { + OUString aName( ppNames[nPos] ); + _aName2Field[aName] = pSeq[--nAll] = new IdlCompFieldImpl( + getReflection(), aName, pTD, IdlClassImpl::getTypeDescr(), pMemberOffsets[nPos] ); + TYPELIB_DANGER_RELEASE( pTD ); + } + } + } + + m_xFields = std::move( aFields ); + } + return *m_xFields; +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crefl.cxx b/stoc/source/corereflection/crefl.cxx new file mode 100644 index 000000000..af1854e7e --- /dev/null +++ b/stoc/source/corereflection/crefl.cxx @@ -0,0 +1,379 @@ +/* -*- 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 <cppuhelper/queryinterface.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/typeprovider.hxx> +#include <sal/log.hxx> + +#include <com/sun/star/reflection/XConstantTypeDescription.hpp> +#include <com/sun/star/reflection/XTypeDescription.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <o3tl/any.hxx> +#include <uno/lbnames.h> + +using namespace css; +using namespace css::uno; +using namespace css::lang; +using namespace css::reflection; +using namespace cppu; +using namespace osl; + + +#include "base.hxx" + + +namespace stoc_corefl +{ + +IdlReflectionServiceImpl::IdlReflectionServiceImpl( + const Reference< XComponentContext > & xContext ) + : OComponentHelper( _aComponentMutex ) +{ + xContext->getValueByName( + "/singletons/com.sun.star.reflection.theTypeDescriptionManager" ) >>= _xTDMgr; + OSL_ENSURE( _xTDMgr.is(), "### cannot get singleton \"TypeDescriptionManager\" from context!" ); +} + +IdlReflectionServiceImpl::~IdlReflectionServiceImpl() {} + +// XInterface + +Any IdlReflectionServiceImpl::queryInterface( const Type & rType ) +{ + Any aRet( ::cppu::queryInterface( + rType, + static_cast< XIdlReflection * >( this ), + static_cast< XHierarchicalNameAccess * >( this ), + static_cast< XServiceInfo * >( this ) ) ); + + return (aRet.hasValue() ? aRet : OComponentHelper::queryInterface( rType )); +} + +void IdlReflectionServiceImpl::acquire() noexcept +{ + OComponentHelper::acquire(); +} + +void IdlReflectionServiceImpl::release() noexcept +{ + OComponentHelper::release(); +} + +// XTypeProvider + +Sequence< Type > IdlReflectionServiceImpl::getTypes() +{ + static OTypeCollection s_aTypes( + cppu::UnoType<XIdlReflection>::get(), + cppu::UnoType<XHierarchicalNameAccess>::get(), + cppu::UnoType<XServiceInfo>::get(), + OComponentHelper::getTypes() ); + + return s_aTypes.getTypes(); +} + +Sequence< sal_Int8 > IdlReflectionServiceImpl::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XComponent + +void IdlReflectionServiceImpl::dispose() +{ + OComponentHelper::dispose(); + + MutexGuard aGuard( _aComponentMutex ); + _aElements.clear(); +#ifdef TEST_LIST_CLASSES + OSL_ENSURE( g_aClassNames.empty(), "### idl classes still alive!" ); + for (auto const& className : g_aClassNames) + { + OUString aName(className); + } +#endif +} + +// XServiceInfo + +OUString IdlReflectionServiceImpl::getImplementationName() +{ + return "com.sun.star.comp.stoc.CoreReflection"; +} + +sal_Bool IdlReflectionServiceImpl::supportsService( const OUString & rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > IdlReflectionServiceImpl::getSupportedServiceNames() +{ + Sequence< OUString > seqNames { "com.sun.star.reflection.CoreReflection" }; + return seqNames; +} + +// XIdlReflection + +Reference< XIdlClass > IdlReflectionServiceImpl::getType( const Any & rObj ) +{ + return (rObj.hasValue() ? forType( rObj.getValueTypeRef() ) : Reference< XIdlClass >()); +} + + +inline Reference< XIdlClass > IdlReflectionServiceImpl::constructClass( + typelib_TypeDescription * pTypeDescr ) +{ + OSL_ENSURE( pTypeDescr->eTypeClass != typelib_TypeClass_TYPEDEF, "### unexpected typedef!" ); + + switch (pTypeDescr->eTypeClass) + { + case typelib_TypeClass_VOID: + case typelib_TypeClass_CHAR: + case typelib_TypeClass_BOOLEAN: + case typelib_TypeClass_BYTE: + case typelib_TypeClass_SHORT: + case typelib_TypeClass_UNSIGNED_SHORT: + case typelib_TypeClass_LONG: + case typelib_TypeClass_UNSIGNED_LONG: + case typelib_TypeClass_HYPER: + case typelib_TypeClass_UNSIGNED_HYPER: + case typelib_TypeClass_FLOAT: + case typelib_TypeClass_DOUBLE: + case typelib_TypeClass_STRING: + case typelib_TypeClass_ANY: + return new IdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_ENUM: + return new EnumIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_STRUCT: + case typelib_TypeClass_EXCEPTION: + return new CompoundIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_SEQUENCE: + return new ArrayIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_INTERFACE: + return new InterfaceIdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + case typelib_TypeClass_TYPE: + return new IdlClassImpl( this, pTypeDescr->pTypeName, pTypeDescr->eTypeClass, pTypeDescr ); + + default: + SAL_INFO("stoc", "corereflection type unsupported: " << pTypeDescr->pTypeName); + return Reference< XIdlClass >(); + } +} + +Reference< XIdlClass > IdlReflectionServiceImpl::forName( const OUString & rTypeName ) +{ + Reference< XIdlClass > xRet; + Any aAny( _aElements.getValue( rTypeName ) ); + + if (aAny.hasValue()) + { + aAny >>= xRet; + } + else + { + // try to get _type_ by name + typelib_TypeDescription * pTD = nullptr; + typelib_typedescription_getByName( &pTD, rTypeName.pData ); + if (pTD) + { + xRet = constructClass( pTD ); + if (xRet.is()) + _aElements.setValue( rTypeName, Any( xRet ) ); // update + typelib_typedescription_release( pTD ); + } + } + + return xRet; +} + +// XHierarchicalNameAccess + +Any IdlReflectionServiceImpl::getByHierarchicalName( const OUString & rName ) +{ + Any aRet( _aElements.getValue( rName ) ); + if (! aRet.hasValue()) + { + aRet = _xTDMgr->getByHierarchicalName( rName ); + if (aRet.getValueTypeClass() == TypeClass_INTERFACE) + { + // type retrieved from tdmgr + OSL_ASSERT( (*o3tl::forceAccess<Reference<XInterface>>(aRet))->queryInterface( + cppu::UnoType<XTypeDescription>::get()).hasValue() ); + + css::uno::Reference< css::reflection::XConstantTypeDescription > + ctd; + if (aRet >>= ctd) + { + aRet = ctd->getConstantValue(); + } + else + { + // if you are interested in a type then CALL forName()!!! + // this way is NOT recommended for types, because this method looks for constants first + + // if td manager found some type, it will be in the cache (hopefully... we just got it) + // so the second retrieving via c typelib callback chain should succeed... + + // try to get _type_ by name + typelib_TypeDescription * pTD = nullptr; + typelib_typedescription_getByName( &pTD, rName.pData ); + + aRet.clear(); // kick XTypeDescription interface + + if (pTD) + { + Reference< XIdlClass > xIdlClass( constructClass( pTD ) ); + aRet.setValue( &xIdlClass, cppu::UnoType<XIdlClass>::get()); + typelib_typedescription_release( pTD ); + } + } + } + // else is enum member(?) + + // update + if (!aRet.hasValue()) + throw container::NoSuchElementException( rName ); + + _aElements.setValue( rName, aRet ); + } + return aRet; +} + +sal_Bool IdlReflectionServiceImpl::hasByHierarchicalName( const OUString & rName ) +{ + try + { + return getByHierarchicalName( rName ).hasValue(); + } + catch (container::NoSuchElementException &) + { + } + return false; +} + + +Reference< XIdlClass > IdlReflectionServiceImpl::forType( typelib_TypeDescription * pTypeDescr ) +{ + Reference< XIdlClass > xRet; + OUString aName( pTypeDescr->pTypeName ); + Any aAny( _aElements.getValue( aName ) ); + + if (aAny.hasValue()) + { + aAny >>= xRet; + } + else + { + xRet = constructClass( pTypeDescr ); + if (xRet.is()) + _aElements.setValue( aName, Any( xRet ) ); // update + } + + return xRet; +} + +Reference< XIdlClass > IdlReflectionServiceImpl::forType( typelib_TypeDescriptionReference * pRef ) +{ + typelib_TypeDescription * pTD = nullptr; + TYPELIB_DANGER_GET( &pTD, pRef ); + if (pTD) + { + Reference< XIdlClass > xRet = forType( pTD ); + TYPELIB_DANGER_RELEASE( pTD ); + return xRet; + } + throw RuntimeException( + "IdlReflectionServiceImpl::forType() failed!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); +} + + +const Mapping & IdlReflectionServiceImpl::getCpp2Uno() +{ + if (! _aCpp2Uno.is()) + { + MutexGuard aGuard( getMutexAccess() ); + if (! _aCpp2Uno.is()) + { + _aCpp2Uno = Mapping( CPPU_CURRENT_LANGUAGE_BINDING_NAME, UNO_LB_UNO ); + OSL_ENSURE( _aCpp2Uno.is(), "### cannot get c++ to uno mapping!" ); + if (! _aCpp2Uno.is()) + { + throw RuntimeException( + "cannot get c++ to uno mapping!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); + } + } + } + return _aCpp2Uno; +} + +const Mapping & IdlReflectionServiceImpl::getUno2Cpp() +{ + if (! _aUno2Cpp.is()) + { + MutexGuard aGuard( getMutexAccess() ); + if (! _aUno2Cpp.is()) + { + _aUno2Cpp = Mapping( UNO_LB_UNO, CPPU_CURRENT_LANGUAGE_BINDING_NAME ); + OSL_ENSURE( _aUno2Cpp.is(), "### cannot get uno to c++ mapping!" ); + if (! _aUno2Cpp.is()) + { + throw RuntimeException( + "cannot get uno to c++ mapping!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); + } + } + } + return _aUno2Cpp; +} + +uno_Interface * IdlReflectionServiceImpl::mapToUno( + const Any & rObj, typelib_InterfaceTypeDescription * pTo ) +{ + Reference< XInterface > xObj; + if (extract( rObj, pTo, xObj, this )) + return static_cast<uno_Interface *>(getCpp2Uno().mapInterface( xObj.get(), pTo )); + + throw RuntimeException( + "illegal object given!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); +} + +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_CoreReflection_get_implementation( + css::uno::XComponentContext * context, + css::uno::Sequence<css::uno::Any> const & arguments) +{ + SAL_WARN_IF( + arguments.hasElements(), "stoc", "unexpected singleton arguments"); + return cppu::acquire(new stoc_corefl::IdlReflectionServiceImpl(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/crenum.cxx b/stoc/source/corereflection/crenum.cxx new file mode 100644 index 000000000..dae3449d3 --- /dev/null +++ b/stoc/source/corereflection/crenum.cxx @@ -0,0 +1,206 @@ +/* -*- 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 "base.hxx" + +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/typeprovider.hxx> + +#include <com/sun/star/reflection/XIdlField2.hpp> + +using namespace css::lang; +using namespace css::reflection; +using namespace css::uno; + +namespace stoc_corefl +{ + +namespace { + +class IdlEnumFieldImpl + : public IdlMemberImpl + , public XIdlField + , public XIdlField2 +{ + sal_Int32 _nValue; + +public: + IdlEnumFieldImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, sal_Int32 nValue ) + : IdlMemberImpl( pReflection, rName, pTypeDescr, pTypeDescr ) + , _nValue( nValue ) + {} + + // XInterface + virtual Any SAL_CALL queryInterface( const Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual Sequence< Type > SAL_CALL getTypes() override; + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XIdlMember + virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; + // XIdlField + virtual Reference< XIdlClass > SAL_CALL getType() override; + virtual FieldAccessMode SAL_CALL getAccessMode() override; + virtual Any SAL_CALL get( const Any & rObj ) override; + virtual void SAL_CALL set( const Any & rObj, const Any & rValue ) override; + // XIdlField2: getType, getAccessMode and get are equal to XIdlField + virtual void SAL_CALL set( Any & rObj, const Any & rValue ) override; +}; + +} + +// XInterface + +Any IdlEnumFieldImpl::queryInterface( const Type & rType ) +{ + Any aRet( ::cppu::queryInterface( rType, + static_cast< XIdlField * >( this ), + static_cast< XIdlField2 * >( this ) ) ); + return (aRet.hasValue() ? aRet : IdlMemberImpl::queryInterface( rType )); +} + +void IdlEnumFieldImpl::acquire() noexcept +{ + IdlMemberImpl::acquire(); +} + +void IdlEnumFieldImpl::release() noexcept +{ + IdlMemberImpl::release(); +} + +// XTypeProvider + +Sequence< Type > IdlEnumFieldImpl::getTypes() +{ + static cppu::OTypeCollection s_aTypes( + cppu::UnoType<XIdlField2>::get(), + cppu::UnoType<XIdlField>::get(), + IdlMemberImpl::getTypes() ); + + return s_aTypes.getTypes(); +} + +Sequence< sal_Int8 > IdlEnumFieldImpl::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XIdlMember + +Reference< XIdlClass > IdlEnumFieldImpl::getDeclaringClass() +{ + return IdlMemberImpl::getDeclaringClass(); +} + +OUString IdlEnumFieldImpl::getName() +{ + return IdlMemberImpl::getName(); +} + +// XIdlField + +Reference< XIdlClass > IdlEnumFieldImpl::getType() +{ + return getDeclaringClass(); +} + +FieldAccessMode IdlEnumFieldImpl::getAccessMode() +{ + return FieldAccessMode_READONLY; +} + +Any IdlEnumFieldImpl::get( const Any & ) +{ + return Any( &_nValue, getTypeDescr() ); +} + +void IdlEnumFieldImpl::set( const Any &, const Any & ) +{ + throw IllegalAccessException( + "cannot set enum field, it is constant", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); +} + +void IdlEnumFieldImpl::set( Any &, const Any & ) +{ + throw IllegalAccessException( + "cannot set enum field, it is constant", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); +} + + +EnumIdlClassImpl::~EnumIdlClassImpl() +{ +} + +// IdlClassImpl modifications + +Reference< XIdlField > EnumIdlClassImpl::getField( const OUString & rName ) +{ + if (! m_xFields) + getFields(); // init members + + const OUString2Field::const_iterator iFind( _aName2Field.find( rName ) ); + if (iFind != _aName2Field.end()) + return (*iFind).second; + else + return Reference< XIdlField >(); +} + +Sequence< Reference< XIdlField > > EnumIdlClassImpl::getFields() +{ + if (! m_xFields) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xFields) + { + sal_Int32 nFields = getTypeDescr()->nEnumValues; + Sequence< Reference< XIdlField > > aFields( nFields ); + Reference< XIdlField > * pSeq = aFields.getArray(); + + while (nFields--) + { + OUString aName( getTypeDescr()->ppEnumNames[nFields] ); + _aName2Field[aName] = pSeq[nFields] = new IdlEnumFieldImpl( + getReflection(), aName, IdlClassImpl::getTypeDescr(), getTypeDescr()->pEnumValues[nFields] ); + } + + m_xFields = std::move( aFields ); + } + } + return *m_xFields; +} + +void EnumIdlClassImpl::createObject( Any & rObj ) +{ + sal_Int32 eVal = + reinterpret_cast<typelib_EnumTypeDescription *>(IdlClassImpl::getTypeDescr())->nDefaultEnumValue; + rObj.setValue( &eVal, IdlClassImpl::getTypeDescr() ); +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/criface.cxx b/stoc/source/corereflection/criface.cxx new file mode 100644 index 000000000..88dcc29ff --- /dev/null +++ b/stoc/source/corereflection/criface.cxx @@ -0,0 +1,916 @@ +/* -*- 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 <sal/config.h> + +#include <cassert> +#include <cstddef> +#include <limits> + +#ifdef SAL_UNX +#include <sal/alloca.h> +#endif +#include <o3tl/any.hxx> +#include <typelib/typedescription.hxx> +#include <uno/data.h> + +#include "base.hxx" + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/reflection/XIdlField2.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/typeprovider.hxx> + +using namespace css::lang; +using namespace css::reflection; +using namespace css::uno; + +namespace { + +std::size_t multipleOf16(std::size_t n) { + assert(n <= std::numeric_limits<std::size_t>::max() - 15); + return (n + 15) & ~std::size_t(15); +} + +} + +namespace stoc_corefl +{ + +namespace { + +class IdlAttributeFieldImpl + : public IdlMemberImpl + , public XIdlField + , public XIdlField2 +{ +public: + typelib_InterfaceAttributeTypeDescription * getAttributeTypeDescr() const + { return reinterpret_cast<typelib_InterfaceAttributeTypeDescription *>(getTypeDescr()); } + + IdlAttributeFieldImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr ) + : IdlMemberImpl( pReflection, rName, pTypeDescr, pDeclTypeDescr ) + {} + + // XInterface + virtual Any SAL_CALL queryInterface( const Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual Sequence< Type > SAL_CALL getTypes() override; + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XIdlMember + virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; + // XIdlField + virtual Reference< XIdlClass > SAL_CALL getType() override; + virtual FieldAccessMode SAL_CALL getAccessMode() override; + virtual Any SAL_CALL get( const Any & rObj ) override; + virtual void SAL_CALL set( const Any & rObj, const Any & rValue ) override; + // XIdlField2: getType, getAccessMode and get are equal to XIdlField + virtual void SAL_CALL set( Any & rObj, const Any & rValue ) override; + +private: + void checkException( + uno_Any * exception, Reference< XInterface > const & context) const; +}; + +} + +// XInterface + +Any IdlAttributeFieldImpl::queryInterface( const Type & rType ) +{ + Any aRet( ::cppu::queryInterface( rType, + static_cast< XIdlField * >( this ), + static_cast< XIdlField2 * >( this ) ) ); + return (aRet.hasValue() ? aRet : IdlMemberImpl::queryInterface( rType )); +} + +void IdlAttributeFieldImpl::acquire() noexcept +{ + IdlMemberImpl::acquire(); +} + +void IdlAttributeFieldImpl::release() noexcept +{ + IdlMemberImpl::release(); +} + +// XTypeProvider + +Sequence< Type > IdlAttributeFieldImpl::getTypes() +{ + static cppu::OTypeCollection s_aTypes( + cppu::UnoType<XIdlField2>::get(), + cppu::UnoType<XIdlField>::get(), + IdlMemberImpl::getTypes() ); + + return s_aTypes.getTypes(); +} + +Sequence< sal_Int8 > IdlAttributeFieldImpl::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XIdlMember + +Reference< XIdlClass > IdlAttributeFieldImpl::getDeclaringClass() +{ + if (! _xDeclClass.is()) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xDeclClass.is()) + { + OUString aName(getAttributeTypeDescr()->aBase.aBase.pTypeName); + sal_Int32 i = aName.indexOf(':'); + OSL_ASSERT(i >= 0); + _xDeclClass = getReflection()->forName(aName.copy(0, i)); + } + } + return _xDeclClass; +} + +OUString IdlAttributeFieldImpl::getName() +{ + return IdlMemberImpl::getName(); +} + +// XIdlField + +Reference< XIdlClass > IdlAttributeFieldImpl::getType() +{ + return getReflection()->forType( + getAttributeTypeDescr()->pAttributeTypeRef ); +} + +FieldAccessMode IdlAttributeFieldImpl::getAccessMode() +{ + return (getAttributeTypeDescr()->bReadOnly + ? FieldAccessMode_READONLY : FieldAccessMode_READWRITE); +} + +Any IdlAttributeFieldImpl::get( const Any & rObj ) +{ + uno_Interface * pUnoI = getReflection()->mapToUno( + rObj, reinterpret_cast<typelib_InterfaceTypeDescription *>(getDeclTypeDescr()) ); + OSL_ENSURE( pUnoI, "### illegal destination object given!" ); + if (pUnoI) + { + TypeDescription aTD( getAttributeTypeDescr()->pAttributeTypeRef ); + typelib_TypeDescription * pTD = aTD.get(); + + uno_Any aExc; + uno_Any * pExc = &aExc; + void * pReturn = alloca( pTD->nSize ); + + (*pUnoI->pDispatcher)( pUnoI, getTypeDescr(), pReturn, nullptr, &pExc ); + (*pUnoI->release)( pUnoI ); + + checkException(pExc, *o3tl::doAccess<Reference<XInterface>>(rObj)); + Any aRet; + uno_any_destruct( + &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_constructAndConvert( &aRet, pReturn, pTD, getReflection()->getUno2Cpp().get() ); + uno_destructData( pReturn, pTD, nullptr ); + return aRet; + } + throw IllegalArgumentException( + "illegal object given!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); +} + +void IdlAttributeFieldImpl::set( Any & rObj, const Any & rValue ) +{ + if (getAttributeTypeDescr()->bReadOnly) + { + throw IllegalAccessException( + "cannot set readonly attribute!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)) ); + } + + uno_Interface * pUnoI = getReflection()->mapToUno( + rObj, reinterpret_cast<typelib_InterfaceTypeDescription *>(getDeclTypeDescr()) ); + OSL_ENSURE( pUnoI, "### illegal destination object given!" ); + if (pUnoI) + { + TypeDescription aTD( getAttributeTypeDescr()->pAttributeTypeRef ); + typelib_TypeDescription * pTD = aTD.get(); + + // construct uno value to be set + void * pArgs[1]; + void * pArg = pArgs[0] = alloca( pTD->nSize ); + + bool bAssign; + if (pTD->eTypeClass == typelib_TypeClass_ANY) + { + uno_copyAndConvertData( pArg, const_cast< Any * >(&rValue), + pTD, getReflection()->getCpp2Uno().get() ); + bAssign = true; + } + else if (typelib_typedescriptionreference_equals( rValue.getValueTypeRef(), pTD->pWeakRef )) + { + uno_copyAndConvertData( pArg, const_cast< void * >(rValue.getValue()), + pTD, getReflection()->getCpp2Uno().get() ); + bAssign = true; + } + else if (pTD->eTypeClass == typelib_TypeClass_INTERFACE) + { + Reference< XInterface > xObj; + bAssign = extract( + rValue, reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD), xObj, + getReflection() ); + if (bAssign) + { + *static_cast<void **>(pArg) = getReflection()->getCpp2Uno().mapInterface( + xObj.get(), reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD) ); + } + } + else + { + typelib_TypeDescription * pValueTD = nullptr; + TYPELIB_DANGER_GET( &pValueTD, rValue.getValueTypeRef() ); + // construct temp uno val to do proper assignment: todo opt + void * pTemp = alloca( pValueTD->nSize ); + uno_copyAndConvertData( + pTemp, const_cast<void *>(rValue.getValue()), pValueTD, getReflection()->getCpp2Uno().get() ); + uno_constructData( + pArg, pTD ); + // assignment does simple conversion + bAssign = uno_assignData( + pArg, pTD, pTemp, pValueTD, nullptr, nullptr, nullptr ); + uno_destructData( + pTemp, pValueTD, nullptr ); + TYPELIB_DANGER_RELEASE( pValueTD ); + } + + if (bAssign) + { + uno_Any aExc; + uno_Any * pExc = &aExc; + (*pUnoI->pDispatcher)( pUnoI, getTypeDescr(), nullptr, pArgs, &pExc ); + (*pUnoI->release)( pUnoI ); + + uno_destructData( pArg, pTD, nullptr ); + checkException(pExc, *o3tl::doAccess<Reference<XInterface>>(rObj)); + return; + } + (*pUnoI->release)( pUnoI ); + + throw IllegalArgumentException( + "illegal value given!", + *o3tl::doAccess<Reference<XInterface>>(rObj), 1 ); + } + throw IllegalArgumentException( + "illegal destination object given!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); +} + +void IdlAttributeFieldImpl::set( const Any & rObj, const Any & rValue ) +{ + IdlAttributeFieldImpl::set( const_cast< Any & >( rObj ), rValue ); +} + +void IdlAttributeFieldImpl::checkException( + uno_Any * exception, Reference< XInterface > const & context) const +{ + if (exception == nullptr) + return; + + Any e; + uno_any_destruct(&e, reinterpret_cast< uno_ReleaseFunc >(cpp_release)); + uno_type_any_constructAndConvert( + &e, exception->pData, exception->pType, + getReflection()->getUno2Cpp().get()); + uno_any_destruct(exception, nullptr); + if (!e.isExtractableTo( + cppu::UnoType<RuntimeException>::get())) + { + throw WrappedTargetRuntimeException( + "non-RuntimeException occurred when accessing an" + " interface type attribute", + context, e); + } + cppu::throwException(e); +} + +namespace { + +class IdlInterfaceMethodImpl + : public IdlMemberImpl + , public XIdlMethod +{ + std::optional<Sequence< Reference< XIdlClass > >> m_xExceptionTypes; + std::optional<Sequence< Reference< XIdlClass > >> m_xParamTypes; + std::optional<Sequence< ParamInfo >> m_xParamInfos; + +public: + typelib_InterfaceMethodTypeDescription * getMethodTypeDescr() const + { return reinterpret_cast<typelib_InterfaceMethodTypeDescription *>(getTypeDescr()); } + + IdlInterfaceMethodImpl( IdlReflectionServiceImpl * pReflection, const OUString & rName, + typelib_TypeDescription * pTypeDescr, typelib_TypeDescription * pDeclTypeDescr ) + : IdlMemberImpl( pReflection, rName, pTypeDescr, pDeclTypeDescr ) + {} + + // XInterface + virtual Any SAL_CALL queryInterface( const Type & rType ) override; + virtual void SAL_CALL acquire() noexcept override; + virtual void SAL_CALL release() noexcept override; + + // XTypeProvider + virtual Sequence< Type > SAL_CALL getTypes() override; + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId() override; + + // XIdlMember + virtual Reference< XIdlClass > SAL_CALL getDeclaringClass() override; + virtual OUString SAL_CALL getName() override; + // XIdlMethod + virtual Reference< XIdlClass > SAL_CALL getReturnType() override; + virtual Sequence< Reference< XIdlClass > > SAL_CALL getParameterTypes() override; + virtual Sequence< ParamInfo > SAL_CALL getParameterInfos() override; + virtual Sequence< Reference< XIdlClass > > SAL_CALL getExceptionTypes() override; + virtual MethodMode SAL_CALL getMode() override; + virtual Any SAL_CALL invoke( const Any & rObj, Sequence< Any > & rArgs ) override; +}; + +} + +// XInterface + +Any IdlInterfaceMethodImpl::queryInterface( const Type & rType ) +{ + Any aRet( ::cppu::queryInterface( rType, static_cast< XIdlMethod * >( this ) ) ); + return (aRet.hasValue() ? aRet : IdlMemberImpl::queryInterface( rType )); +} + +void IdlInterfaceMethodImpl::acquire() noexcept +{ + IdlMemberImpl::acquire(); +} + +void IdlInterfaceMethodImpl::release() noexcept +{ + IdlMemberImpl::release(); +} + +// XTypeProvider + +Sequence< Type > IdlInterfaceMethodImpl::getTypes() +{ + static cppu::OTypeCollection s_aTypes( + cppu::UnoType<XIdlMethod>::get(), + IdlMemberImpl::getTypes() ); + + return s_aTypes.getTypes(); +} + +Sequence< sal_Int8 > IdlInterfaceMethodImpl::getImplementationId() +{ + return css::uno::Sequence<sal_Int8>(); +} + +// XIdlMember + +Reference< XIdlClass > IdlInterfaceMethodImpl::getDeclaringClass() +{ + if (! _xDeclClass.is()) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _xDeclClass.is()) + { + OUString aName(getMethodTypeDescr()->aBase.aBase.pTypeName); + sal_Int32 i = aName.indexOf(':'); + OSL_ASSERT(i >= 0); + _xDeclClass = getReflection()->forName(aName.copy(0, i)); + } + } + return _xDeclClass; +} + +OUString IdlInterfaceMethodImpl::getName() +{ + return IdlMemberImpl::getName(); +} + +// XIdlMethod + +Reference< XIdlClass > SAL_CALL IdlInterfaceMethodImpl::getReturnType() +{ + return getReflection()->forType( getMethodTypeDescr()->pReturnTypeRef ); +} + +Sequence< Reference< XIdlClass > > IdlInterfaceMethodImpl::getExceptionTypes() +{ + if (! m_xExceptionTypes) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xExceptionTypes) + { + sal_Int32 nExc = getMethodTypeDescr()->nExceptions; + Sequence< Reference< XIdlClass > > aTempExceptionTypes( nExc ); + Reference< XIdlClass > * pExceptionTypes = aTempExceptionTypes.getArray(); + + typelib_TypeDescriptionReference ** ppExc = + getMethodTypeDescr()->ppExceptions; + IdlReflectionServiceImpl * pRefl = getReflection(); + + while (nExc--) + pExceptionTypes[nExc] = pRefl->forType( ppExc[nExc] ); + + m_xExceptionTypes = std::move(aTempExceptionTypes); + } + } + return *m_xExceptionTypes; +} + +Sequence< Reference< XIdlClass > > IdlInterfaceMethodImpl::getParameterTypes() +{ + if (! m_xParamTypes) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xParamTypes) + { + sal_Int32 nParams = getMethodTypeDescr()->nParams; + Sequence< Reference< XIdlClass > > aTempParamTypes( nParams ); + Reference< XIdlClass > * pParamTypes = aTempParamTypes.getArray(); + + typelib_MethodParameter * pTypelibParams = + getMethodTypeDescr()->pParams; + IdlReflectionServiceImpl * pRefl = getReflection(); + + while (nParams--) + pParamTypes[nParams] = pRefl->forType( pTypelibParams[nParams].pTypeRef ); + + m_xParamTypes = std::move(aTempParamTypes); + } + } + return *m_xParamTypes; +} + +Sequence< ParamInfo > IdlInterfaceMethodImpl::getParameterInfos() +{ + if (! m_xParamInfos) + { + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! m_xParamInfos) + { + sal_Int32 nParams = getMethodTypeDescr()->nParams; + Sequence< ParamInfo > aTempParamInfos( nParams ); + ParamInfo * pParamInfos = aTempParamInfos.getArray(); + + typelib_MethodParameter * pTypelibParams = + getMethodTypeDescr()->pParams; + + if (m_xParamTypes) // use param types + { + const Reference< XIdlClass > * pParamTypes = m_xParamTypes->getConstArray(); + + while (nParams--) + { + const typelib_MethodParameter & rParam = pTypelibParams[nParams]; + ParamInfo & rInfo = pParamInfos[nParams]; + rInfo.aName = rParam.pName; + if (rParam.bIn) + rInfo.aMode = (rParam.bOut ? ParamMode_INOUT : ParamMode_IN); + else + rInfo.aMode = ParamMode_OUT; + rInfo.aType = pParamTypes[nParams]; + } + } + else // make also param types sequence if not already initialized + { + Sequence< Reference< XIdlClass > > aTempParamTypes( nParams ); + Reference< XIdlClass > * pParamTypes = aTempParamTypes.getArray(); + + IdlReflectionServiceImpl * pRefl = getReflection(); + + while (nParams--) + { + const typelib_MethodParameter & rParam = pTypelibParams[nParams]; + ParamInfo & rInfo = pParamInfos[nParams]; + rInfo.aName = rParam.pName; + if (rParam.bIn) + rInfo.aMode = (rParam.bOut ? ParamMode_INOUT : ParamMode_IN); + else + rInfo.aMode = ParamMode_OUT; + rInfo.aType = pParamTypes[nParams] = pRefl->forType( rParam.pTypeRef ); + } + + m_xParamTypes = std::move(aTempParamTypes); + } + + m_xParamInfos = std::move(aTempParamInfos); + } + } + return *m_xParamInfos; +} + +MethodMode SAL_CALL IdlInterfaceMethodImpl::getMode() +{ + return + getMethodTypeDescr()->bOneWay ? MethodMode_ONEWAY : MethodMode_TWOWAY; +} + +Any SAL_CALL IdlInterfaceMethodImpl::invoke( const Any & rObj, Sequence< Any > & rArgs ) +{ + if (auto ifc = o3tl::tryAccess<css::uno::Reference<css::uno::XInterface>>( + rObj)) + { + // acquire()/ release() + if (rtl_ustr_ascii_compare( getTypeDescr()->pTypeName->buffer, + "com.sun.star.uno.XInterface::acquire" ) == 0) + { + (*ifc)->acquire(); + return Any(); + } + else if (rtl_ustr_ascii_compare( getTypeDescr()->pTypeName->buffer, + "com.sun.star.uno.XInterface::release" ) == 0) + { + (*ifc)->release(); + return Any(); + } + } + + uno_Interface * pUnoI = getReflection()->mapToUno( + rObj, reinterpret_cast<typelib_InterfaceTypeDescription *>(getDeclTypeDescr()) ); + OSL_ENSURE( pUnoI, "### illegal destination object given!" ); + if (pUnoI) + { + sal_Int32 nParams = getMethodTypeDescr()->nParams; + if (rArgs.getLength() != nParams) + { + (*pUnoI->release)( pUnoI ); + throw IllegalArgumentException( + "expected " + OUString::number(nParams) + + " arguments, got " + OUString::number(rArgs.getLength()), + *o3tl::doAccess<Reference<XInterface>>(rObj), 1 ); + } + + Any * pCppArgs = rArgs.getArray(); + typelib_MethodParameter * pParams = getMethodTypeDescr()->pParams; + typelib_TypeDescription * pReturnType = nullptr; + TYPELIB_DANGER_GET( + &pReturnType, getMethodTypeDescr()->pReturnTypeRef ); + + // C/C++ ABIs typically assume that structs are padded at the end, and + // that those padding bytes may be written to (e.g., to write into the + // end of a "short" struct by writing the full contents of a "long" + // register); so create enough space here (assuming that no ABI requires + // padding larger than 16 byte boundaries): + void * pUnoReturn = (pReturnType->nSize == 0) ? nullptr : alloca( multipleOf16(pReturnType->nSize) ); + void ** ppUnoArgs = static_cast<void **>(alloca( sizeof(void *) * nParams *2 )); + typelib_TypeDescription ** ppParamTypes = reinterpret_cast<typelib_TypeDescription **>(ppUnoArgs + nParams); + + // convert arguments + for ( sal_Int32 nPos = 0; nPos < nParams; ++nPos ) + { + ppParamTypes[nPos] = nullptr; + TYPELIB_DANGER_GET( ppParamTypes + nPos, pParams[nPos].pTypeRef ); + typelib_TypeDescription * pTD = ppParamTypes[nPos]; + + ppUnoArgs[nPos] = alloca( pTD->nSize ); + if (pParams[nPos].bIn) + { + bool bAssign; + if (typelib_typedescriptionreference_equals( + pCppArgs[nPos].getValueTypeRef(), pTD->pWeakRef )) + { + uno_type_copyAndConvertData( + ppUnoArgs[nPos], const_cast<void *>(pCppArgs[nPos].getValue()), + pCppArgs[nPos].getValueTypeRef(), getReflection()->getCpp2Uno().get() ); + bAssign = true; + } + else if (pTD->eTypeClass == typelib_TypeClass_ANY) + { + uno_type_any_constructAndConvert( + static_cast<uno_Any *>(ppUnoArgs[nPos]), const_cast<void *>(pCppArgs[nPos].getValue()), + pCppArgs[nPos].getValueTypeRef(), getReflection()->getCpp2Uno().get() ); + bAssign = true; + } + else if (pTD->eTypeClass == typelib_TypeClass_INTERFACE) + { + Reference< XInterface > xDest; + bAssign = extract( + pCppArgs[nPos], reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD), + xDest, getReflection() ); + if (bAssign) + { + *static_cast<void **>(ppUnoArgs[nPos]) = getReflection()->getCpp2Uno().mapInterface( + xDest.get(), reinterpret_cast<typelib_InterfaceTypeDescription *>(pTD) ); + } + } + else + { + typelib_TypeDescription * pValueTD = nullptr; + TYPELIB_DANGER_GET( &pValueTD, pCppArgs[nPos].getValueTypeRef() ); + // construct temp uno val to do proper assignment: todo opt + void * pTemp = alloca( pValueTD->nSize ); + uno_copyAndConvertData( + pTemp, const_cast<void *>(pCppArgs[nPos].getValue()), pValueTD, + getReflection()->getCpp2Uno().get() ); + uno_constructData( + ppUnoArgs[nPos], pTD ); + // assignment does simple conversion + bAssign = uno_assignData( + ppUnoArgs[nPos], pTD, pTemp, pValueTD, nullptr, nullptr, nullptr ); + uno_destructData( + pTemp, pValueTD, nullptr ); + TYPELIB_DANGER_RELEASE( pValueTD ); + } + + if (! bAssign) + { + IllegalArgumentException aExc( + "cannot coerce argument type during corereflection call:" + "\narg no.: " + OUString::number(nPos) + + " expected: \"" + OUString::unacquired(&pTD->pTypeName) + + "\" actual: \"" + OUString::unacquired(&pCppArgs[nPos].getValueTypeRef()->pTypeName) + + "\"", + *o3tl::doAccess<Reference<XInterface>>(rObj), static_cast<sal_Int16>(nPos) ); + + // cleanup + while (nPos--) + { + if (pParams[nPos].bIn) + uno_destructData( ppUnoArgs[nPos], ppParamTypes[nPos], nullptr ); + TYPELIB_DANGER_RELEASE( ppParamTypes[nPos] ); + } + TYPELIB_DANGER_RELEASE( pReturnType ); + (*pUnoI->release)( pUnoI ); + + throw aExc; + } + } + } + + uno_Any aUnoExc; + uno_Any * pUnoExc = &aUnoExc; + + (*pUnoI->pDispatcher)( + pUnoI, getTypeDescr(), pUnoReturn, ppUnoArgs, &pUnoExc ); + (*pUnoI->release)( pUnoI ); + + Any aRet; + if (pUnoExc) + { + // cleanup + while (nParams--) + { + if (pParams[nParams].bIn) + uno_destructData( ppUnoArgs[nParams], ppParamTypes[nParams], nullptr ); + TYPELIB_DANGER_RELEASE( ppParamTypes[nParams] ); + } + TYPELIB_DANGER_RELEASE( pReturnType ); + + InvocationTargetException aExc; + aExc.Context = *o3tl::doAccess<Reference<XInterface>>(rObj); + aExc.Message = "exception occurred during invocation!"; + uno_any_destruct( + &aExc.TargetException, + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_type_copyAndConvertData( + &aExc.TargetException, pUnoExc, cppu::UnoType<Any>::get().getTypeLibType(), + getReflection()->getUno2Cpp().get() ); + uno_any_destruct( pUnoExc, nullptr ); + throw aExc; + } + else + { + // reconvert arguments and cleanup + while (nParams--) + { + if (pParams[nParams].bOut) // write back + { + uno_any_destruct( + &pCppArgs[nParams], + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_constructAndConvert( + &pCppArgs[nParams], ppUnoArgs[nParams], ppParamTypes[nParams], + getReflection()->getUno2Cpp().get() ); + } + uno_destructData( ppUnoArgs[nParams], ppParamTypes[nParams], nullptr ); + TYPELIB_DANGER_RELEASE( ppParamTypes[nParams] ); + } + uno_any_destruct( + &aRet, reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + uno_any_constructAndConvert( + &aRet, pUnoReturn, pReturnType, + getReflection()->getUno2Cpp().get() ); + uno_destructData( pUnoReturn, pReturnType, nullptr ); + TYPELIB_DANGER_RELEASE( pReturnType ); + } + return aRet; + } + throw IllegalArgumentException( + "illegal destination object given!", + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); +} + + +InterfaceIdlClassImpl::~InterfaceIdlClassImpl() +{ + for ( sal_Int32 nPos = _nMethods + _nAttributes; nPos--; ) + typelib_typedescription_release( _pSortedMemberInit[nPos].second ); +} + + +Sequence< Reference< XIdlClass > > InterfaceIdlClassImpl::getSuperclasses() +{ + ::osl::MutexGuard aGuard(getMutexAccess()); + if (!_xSuperClasses.hasElements()) { + typelib_InterfaceTypeDescription * pType = getTypeDescr(); + _xSuperClasses.realloc(pType->nBaseTypes); + auto pSuperClasses = _xSuperClasses.getArray(); + for (sal_Int32 i = 0; i < pType->nBaseTypes; ++i) { + pSuperClasses[i] = getReflection()->forType( + &pType->ppBaseTypes[i]->aBase); + OSL_ASSERT(_xSuperClasses[i].is()); + } + } + return _xSuperClasses; +} + +void InterfaceIdlClassImpl::initMembers() +{ + sal_Int32 nAll = getTypeDescr()->nAllMembers; + std::unique_ptr<MemberInit[]> pSortedMemberInit(new MemberInit[nAll]); + typelib_TypeDescriptionReference ** ppAllMembers = getTypeDescr()->ppAllMembers; + + for ( sal_Int32 nPos = 0; nPos < nAll; ++nPos ) + { + sal_Int32 nIndex; + if (ppAllMembers[nPos]->eTypeClass == typelib_TypeClass_INTERFACE_METHOD) + { + // methods to front + nIndex = _nMethods; + ++_nMethods; + } + else + { + ++_nAttributes; + nIndex = (nAll - _nAttributes); + // attributes at the back + } + + typelib_TypeDescription * pTD = nullptr; + typelib_typedescriptionreference_getDescription( &pTD, ppAllMembers[nPos] ); + assert(pTD && "### cannot get type description!"); + pSortedMemberInit[nIndex].first = reinterpret_cast<typelib_InterfaceMemberTypeDescription *>(pTD)->pMemberName; + pSortedMemberInit[nIndex].second = pTD; + } + + _pSortedMemberInit = std::move(pSortedMemberInit); +} + +sal_Bool InterfaceIdlClassImpl::isAssignableFrom( const Reference< XIdlClass > & xType ) +{ + if (xType.is() && xType->getTypeClass() == TypeClass_INTERFACE) + { + if (equals( xType )) + return true; + else + { + const Sequence< Reference< XIdlClass > > & rSeq = xType->getSuperclasses(); + if (std::any_of(rSeq.begin(), rSeq.end(), + [this](const Reference<XIdlClass>& rType){ return isAssignableFrom(rType); })) + return true; + } + } + return false; +} + +Uik InterfaceIdlClassImpl::getUik() +{ + return Uik(0, 0, 0, 0, 0); + // Uiks are deprecated and this function must not be called +} + +Sequence< Reference< XIdlMethod > > InterfaceIdlClassImpl::getMethods() +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _pSortedMemberInit) + initMembers(); + + // create methods sequence + Sequence< Reference< XIdlMethod > > aRet( _nMethods ); + Reference< XIdlMethod > * pRet = aRet.getArray(); + for ( sal_Int32 nPos = _nMethods; nPos--; ) + { + + /*_aName2Method[_pSortedMemberInit[nPos].first] = */pRet[nPos] = new IdlInterfaceMethodImpl( + getReflection(), _pSortedMemberInit[nPos].first, + _pSortedMemberInit[nPos].second, IdlClassImpl::getTypeDescr() ); + } + return aRet; +} + +Sequence< Reference< XIdlField > > InterfaceIdlClassImpl::getFields() +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _pSortedMemberInit) + initMembers(); + + // create fields sequence + Sequence< Reference< XIdlField > > aRet( _nAttributes ); + Reference< XIdlField > * pRet = aRet.getArray(); + for ( sal_Int32 nPos = _nAttributes; nPos--; ) + { + /*_aName2Field[_pSortedMemberInit[_nMethods+nPos].first] = */pRet[_nAttributes-nPos-1] = + new IdlAttributeFieldImpl( + getReflection(), _pSortedMemberInit[_nMethods+nPos].first, + _pSortedMemberInit[_nMethods+nPos].second, IdlClassImpl::getTypeDescr() ); + } + return aRet; +} + +Reference< XIdlMethod > InterfaceIdlClassImpl::getMethod( const OUString & rName ) +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _pSortedMemberInit) + initMembers(); + + Reference< XIdlMethod > xRet; + + // try weak map + const OUString2Method::const_iterator iFind( _aName2Method.find( rName ) ); + if (iFind != _aName2Method.end()) + xRet = (*iFind).second; // harden ref + + if (! xRet.is()) + { + for ( sal_Int32 nPos = _nMethods; nPos--; ) + { + if (_pSortedMemberInit[nPos].first == rName) + { + _aName2Method[rName] = xRet = new IdlInterfaceMethodImpl( + getReflection(), rName, + _pSortedMemberInit[nPos].second, IdlClassImpl::getTypeDescr() ); + break; + } + } + } + return xRet; +} + +Reference< XIdlField > InterfaceIdlClassImpl::getField( const OUString & rName ) +{ + ::osl::MutexGuard aGuard( getMutexAccess() ); + if (! _pSortedMemberInit) + initMembers(); + + Reference< XIdlField > xRet; + + // try weak map + const OUString2Field::const_iterator iFind( _aName2Field.find( rName ) ); + if (iFind != _aName2Field.end()) + xRet = (*iFind).second; // harden ref + + if (! xRet.is()) + { + for ( sal_Int32 nPos = _nAttributes; nPos--; ) + { + if (_pSortedMemberInit[_nMethods+nPos].first == rName) + { + _aName2Field[rName] = xRet = new IdlAttributeFieldImpl( + getReflection(), rName, + _pSortedMemberInit[_nMethods+nPos].second, IdlClassImpl::getTypeDescr() ); + break; + } + } + } + return xRet; +} + +void InterfaceIdlClassImpl::createObject( Any & rObj ) +{ + // interfaces cannot be constructed + rObj.clear(); +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/lrucache.hxx b/stoc/source/corereflection/lrucache.hxx new file mode 100644 index 000000000..d5eebe804 --- /dev/null +++ b/stoc/source/corereflection/lrucache.hxx @@ -0,0 +1,209 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_STOC_SOURCE_COREREFLECTION_LRUCACHE_HXX +#define INCLUDED_STOC_SOURCE_COREREFLECTION_LRUCACHE_HXX + +// __CACHE_DIAGNOSE forces cache size to 4 and works only for OUString keys +// #define __CACHE_DIAGNOSE 1 + +#include <rtl/ustring.hxx> + +#include <memory> +#include <mutex> +#include <unordered_map> + +namespace com::sun::star::uno { class Any; } + +/** Implementation of a least recently used (lru) cache. + <br> +*/ +template< class t_Key, class t_Val, class t_KeyHash > +class LRU_Cache +{ + struct CacheEntry + { + t_Key aKey; + t_Val aVal; + CacheEntry * pPred; + CacheEntry * pSucc; + }; + typedef std::unordered_map< t_Key, CacheEntry *, t_KeyHash > t_Key2Element; + + mutable std::mutex _aCacheMutex; + sal_Int32 _nCachedElements; + t_Key2Element _aKey2Element; + + std::unique_ptr<CacheEntry[]> _pBlock; + mutable CacheEntry * _pHead; + mutable CacheEntry * _pTail; + inline void toFront( CacheEntry * pEntry ) const; + +public: + /** Constructor: + <br> + @param nCachedElements number of elements to be cached; default param set to 128 + */ + explicit inline LRU_Cache(); + + /** Retrieves a value from the cache. Returns default constructed value, + if none was found. + <br> + @param rKey a key + @return value + */ + inline t_Val getValue( const t_Key & rKey ) const; + /** Sets a value to be cached for given key. + <br> + @param rKey a key + @param rValue a value + */ + inline void setValue( const t_Key & rKey, const t_Val & rValue ); + /** Clears the cache, thus releasing all cached elements and keys. + <br> + */ + inline void clear(); +}; + +template< class t_Key, class t_Val, class t_KeyHash > +inline LRU_Cache< t_Key, t_Val, t_KeyHash >::LRU_Cache() +#ifdef __CACHE_DIAGNOSE + : _nCachedElements( 4 ) +#else + : _nCachedElements( 256 ) +#endif + , _pBlock( nullptr ) + , _pHead( nullptr ) + , _pTail( nullptr ) +{ + _pBlock.reset(new CacheEntry[_nCachedElements]); + _pHead = _pBlock.get(); + _pTail = _pBlock.get() + _nCachedElements - 1; + for (sal_Int32 nPos = _nCachedElements; nPos--;) + { + _pBlock[nPos].pPred = _pBlock.get() + nPos - 1; + _pBlock[nPos].pSucc = _pBlock.get() + nPos + 1; + } +} + +template< class t_Key, class t_Val, class t_KeyHash > +inline void LRU_Cache< t_Key, t_Val, t_KeyHash >::toFront( CacheEntry * pEntry ) const +{ + if (pEntry != _pHead) + { + // cut out element + if (pEntry == _pTail) + { + _pTail = pEntry->pPred; + } + else + { + pEntry->pSucc->pPred = pEntry->pPred; + pEntry->pPred->pSucc = pEntry->pSucc; + } + // push to front + _pHead->pPred = pEntry; + pEntry->pSucc = _pHead; + _pHead = pEntry; + } +} + +template< class t_Key, class t_Val, class t_KeyHash > +inline t_Val LRU_Cache< t_Key, t_Val, t_KeyHash >::getValue( const t_Key & rKey ) const +{ + std::scoped_lock aGuard( _aCacheMutex ); + const typename t_Key2Element::const_iterator iFind( _aKey2Element.find( rKey ) ); + if (iFind != _aKey2Element.end()) + { + CacheEntry * pEntry = (*iFind).second; + toFront( pEntry ); +#ifdef __CACHE_DIAGNOSE + SAL_INFO("stoc.corerefl", "> retrieved element \"" ); + SAL_INFO("stoc.corerefl", "" << pEntry->aKey); + SAL_INFO("stoc.corerefl", "\" from cache <" ); +#endif + return pEntry->aVal; + } + return t_Val(); +} + +template< class t_Key, class t_Val, class t_KeyHash > +inline void LRU_Cache< t_Key, t_Val, t_KeyHash >::setValue( + const t_Key & rKey, const t_Val & rValue ) +{ + std::scoped_lock aGuard( _aCacheMutex ); + if (_nCachedElements > 0) + { + const typename t_Key2Element::const_iterator iFind( _aKey2Element.find( rKey ) ); + + CacheEntry * pEntry; + if (iFind == _aKey2Element.end()) + { + pEntry = _pTail; // erase last element +#ifdef __CACHE_DIAGNOSE + if (pEntry->aKey.getLength()) + { + SAL_INFO("stoc.corerefl", "> kicking element \"" ); + SAL_INFO("stoc.corerefl", "" << pEntry->aKey); + SAL_INFO("stoc.corerefl", "\" from cache <" ); + } +#endif + _aKey2Element.erase( pEntry->aKey ); + pEntry->aKey = rKey; + _aKey2Element[ rKey ] = pEntry; + } + else + { + pEntry = (*iFind).second; +#ifdef __CACHE_DIAGNOSE + SAL_INFO("stoc.corerefl", "> replacing element \"" ); + SAL_INFO("stoc.corerefl", "" << pEntry->aKey); + SAL_INFO("stoc.corerefl", "\" in cache <" ); +#endif + } + pEntry->aVal = rValue; + toFront( pEntry ); + } +} + +template< class t_Key, class t_Val, class t_KeyHash > +inline void LRU_Cache< t_Key, t_Val, t_KeyHash >::clear() +{ + std::scoped_lock aGuard( _aCacheMutex ); + _aKey2Element.clear(); + for ( sal_Int32 nPos = _nCachedElements; nPos--; ) + { + _pBlock[nPos].aKey = t_Key(); + _pBlock[nPos].aVal = t_Val(); + } + _nCachedElements = 0; +#ifdef __CACHE_DIAGNOSE + SAL_INFO("stoc.corerefl", "> cleared cache <" ); +#endif +} + + +/** Template instance for OUString keys, Any values.<br> +*/ +typedef LRU_Cache< OUString, css::uno::Any, OUStringHash > + LRU_CacheAnyByOUString; + + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/corereflection/reflection.component b/stoc/source/corereflection/reflection.component new file mode 100644 index 000000000..70ccb68e0 --- /dev/null +++ b/stoc/source/corereflection/reflection.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.CoreReflection" + constructor="com_sun_star_comp_stoc_CoreReflection_get_implementation" + single-instance="true"> + <service name="com.sun.star.reflection.CoreReflection"/> + <singleton name="com.sun.star.reflection.theCoreReflection"/> + </implementation> +</component> diff --git a/stoc/source/defaultregistry/defaultregistry.cxx b/stoc/source/defaultregistry/defaultregistry.cxx new file mode 100644 index 000000000..866648439 --- /dev/null +++ b/stoc/source/defaultregistry/defaultregistry.cxx @@ -0,0 +1,1193 @@ +/* -*- 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 <osl/mutex.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/implbase4.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <rtl/ref.hxx> + +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace css::uno; +using namespace css::registry; +using namespace css::lang; +using namespace css::container; +using namespace cppu; +using namespace osl; + +namespace { + +class NestedRegistryImpl : public WeakAggImplHelper4 < XSimpleRegistry, XInitialization, XServiceInfo, XEnumerationAccess > +{ +public: + NestedRegistryImpl( ); + + // 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; + + // XSimpleRegistry + virtual OUString SAL_CALL getURL() override; + virtual void SAL_CALL open( const OUString& rURL, sal_Bool bReadOnly, sal_Bool bCreate ) override; + virtual sal_Bool SAL_CALL isValid( ) override; + virtual void SAL_CALL close( ) override; + virtual void SAL_CALL destroy( ) override; + virtual Reference< XRegistryKey > SAL_CALL getRootKey( ) override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual void SAL_CALL mergeKey( const OUString& aKeyName, const OUString& aUrl ) override; + + // XEnumerationAccess + virtual Reference< XEnumeration > SAL_CALL createEnumeration( ) override; + virtual Type SAL_CALL getElementType( ) override; + virtual sal_Bool SAL_CALL hasElements( ) override; + + friend class NestedKeyImpl; +protected: + Mutex m_mutex; + sal_uInt32 m_state; + Reference<XSimpleRegistry> m_localReg; + Reference<XSimpleRegistry> m_defaultReg; + +}; + + +// class NestedKeyImpl the implementation of interface XRegistryKey + +class NestedKeyImpl : public WeakImplHelper< XRegistryKey > +{ +public: + NestedKeyImpl( NestedRegistryImpl* pDefaultRegistry, + Reference<XRegistryKey>& localKey, + Reference<XRegistryKey>& defaultKey); + + NestedKeyImpl( const OUString& aKeyName, + NestedKeyImpl* pKey); + + // XRegistryKey + virtual OUString SAL_CALL getKeyName() override; + virtual sal_Bool SAL_CALL isReadOnly( ) override; + virtual sal_Bool SAL_CALL isValid( ) override; + virtual RegistryKeyType SAL_CALL getKeyType( const OUString& rKeyName ) override; + virtual RegistryValueType SAL_CALL getValueType( ) override; + virtual sal_Int32 SAL_CALL getLongValue( ) override; + virtual void SAL_CALL setLongValue( sal_Int32 value ) override; + virtual Sequence< sal_Int32 > SAL_CALL getLongListValue( ) override; + virtual void SAL_CALL setLongListValue( const css::uno::Sequence< sal_Int32 >& seqValue ) override; + virtual OUString SAL_CALL getAsciiValue( ) override; + virtual void SAL_CALL setAsciiValue( const OUString& value ) override; + virtual Sequence< OUString > SAL_CALL getAsciiListValue( ) override; + virtual void SAL_CALL setAsciiListValue( const css::uno::Sequence< OUString >& seqValue ) override; + virtual OUString SAL_CALL getStringValue( ) override; + virtual void SAL_CALL setStringValue( const OUString& value ) override; + virtual Sequence< OUString > SAL_CALL getStringListValue( ) override; + virtual void SAL_CALL setStringListValue( const css::uno::Sequence< OUString >& seqValue ) override; + virtual Sequence< sal_Int8 > SAL_CALL getBinaryValue( ) override; + virtual void SAL_CALL setBinaryValue( const css::uno::Sequence< sal_Int8 >& value ) override; + virtual Reference< XRegistryKey > SAL_CALL openKey( const OUString& aKeyName ) override; + virtual Reference< XRegistryKey > SAL_CALL createKey( const OUString& aKeyName ) override; + virtual void SAL_CALL closeKey( ) override; + virtual void SAL_CALL deleteKey( const OUString& rKeyName ) override; + virtual Sequence< Reference< XRegistryKey > > SAL_CALL openKeys( ) override; + virtual Sequence< OUString > SAL_CALL getKeyNames( ) override; + virtual sal_Bool SAL_CALL createLink( const OUString& aLinkName, const OUString& aLinkTarget ) override; + virtual void SAL_CALL deleteLink( const OUString& rLinkName ) override; + virtual OUString SAL_CALL getLinkTarget( const OUString& rLinkName ) override; + virtual OUString SAL_CALL getResolvedName( const OUString& aKeyName ) override; + +protected: + void computeChanges(); + OUString computeName(const OUString& name); + + OUString m_name; + sal_uInt32 m_state; + rtl::Reference<NestedRegistryImpl> m_xRegistry; + Reference<XRegistryKey> m_localKey; + Reference<XRegistryKey> m_defaultKey; +}; + + +NestedKeyImpl::NestedKeyImpl( NestedRegistryImpl* pDefaultRegistry, + Reference<XRegistryKey>& localKey, + Reference<XRegistryKey>& defaultKey ) + : m_state(pDefaultRegistry->m_state), m_xRegistry(pDefaultRegistry), m_localKey(localKey), m_defaultKey(defaultKey) +{ + if (m_localKey.is()) + { + m_name = m_localKey->getKeyName(); + } + else if (m_defaultKey.is()) + { + m_name = m_defaultKey->getKeyName(); + } +} + + +NestedKeyImpl::NestedKeyImpl( const OUString& rKeyName, + NestedKeyImpl* pKey) + : m_xRegistry(pKey->m_xRegistry) +{ + if (pKey->m_localKey.is() && pKey->m_localKey->isValid()) + { + m_localKey = pKey->m_localKey->openKey(rKeyName); + } + if (pKey->m_defaultKey.is() && pKey->m_defaultKey->isValid()) + { + m_defaultKey = pKey->m_defaultKey->openKey(rKeyName); + } + + if (m_localKey.is()) + { + m_name = m_localKey->getKeyName(); + } + else if (m_defaultKey.is()) + { + m_name = m_defaultKey->getKeyName(); + } + + m_state = m_xRegistry->m_state; +} + +void NestedKeyImpl::computeChanges() +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( m_state == m_xRegistry->m_state ) + return; + + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + + Reference<XRegistryKey> tmpKey = rootKey->openKey(m_name); + + if ( tmpKey.is() ) + { + m_localKey = rootKey->openKey(m_name); + } + + m_state = m_xRegistry->m_state; +} + + +// NestedKey_Impl::computeName() + +OUString NestedKeyImpl::computeName(const OUString& name) +{ + OUString resLocalName, resDefaultName; + + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + try + { + if ( m_localKey.is() && m_localKey->isValid() ) + { + resLocalName = m_localKey->getResolvedName(name); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getResolvedName(name); + } + + if ( !resLocalName.isEmpty() && m_xRegistry->m_defaultReg->isValid() ) + { + Reference<XRegistryKey> localRoot(m_xRegistry->m_localReg->getRootKey()); + Reference<XRegistryKey> defaultRoot(m_xRegistry->m_defaultReg->getRootKey()); + + resDefaultName = defaultRoot->getResolvedName(resLocalName); + + sal_uInt32 count = 100; + + while (resLocalName != resDefaultName && count > 0) + { + count--; + + if (resLocalName.isEmpty() || resDefaultName.isEmpty()) + throw InvalidRegistryException(); + + resLocalName = localRoot->getResolvedName(resDefaultName); + resDefaultName = defaultRoot->getResolvedName(resLocalName); + } + } + } + catch(InvalidRegistryException& ) + { + } + + return resLocalName; +} + + +OUString SAL_CALL NestedKeyImpl::getKeyName() +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + return m_name; +} + + +sal_Bool SAL_CALL NestedKeyImpl::isReadOnly( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( !m_localKey.is() || !m_localKey->isValid() ) + throw InvalidRegistryException(); + + return m_localKey->isReadOnly(); +} + + +sal_Bool SAL_CALL NestedKeyImpl::isValid( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + return ((m_localKey.is() && m_localKey->isValid()) || + (m_defaultKey.is() && m_defaultKey->isValid()) ); +} + + +RegistryKeyType SAL_CALL NestedKeyImpl::getKeyType( const OUString& rKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getKeyType(rKeyName); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getKeyType(rKeyName); + } + + return RegistryKeyType_KEY; +} + + +RegistryValueType SAL_CALL NestedKeyImpl::getValueType( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getValueType(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getValueType(); + } + + return RegistryValueType_NOT_DEFINED; +} + + +sal_Int32 SAL_CALL NestedKeyImpl::getLongValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getLongValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getLongValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setLongValue( sal_Int32 value ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setLongValue(value); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setLongValue(value); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Sequence< sal_Int32 > SAL_CALL NestedKeyImpl::getLongListValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getLongListValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getLongListValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setLongListValue( const Sequence< sal_Int32 >& seqValue ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setLongListValue(seqValue); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setLongListValue(seqValue); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +OUString SAL_CALL NestedKeyImpl::getAsciiValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getAsciiValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getAsciiValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setAsciiValue( const OUString& value ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setAsciiValue(value); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setAsciiValue(value); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Sequence< OUString > SAL_CALL NestedKeyImpl::getAsciiListValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getAsciiListValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getAsciiListValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setAsciiListValue( const Sequence< OUString >& seqValue ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setAsciiListValue(seqValue); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setAsciiListValue(seqValue); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +OUString SAL_CALL NestedKeyImpl::getStringValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getStringValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getStringValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setStringValue( const OUString& value ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setStringValue(value); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setStringValue(value); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Sequence< OUString > SAL_CALL NestedKeyImpl::getStringListValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getStringListValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getStringListValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setStringListValue( const Sequence< OUString >& seqValue ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setStringListValue(seqValue); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setStringListValue(seqValue); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Sequence< sal_Int8 > SAL_CALL NestedKeyImpl::getBinaryValue( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + return m_localKey->getBinaryValue(); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + return m_defaultKey->getBinaryValue(); + } + else + { + throw InvalidRegistryException(); + } +} + + +void SAL_CALL NestedKeyImpl::setBinaryValue( const Sequence< sal_Int8 >& value ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + computeChanges(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->setBinaryValue(value); + } + else if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + m_localKey->setBinaryValue(value); + m_state = m_xRegistry->m_state++; + } + else + { + throw InvalidRegistryException(); + } +} + + +Reference< XRegistryKey > SAL_CALL NestedKeyImpl::openKey( const OUString& aKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName = computeName(aKeyName); + + if ( resolvedName.isEmpty() ) + throw InvalidRegistryException(); + + Reference<XRegistryKey> localKey, defaultKey; + + if ( m_localKey.is() && m_localKey->isValid() ) + { + localKey = m_xRegistry->m_localReg->getRootKey()->openKey(resolvedName); + } + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + defaultKey = m_xRegistry->m_defaultReg->getRootKey()->openKey(resolvedName); + } + + if ( localKey.is() || defaultKey.is() ) + { + return new NestedKeyImpl(m_xRegistry.get(), localKey, defaultKey); + } + else + { + return Reference<XRegistryKey>(); + } +} + + +Reference< XRegistryKey > SAL_CALL NestedKeyImpl::createKey( const OUString& aKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( (!m_localKey.is() && !m_defaultKey.is()) || + (m_localKey.is() && m_localKey->isReadOnly()) ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName = computeName(aKeyName); + + if ( resolvedName.isEmpty() ) + throw InvalidRegistryException(); + + if ( m_localKey.is() && m_localKey->isValid() ) + { + Reference<XRegistryKey> localKey, defaultKey; + + localKey = m_xRegistry->m_localReg->getRootKey()->createKey(resolvedName); + if ( localKey.is() ) + { + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + defaultKey = m_xRegistry->m_defaultReg->getRootKey()->openKey(resolvedName); + } + + m_state = m_xRegistry->m_state++; + + return new NestedKeyImpl(m_xRegistry.get(), localKey, defaultKey); + } + } + else + { + Reference<XRegistryKey> localKey, defaultKey; + + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + + localKey = m_xRegistry->m_localReg->getRootKey()->createKey(resolvedName); + + if ( localKey.is() ) + { + defaultKey = m_xRegistry->m_defaultReg->getRootKey()->openKey(resolvedName); + + m_state = m_xRegistry->m_state++; + + return new NestedKeyImpl(m_xRegistry.get(), localKey, defaultKey); + } + } + } + + return Reference<XRegistryKey>(); +} + + +void SAL_CALL NestedKeyImpl::closeKey( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( m_localKey.is() && m_localKey->isValid() ) + { + m_localKey->closeKey(); + } + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + m_defaultKey->closeKey(); + } +} + + +void SAL_CALL NestedKeyImpl::deleteKey( const OUString& rKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() || !m_localKey->isValid() || + m_localKey->isReadOnly() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName = computeName(rKeyName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + m_xRegistry->m_localReg->getRootKey()->deleteKey(resolvedName); +} + + +Sequence< Reference< XRegistryKey > > SAL_CALL NestedKeyImpl::openKeys( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + Sequence<OUString> localSeq, defaultSeq; + + if ( m_localKey.is() && m_localKey->isValid() ) + { + localSeq = m_localKey->getKeyNames(); + } + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + defaultSeq = m_defaultKey->getKeyNames(); + } + + std::vector< Reference<XRegistryKey> > retVec; + retVec.reserve(localSeq.getLength() + defaultSeq.getLength()); + + auto lKeyNameToRegKey = [this](const OUString& rName) -> Reference<XRegistryKey> { + sal_Int32 lastIndex = rName.lastIndexOf('/'); + OUString name = rName.copy(lastIndex); + return new NestedKeyImpl(name, this); + }; + + for (const auto& rKeyName : std::as_const(localSeq)) + retVec.push_back(lKeyNameToRegKey(rKeyName)); + + for (const auto& rKeyName : std::as_const(defaultSeq)) + { + if ( comphelper::findValue(localSeq, rKeyName) == -1 ) + { + retVec.push_back(lKeyNameToRegKey(rKeyName)); + } + } + + return comphelper::containerToSequence(retVec); +} + + +Sequence< OUString > SAL_CALL NestedKeyImpl::getKeyNames( ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + Sequence<OUString> localSeq, defaultSeq; + + if ( m_localKey.is() && m_localKey->isValid() ) + { + localSeq = m_localKey->getKeyNames(); + } + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + defaultSeq = m_defaultKey->getKeyNames(); + } + + return comphelper::combineSequences(localSeq, defaultSeq); +} + + +sal_Bool SAL_CALL NestedKeyImpl::createLink( const OUString& aLinkName, const OUString& aLinkTarget ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + + bool isCreated = false; + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName; + sal_Int32 lastIndex = aLinkName.lastIndexOf('/'); + + if ( lastIndex > 0 ) + { + OUString linkName = aLinkName.copy(0, lastIndex); + + resolvedName = computeName(linkName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + resolvedName += aLinkName.subView(lastIndex); + } + else + { + if ( lastIndex == 0 ) + resolvedName = m_name + aLinkName; + else + resolvedName = m_name + "/" + aLinkName; + } + + if ( m_localKey.is() && m_localKey->isValid() ) + { + isCreated = m_xRegistry->m_localReg->getRootKey()->createLink(resolvedName, aLinkTarget); + } + else + { + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + { + Reference<XRegistryKey> rootKey(m_xRegistry->m_localReg->getRootKey()); + m_localKey = rootKey->createKey(m_name); + + isCreated = m_xRegistry->m_localReg->getRootKey()->createLink(resolvedName, aLinkTarget); + } + } + + if ( isCreated ) + m_state = m_xRegistry->m_state++; + + return isCreated; +} + + +void SAL_CALL NestedKeyImpl::deleteLink( const OUString& rLinkName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName; + sal_Int32 lastIndex = rLinkName.lastIndexOf('/'); + + if ( lastIndex > 0 ) + { + OUString linkName = rLinkName.copy(0, lastIndex); + + resolvedName = computeName(linkName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + resolvedName += rLinkName.subView(lastIndex); + } + else + { + if ( lastIndex == 0 ) + resolvedName = m_name + rLinkName; + else + resolvedName = m_name + "/" + rLinkName; + } + + if ( !m_localKey.is() || !m_localKey->isValid() || + m_localKey->isReadOnly() ) + { + throw InvalidRegistryException(); + } + + m_xRegistry->m_localReg->getRootKey()->deleteLink(resolvedName); +} + + +OUString SAL_CALL NestedKeyImpl::getLinkTarget( const OUString& rLinkName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName; + sal_Int32 lastIndex = rLinkName.lastIndexOf('/'); + + if ( lastIndex > 0 ) + { + OUString linkName = rLinkName.copy(0, lastIndex); + + resolvedName = computeName(linkName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + resolvedName += rLinkName.subView(lastIndex); + } + else + { + if ( lastIndex == 0 ) + resolvedName = m_name + rLinkName; + else + resolvedName = m_name + "/" + rLinkName; + } + + OUString linkTarget; + if ( m_localKey.is() && m_localKey->isValid() ) + { + try + { + linkTarget = m_xRegistry->m_localReg->getRootKey()->getLinkTarget(resolvedName); + return linkTarget; + } + catch(InvalidRegistryException& ) + { + } + } + + if ( m_defaultKey.is() && m_defaultKey->isValid() ) + linkTarget = m_xRegistry->m_defaultReg->getRootKey()->getLinkTarget(resolvedName); + + return linkTarget; +} + + +OUString SAL_CALL NestedKeyImpl::getResolvedName( const OUString& aKeyName ) +{ + Guard< Mutex > aGuard( m_xRegistry->m_mutex ); + if ( !m_localKey.is() && !m_defaultKey.is() ) + { + throw InvalidRegistryException(); + } + + OUString resolvedName = computeName(aKeyName); + + if ( resolvedName.isEmpty() ) + { + throw InvalidRegistryException(); + } + + return resolvedName; +} + + +// DefaultRegistry Implementation + + +NestedRegistryImpl::NestedRegistryImpl( ) + : m_state(0) +{} + +class RegistryEnumueration : public WeakImplHelper< XEnumeration > +{ +public: + RegistryEnumueration( + const Reference< XSimpleRegistry > &r1, + const Reference< XSimpleRegistry > &r2 ) + : m_xReg1( r1 ) , m_xReg2( r2 ) + {} +public: + virtual sal_Bool SAL_CALL hasMoreElements( ) override; + virtual Any SAL_CALL nextElement( ) override; + +private: + Reference< XSimpleRegistry > m_xReg1; + Reference< XSimpleRegistry > m_xReg2; +}; + +sal_Bool RegistryEnumueration::hasMoreElements( ) +{ + return m_xReg1.is() || m_xReg2.is(); +} + +Any RegistryEnumueration::nextElement( ) +{ + Any a; + if( m_xReg1.is() ) + { + a <<= m_xReg1; + m_xReg1.clear(); + } + else if( m_xReg2.is() ) + { + a <<= m_xReg2; + m_xReg2.clear(); + } + else + { + throw NoSuchElementException( "NestedRegistry: no nextElement() !" ); + } + return a; +} + + +Reference< XEnumeration > NestedRegistryImpl::createEnumeration( ) +{ + MutexGuard guard( m_mutex ); + return new RegistryEnumueration( m_localReg, m_defaultReg ); +} + +Type NestedRegistryImpl::getElementType( ) +{ + return cppu::UnoType<decltype(m_localReg)>::get(); +} + +sal_Bool SAL_CALL NestedRegistryImpl::hasElements( ) +{ + MutexGuard guard( m_mutex ); + return m_localReg.is() || m_defaultReg.is(); +} + + +OUString SAL_CALL NestedRegistryImpl::getImplementationName( ) +{ + return "com.sun.star.comp.stoc.NestedRegistry"; +} + +sal_Bool SAL_CALL NestedRegistryImpl::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL NestedRegistryImpl::getSupportedServiceNames( ) +{ + Sequence< OUString > seqNames { "com.sun.star.registry.NestedRegistry" }; + return seqNames; +} + + +void SAL_CALL NestedRegistryImpl::initialize( const Sequence< Any >& aArguments ) +{ + Guard< Mutex > aGuard( m_mutex ); + if ( (aArguments.getLength() == 2) && + (aArguments[0].getValueType().getTypeClass() == TypeClass_INTERFACE) && + (aArguments[1].getValueType().getTypeClass() == TypeClass_INTERFACE) ) + { + aArguments[0] >>= m_localReg; + aArguments[1] >>= m_defaultReg; + if ( m_localReg == m_defaultReg ) + m_defaultReg.clear(); + } +} + + +OUString SAL_CALL NestedRegistryImpl::getURL() +{ + Guard< Mutex > aGuard( m_mutex ); + try + { + if ( m_localReg.is() && m_localReg->isValid() ) + return m_localReg->getURL(); + } + catch(InvalidRegistryException& ) + { + } + + return OUString(); +} + + +void SAL_CALL NestedRegistryImpl::open( const OUString&, sal_Bool, sal_Bool ) +{ + throw InvalidRegistryException( + "the 'open' method is not specified for a nested registry" ); +} + + +sal_Bool SAL_CALL NestedRegistryImpl::isValid( ) +{ + Guard< Mutex > aGuard( m_mutex ); + try + { + if ( (m_localReg.is() && m_localReg->isValid()) || + (m_defaultReg.is() && m_defaultReg->isValid()) ) + return true; + } + catch(InvalidRegistryException& ) + { + } + + return false; +} + + +void SAL_CALL NestedRegistryImpl::close( ) +{ + Guard< Mutex > aGuard( m_mutex ); + if ( m_localReg.is() && m_localReg->isValid() ) + { + m_localReg->close(); + } + if ( m_defaultReg.is() && m_defaultReg->isValid() ) + { + m_defaultReg->close(); + } +} + + +void SAL_CALL NestedRegistryImpl::destroy( ) +{ + throw InvalidRegistryException( + "the 'destroy' method is not specified for a nested registry" ); +} + + +Reference< XRegistryKey > SAL_CALL NestedRegistryImpl::getRootKey( ) +{ + Guard< Mutex > aGuard( m_mutex ); + if ( !m_localReg.is() || !m_localReg->isValid() ) + { + throw InvalidRegistryException(); + } + + Reference<XRegistryKey> localKey, defaultKey; + + localKey = m_localReg->getRootKey(); + + if ( localKey.is() ) + { + if ( m_defaultReg.is() && m_defaultReg->isValid() ) + { + defaultKey = m_defaultReg->getRootKey(); + } + + return new NestedKeyImpl(this, localKey, defaultKey); + } + + return Reference<XRegistryKey>(); +} + + +sal_Bool SAL_CALL NestedRegistryImpl::isReadOnly( ) +{ + Guard< Mutex > aGuard( m_mutex ); + try + { + if ( m_localReg.is() && m_localReg->isValid() ) + return m_localReg->isReadOnly(); + } + catch(InvalidRegistryException& ) + { + } + + return false; +} + + +void SAL_CALL NestedRegistryImpl::mergeKey( const OUString& aKeyName, const OUString& aUrl ) +{ + Guard< Mutex > aGuard( m_mutex ); + if ( m_localReg.is() && m_localReg->isValid() ) + { + m_localReg->mergeKey(aKeyName, aUrl); + + m_state++; + } +} + +} // namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_NestedRegistry_get_implementation( + SAL_UNUSED_PARAMETER css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new NestedRegistryImpl); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/implementationregistration/implreg.cxx b/stoc/source/implementationregistration/implreg.cxx new file mode 100644 index 000000000..7d697e41f --- /dev/null +++ b/stoc/source/implementationregistration/implreg.cxx @@ -0,0 +1,1580 @@ +/* -*- 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 <string.h> +#include <string_view> +#include <vector> + +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> +#include <rtl/ustring.hxx> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/loader/XImplementationLoader.hpp> +#include <com/sun/star/registry/XImplementationRegistration2.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/reflection/XServiceTypeDescription.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include "mergekeys.hxx" + +#if defined(_WIN32) +#include <io.h> +#endif + + +using namespace com::sun::star; +using namespace css::uno; +using namespace css::loader; +using namespace css::beans; +using namespace css::lang; +using namespace css::registry; +using namespace cppu; +using namespace osl; + +namespace { + +constexpr OUStringLiteral slash_UNO_slash_REGISTRY_LINKS + = u"/UNO/REGISTRY_LINKS"; +constexpr OUStringLiteral slash_IMPLEMENTATIONS + = u"/IMPLEMENTATIONS"; +constexpr OUStringLiteral slash_UNO + = u"/UNO"; +constexpr OUStringLiteral slash_UNO_slash_SERVICES + = u"/UNO/SERVICES"; +constexpr OUStringLiteral slash_UNO_slash_SINGLETONS + = u"/UNO/SINGLETONS"; +constexpr OUStringLiteral slash_SERVICES + = u"/SERVICES/"; +constexpr OUStringLiteral slash_UNO_slash_LOCATION + = u"/UNO/LOCATION"; +constexpr OUStringLiteral slash_UNO_slash_ACTIVATOR + = u"/UNO/ACTIVATOR"; +constexpr OUStringLiteral colon_old + = u":old"; +constexpr OUStringLiteral com_sun_star_registry_SimpleRegistry + = u"com.sun.star.registry.SimpleRegistry"; +constexpr OUStringLiteral Registry + = u"Registry"; + +// static deleteAllLinkReferences() + +void deleteAllLinkReferences(const Reference < XSimpleRegistry >& xReg, + const Reference < XRegistryKey >& xSource) + // throw ( InvalidRegistryException, RuntimeException ) +{ + Reference < XRegistryKey > xKey = xSource->openKey( + slash_UNO_slash_REGISTRY_LINKS ); + + if (!(xKey.is() && (xKey->getValueType() == RegistryValueType_ASCIILIST))) + return; + + const Sequence<OUString> linkNames = xKey->getAsciiListValue(); + + if (!linkNames.hasElements()) + return; + + OUString aLinkName; + OUString aLinkParent; + Reference < XRegistryKey > xLinkParent; + const sal_Unicode* pTmpName = nullptr; + const sal_Unicode* pShortName = nullptr; + sal_Int32 sEnd = 0; + + for (const OUString& rLinkName : linkNames) + { + aLinkName = rLinkName; + + pTmpName = aLinkName.getStr(); + + if (pTmpName[0] != L'/') + continue; + + sal_Int32 nIndex = rtl_ustr_indexOfChar( pTmpName, '%' ); + if ( nIndex == -1 ) + pShortName = nullptr; + else + pShortName = pTmpName+nIndex; + + while (pShortName && pShortName[1] == L'%') + { + nIndex = rtl_ustr_indexOfChar( pShortName+2, '%' ); + if ( nIndex == -1 ) + pShortName = nullptr; + else + pShortName += nIndex+2; + } + + if (pShortName) + { + aLinkName = aLinkName.copy(0, pShortName - pTmpName); + } + + xReg->getRootKey()->deleteLink(aLinkName); + + sEnd = aLinkName.lastIndexOf( '/' ); + + aLinkParent = aLinkName.copy(0, sEnd); + + while(!aLinkParent.isEmpty()) + { + xLinkParent = xReg->getRootKey()->openKey(aLinkParent); + + if (xLinkParent.is() && !xLinkParent->getKeyNames().hasElements()) + { + aLinkName = aLinkParent; + + xReg->getRootKey()->deleteKey(aLinkParent); + + sEnd = aLinkName.lastIndexOf( '/' ); + + aLinkParent = aLinkName.copy(0, sEnd); + } + else + { + break; + } + } + } +} + + +// static prepareLink + +void prepareLink( const Reference < XSimpleRegistry > & xDest, + const Reference < XRegistryKey > & xSource, + const OUString& link) + // throw ( InvalidRegistryException, RuntimeException ) +{ + OUString linkRefName = xSource->getKeyName(); + OUString linkName(link); + bool isRelativ = false; + + const sal_Unicode* pTmpName = link.getStr(); + const sal_Unicode* pShortName; + sal_Int32 nIndex = rtl_ustr_indexOfChar( pTmpName, '%' ); + if ( nIndex == -1 ) + pShortName = nullptr; + else + pShortName = pTmpName+nIndex; + + if (pTmpName[0] != L'/') + isRelativ = true; + + while (pShortName && pShortName[1] == L'%') + { + nIndex = rtl_ustr_indexOfChar( pShortName+2, '%' ); + if ( nIndex == -1 ) + pShortName = nullptr; + else + pShortName += nIndex+2; + } + + if (pShortName) + { + linkRefName += link.subView(pShortName - pTmpName + 1); + linkName = link.copy(0, pShortName - pTmpName); + } + + if (isRelativ) + xSource->createLink(linkName, linkRefName); + else + xDest->getRootKey()->createLink(linkName, linkRefName); +} + + +// static searchImplForLink + +OUString searchImplForLink( + const Reference < XRegistryKey > & xRootKey, + std::u16string_view linkName, + std::u16string_view implName ) + // throw ( InvalidRegistryException, RuntimeException ) +{ + Reference < XRegistryKey > xKey = xRootKey->openKey( slash_IMPLEMENTATIONS ); + if (xKey.is()) + { + const Sequence< Reference < XRegistryKey > > subKeys( xKey->openKeys() ); + OUString key_name( slash_UNO + linkName ); + + for (const Reference < XRegistryKey >& xImplKey : subKeys) + { + try + { + if (xImplKey->getKeyType( key_name ) == RegistryKeyType_LINK) + { + OUString oldImplName = xImplKey->getKeyName().copy(strlen("/IMPLEMENTATIONS/")); + if (implName != oldImplName) + { + return oldImplName; + } + } + } + catch(InvalidRegistryException&) + { + } + } + } + + return OUString(); +} + + +// static searchLinkTargetForImpl + +OUString searchLinkTargetForImpl(const Reference < XRegistryKey >& xRootKey, + std::u16string_view linkName, + const OUString& implName) +{ + Reference < XRegistryKey > xKey = xRootKey->openKey( slash_IMPLEMENTATIONS ); + + if (xKey.is()) + { + const Sequence< Reference < XRegistryKey > > subKeys = xKey->openKeys(); + + OUString qualifiedLinkName( slash_UNO + linkName ); + + auto pSubKey = std::find_if(subKeys.begin(), subKeys.end(), + [&implName, &qualifiedLinkName](const Reference<XRegistryKey>& rSubKey) { + OUString tmpImplName = rSubKey->getKeyName().copy(strlen("/IMPLEMENTATIONS/")); + return tmpImplName == implName + && rSubKey->getKeyType( qualifiedLinkName ) == RegistryKeyType_LINK; + }); + if (pSubKey != subKeys.end()) + return (*pSubKey)->getLinkTarget( qualifiedLinkName ); + } + + return OUString(); +} + + +// static createUniqueSubEntry + +void createUniqueSubEntry(const Reference < XRegistryKey > & xSuperKey, + const OUString& value) + // throw ( InvalidRegistryException, RuntimeException ) +{ + if (!xSuperKey.is()) + return; + + if (xSuperKey->getValueType() == RegistryValueType_ASCIILIST) + { + const Sequence<OUString> implEntries = xSuperKey->getAsciiListValue(); + sal_Int32 length = implEntries.getLength(); + + bool bReady = comphelper::findValue(implEntries, value) != -1; + + if (bReady) + { + Sequence<OUString> implEntriesNew(length); + auto it = implEntriesNew.getArray(); + *it = value; + + std::copy_if(implEntries.begin(), implEntries.end(), std::next(it), + [&value](const OUString& rEntry) { return rEntry != value; }); + xSuperKey->setAsciiListValue(implEntriesNew); + } else + { + Sequence<OUString> implEntriesNew(length+1); + auto it = implEntriesNew.getArray(); + *it = value; + + std::copy(implEntries.begin(), implEntries.end(), std::next(it)); + xSuperKey->setAsciiListValue(implEntriesNew); + } + } else + { + Sequence<OUString> implEntriesNew { value }; + + xSuperKey->setAsciiListValue(implEntriesNew); + } +} + + +// static deleteSubEntry + +bool deleteSubEntry(const Reference < XRegistryKey >& xSuperKey, const OUString& value) + // throw ( InvalidRegistryException, RuntimeException ) +{ + if (xSuperKey->getValueType() == RegistryValueType_ASCIILIST) + { + const Sequence<OUString> implEntries = xSuperKey->getAsciiListValue(); + sal_Int32 length = implEntries.getLength(); + sal_Int32 equals = static_cast<sal_Int32>(std::count(implEntries.begin(), implEntries.end(), value)); + bool hasNoImplementations = false; + + if (equals == length) + { + hasNoImplementations = true; + } else + { + Sequence<OUString> implEntriesNew(length - equals); + + std::copy_if(implEntries.begin(), implEntries.end(), implEntriesNew.getArray(), + [&value](const OUString& rEntry) { return rEntry != value; }); + xSuperKey->setAsciiListValue(implEntriesNew); + } + + if (hasNoImplementations) + { + return true; + } + } + return false; +} + + +// static prepareUserLink + +void prepareUserLink(const Reference < XSimpleRegistry >& xDest, + const OUString& linkName, + const OUString& linkTarget, + std::u16string_view implName) +{ + Reference < XRegistryKey > xRootKey = xDest->getRootKey(); + + if (xRootKey->getKeyType(linkName) == RegistryKeyType_LINK) + { + OUString oldImplName(searchImplForLink(xRootKey, linkName, implName)); + + if (!oldImplName.isEmpty()) + { + createUniqueSubEntry(xDest->getRootKey()->createKey( + linkName + colon_old ), oldImplName); + } + } + + if (xRootKey->isValid()) + xRootKey->createLink(linkName, linkTarget); +} + + +// static deleteUserLink + +void deletePathIfPossible(const Reference < XRegistryKey >& xRootKey, + const OUString& path) +{ + try + { + Sequence<OUString> keyNames(xRootKey->openKey(path)->getKeyNames()); + + if (!keyNames.hasElements() && + xRootKey->openKey(path)->getValueType() == RegistryValueType_NOT_DEFINED) + { + xRootKey->deleteKey(path); + + OUString newPath = path.copy(0, path.lastIndexOf('/')); + + if (newPath.getLength() > 1) + deletePathIfPossible(xRootKey, newPath); + } + } + catch(InvalidRegistryException&) + { + } +} + + +// static deleteUserLink + +void deleteUserLink(const Reference < XRegistryKey >& xRootKey, + const OUString& linkName, + std::u16string_view linkTarget, + const OUString& implName) + // throw ( InvalidRegistryException, RuntimeException ) +{ + bool bClean = false; + + if (xRootKey->getKeyType(linkName) == RegistryKeyType_LINK) + { + OUString tmpTarget = xRootKey->getLinkTarget(linkName); + + if (tmpTarget == linkTarget) + { + xRootKey->deleteLink(linkName); + } + } + + Reference < XRegistryKey > xOldKey = xRootKey->openKey( + linkName + colon_old ); + if (xOldKey.is()) + { + if (xOldKey->getValueType() == RegistryValueType_ASCIILIST) + { + const Sequence<OUString> implEntries = xOldKey->getAsciiListValue(); + sal_Int32 length = implEntries.getLength(); + sal_Int32 equals = static_cast<sal_Int32>(std::count(implEntries.begin(), implEntries.end(), implName)); + bool hasNoImplementations = false; + + if (equals == length) + { + hasNoImplementations = true; + } else + { + OUString oldImpl; + + if (length > equals + 1) + { + Sequence<OUString> implEntriesNew(length - equals - 1); + auto pNewArray = implEntriesNew.getArray(); + + sal_Int32 j = 0; + bool first = true; + for (sal_Int32 i = 0; i < length; i++) + { + if (implEntries[i] != implName) + { + if (first) + { + oldImpl = implEntries[i]; + first = false; + } else + { + pNewArray[j++] = implEntries[i]; + } + } + } + + xOldKey->setAsciiListValue(implEntriesNew); + } else + { + oldImpl = implEntries[0]; + OUString path(xOldKey->getKeyName()); + xOldKey->closeKey(); + xRootKey->deleteKey(path); + } + + OUString oldTarget = searchLinkTargetForImpl(xRootKey, linkName, oldImpl); + if (!oldTarget.isEmpty()) + { + xRootKey->createLink(linkName, oldTarget); + } + } + + if (hasNoImplementations) + { + bClean = true; + OUString path(xOldKey->getKeyName()); + xOldKey->closeKey(); + xRootKey->deleteKey(path); + } + } + } else + { + bClean = true; + } + + if (bClean) + { + OUString path = linkName.copy(0, linkName.lastIndexOf('/')); + deletePathIfPossible(xRootKey, path); + } +} + + +// static prepareUserKeys + +void prepareUserKeys(const Reference < XSimpleRegistry >& xDest, + const Reference < XRegistryKey >& xUnoKey, + const Reference < XRegistryKey >& xKey, + const OUString& implName, + bool bRegister) +{ + bool hasSubKeys = false; + + Sequence<OUString> keyNames = xKey->getKeyNames(); + + OUString relativKey; + if (keyNames.hasElements()) + relativKey = keyNames.getConstArray()[0].copy(xKey->getKeyName().getLength()+1); + + if (keyNames.getLength() == 1 && + xKey->getKeyType(relativKey) == RegistryKeyType_LINK) + { + hasSubKeys = true; + + OUString linkTarget = xKey->getLinkTarget(relativKey); + OUString linkName(xKey->getKeyName().copy(xUnoKey->getKeyName().getLength())); + + linkName += "/" + relativKey; + + if (bRegister) + { + prepareUserLink(xDest, linkName, linkTarget, implName); + } else + { + deleteUserLink(xDest->getRootKey(), linkName, linkTarget, implName); + } + } else + { + const Sequence< Reference < XRegistryKey> > subKeys = xKey->openKeys(); + + if (subKeys.hasElements()) + { + hasSubKeys = true; + + for (const Reference < XRegistryKey > & rSubKey : subKeys) + { + prepareUserKeys(xDest, xUnoKey, rSubKey, implName, bRegister); + } + } + } + + if (hasSubKeys) + return; + + OUString keyName(xKey->getKeyName().copy(xUnoKey->getKeyName().getLength())); + + Reference < XRegistryKey > xRootKey = xDest->getRootKey(); + if (bRegister) + { + createUniqueSubEntry(xRootKey->createKey(keyName), implName); + } + else + { + Reference< XRegistryKey > rKey = xRootKey->openKey(keyName); + if( rKey.is() ) + { + deleteSubEntry(rKey, implName); + xRootKey->deleteKey(keyName); + } + + OUString path = keyName.copy(0, keyName.lastIndexOf('/')); + if( !path.isEmpty() ) + { + deletePathIfPossible(xRootKey, path); + } + } +} + + +// static deleteAllImplementations + +void deleteAllImplementations( const Reference < XSimpleRegistry >& xReg, + const Reference < XRegistryKey >& xSource, + std::u16string_view locationUrl, + std::vector<OUString> & implNames) + // throw (InvalidRegistryException, RuntimeException) +{ + Sequence < Reference < XRegistryKey > > subKeys = xSource->openKeys(); + + if (subKeys.hasElements()) + { + bool hasLocationUrl = false; + + for (const Reference < XRegistryKey> & xImplKey : std::as_const(subKeys)) + { + Reference < XRegistryKey > xKey = xImplKey->openKey( + slash_UNO_slash_LOCATION ); + + if (xKey.is() && (xKey->getValueType() == RegistryValueType_ASCII)) + { + if (xKey->getAsciiValue() == locationUrl) + { + hasLocationUrl = true; + + OUString implName(xImplKey->getKeyName().copy(1)); + sal_Int32 firstDot = implName.indexOf('/'); + + if (firstDot >= 0) + implName = implName.copy(firstDot + 1); + + implNames.push_back(implName); + + deleteAllLinkReferences(xReg, xImplKey); + + xKey = xImplKey->openKey( slash_UNO ); + if (xKey.is()) + { + const Sequence< Reference < XRegistryKey > > subKeys2 = xKey->openKeys(); + + for (const Reference < XRegistryKey > & rSubKey2 : subKeys2) + { + if (rSubKey2->getKeyName() != OUStringConcatenation(xImplKey->getKeyName() + slash_UNO_slash_SERVICES ) && + rSubKey2->getKeyName() != OUStringConcatenation(xImplKey->getKeyName() + slash_UNO_slash_REGISTRY_LINKS ) && + rSubKey2->getKeyName() != OUStringConcatenation(xImplKey->getKeyName() + slash_UNO_slash_ACTIVATOR ) && + rSubKey2->getKeyName() != OUStringConcatenation(xImplKey->getKeyName() + slash_UNO_slash_SINGLETONS ) && + rSubKey2->getKeyName() != OUStringConcatenation(xImplKey->getKeyName() + slash_UNO_slash_LOCATION) ) + { + prepareUserKeys(xReg, xKey, rSubKey2, implName, false); + } + } + } + } + } + + if (hasLocationUrl) + { + hasLocationUrl = false; + OUString path(xImplKey->getKeyName()); + xImplKey->closeKey(); + xReg->getRootKey()->deleteKey(path); + } + } + + subKeys = xSource->openKeys(); + if (!subKeys.hasElements()) + { + OUString path(xSource->getKeyName()); + xSource->closeKey(); + xReg->getRootKey()->deleteKey(path); + } + } else + { + OUString path(xSource->getKeyName()); + xSource->closeKey(); + xReg->getRootKey()->deleteKey(path); + } +} + + +void delete_all_singleton_entries( + Reference < registry::XRegistryKey > const & xSingletons_section, + ::std::vector< OUString > const & impl_names ) + // throw (InvalidRegistryException, RuntimeException) +{ + Sequence< Reference< registry::XRegistryKey > > singletons( xSingletons_section->openKeys() ); + Reference< registry::XRegistryKey > const * subkeys = singletons.getConstArray(); + for ( sal_Int32 nPos = singletons.getLength(); nPos--; ) + { + Reference< registry::XRegistryKey > const & xSingleton = subkeys[ nPos ]; + Reference< registry::XRegistryKey > xRegisteredImplNames( + xSingleton->openKey( "REGISTERED_BY" ) ); + if (xRegisteredImplNames.is() && xRegisteredImplNames->isValid()) + { + Sequence< OUString > registered_implnames; + try + { + registered_implnames = xRegisteredImplNames->getAsciiListValue(); + } + catch (registry::InvalidValueException &) + { + } + auto aNonConstRange = asNonConstRange(registered_implnames); + sal_Int32 nOrigRegLength = registered_implnames.getLength(); + sal_Int32 nNewLength = nOrigRegLength; + for ( sal_Int32 n = nOrigRegLength; n--; ) + { + OUString const & registered_implname = registered_implnames[ n ]; + + for (auto const& impl_name : impl_names) + { + if (impl_name == registered_implname) + { + aNonConstRange[ n ] = registered_implnames[ nNewLength -1 ]; + --nNewLength; + } + } + } + + if (nNewLength != nOrigRegLength) + { + if (0 == nNewLength) + { + // remove whole entry + xRegisteredImplNames->closeKey(); + xSingleton->deleteKey( "REGISTERED_BY" ); + // registry key cannot provide its relative name, only absolute :( + OUString abs( xSingleton->getKeyName() ); + xSingletons_section->deleteKey( abs.copy( abs.lastIndexOf( '/' ) +1 ) ); + } + else + { + registered_implnames.realloc( nNewLength ); + xRegisteredImplNames->setAsciiListValue( registered_implnames ); + } + } + } + } +} + + +// static deleteAllServiceEntries + +void deleteAllServiceEntries( const Reference < XSimpleRegistry >& xReg, + const Reference < XRegistryKey >& xSource, + const OUString& implName) + // throw ( InvalidRegistryException, RuntimeException ) +{ + Sequence< Reference < XRegistryKey > > subKeys = xSource->openKeys(); + + if (subKeys.hasElements()) + { + bool hasNoImplementations = false; + + for (const Reference < XRegistryKey > & xServiceKey : std::as_const(subKeys)) + { + if (xServiceKey->getValueType() == RegistryValueType_ASCIILIST) + { + const Sequence<OUString> implEntries = xServiceKey->getAsciiListValue(); + sal_Int32 length = implEntries.getLength(); + sal_Int32 equals = static_cast<sal_Int32>(std::count(implEntries.begin(), implEntries.end(), implName)); + + if (equals == length) + { + hasNoImplementations = true; + } else + { + if (equals > 0) + { + Sequence<OUString> implEntriesNew(length-equals); + + std::copy_if(implEntries.begin(), implEntries.end(), implEntriesNew.getArray(), + [&implName](const OUString& rEntry) { return rEntry != implName; }); + + xServiceKey->setAsciiListValue(implEntriesNew); + } + } + } + + if (hasNoImplementations) + { + hasNoImplementations = false; + OUString path(xServiceKey->getKeyName()); + xServiceKey->closeKey(); + xReg->getRootKey()->deleteKey(path); + } + } + + subKeys = xSource->openKeys(); + if (!subKeys.hasElements()) + { + OUString path(xSource->getKeyName()); + xSource->closeKey(); + xReg->getRootKey()->deleteKey(path); + } + } else + { + OUString path(xSource->getKeyName()); + xSource->closeKey(); + xReg->getRootKey()->deleteKey(path); + } +} + + +bool is_supported_service( + OUString const & service_name, + Reference< reflection::XServiceTypeDescription > const & xService_td ) +{ + if (xService_td->getName() == service_name) + return true; + const Sequence< Reference< reflection::XServiceTypeDescription > > seq( + xService_td->getMandatoryServices() ); + return std::any_of(seq.begin(), seq.end(), [&service_name](const auto& rService) { + return is_supported_service( service_name, rService ); }); +} + + +void insert_singletons( + Reference< registry::XSimpleRegistry > const & xDest, + Reference< registry::XRegistryKey > const & xImplKey, + Reference< XComponentContext > const & xContext ) + // throw( registry::InvalidRegistryException, registry::CannotRegisterImplementationException, RuntimeException ) +{ + // singletons + Reference< registry::XRegistryKey > xKey( xImplKey->openKey( "UNO/SINGLETONS" ) ); + if (!(xKey.is() && xKey->isValid())) + return; + + OUString implname( xImplKey->getKeyName().copy( sizeof ("/IMPLEMENTATIONS/") -1 ) ); + // singleton entries + Sequence< Reference< registry::XRegistryKey > > xSingletons_section( xKey->openKeys() ); + Reference< registry::XRegistryKey > const * p = xSingletons_section.getConstArray(); + for ( sal_Int32 nPos = xSingletons_section.getLength(); nPos--; ) + { + Reference< registry::XRegistryKey > const & xSingleton = p[ nPos ]; + OUString singleton_name( + xSingleton->getKeyName().copy( + implname.getLength() + sizeof ("/IMPLEMENTATIONS//UNO/SINGLETONS/") -1 ) ); + OUString service_name( xSingleton->getStringValue() ); + + OUString keyname( "/SINGLETONS/" + singleton_name ); + Reference< registry::XRegistryKey > xKey2( xDest->getRootKey()->openKey( keyname ) ); + if (xKey2.is() && xKey2->isValid()) + { + try + { + OUString existing_name( xKey2->getStringValue() ); + if ( existing_name != service_name ) + { + Reference< container::XHierarchicalNameAccess > xTDMgr; + OUString the_tdmgr = + "/singletons/com.sun.star.reflection.theTypeDescriptionManager"; + xContext->getValueByName( the_tdmgr ) >>= xTDMgr; + if (! xTDMgr.is()) + { + throw RuntimeException( "cannot get singleton " + the_tdmgr ); + } + try + { + Reference< reflection::XServiceTypeDescription > xExistingService_td; + xTDMgr->getByHierarchicalName( existing_name ) >>= xExistingService_td; + if (! xExistingService_td.is()) + { + throw RuntimeException( "cannot get service type description: " + existing_name ); + } + + // everything's fine if existing service entry supports the one + // to be registered + if (! is_supported_service( service_name, xExistingService_td )) + { + throw registry::CannotRegisterImplementationException( + "existing singleton service (" + singleton_name + "=" + existing_name + ") " + " does not support given one: " + service_name); + } + } + catch (const container::NoSuchElementException & exc) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "cannot get service type description: " + exc.Message, + nullptr, anyEx ); + } + } + } + catch (registry::InvalidValueException &) + { + // repair + xKey2->setStringValue( service_name ); + } + } + else + { + // insert singleton entry + xKey2 = xDest->getRootKey()->createKey( keyname ); + xKey2->setStringValue( service_name ); + } + + Reference< registry::XRegistryKey > xRegisteredImplNames( + xKey2->openKey( "REGISTERED_BY" ) ); + if (!xRegisteredImplNames.is() || !xRegisteredImplNames->isValid()) + { + // create + xRegisteredImplNames = xKey2->createKey( "REGISTERED_BY" ); + } + + Sequence< OUString > implnames; + try + { + implnames = xRegisteredImplNames->getAsciiListValue(); + } + catch (registry::InvalidValueException &) + { + } + // check implname is already in + if (comphelper::findValue(implnames, implname) == -1) + { + // append and write back + implnames.realloc( implnames.getLength() +1 ); + implnames.getArray()[ implnames.getLength() -1 ] = implname; + xRegisteredImplNames->setAsciiListValue( implnames ); + } + } +} + + +// static prepareRegistry + +void prepareRegistry( + const Reference < XSimpleRegistry >& xDest, + const Reference < XRegistryKey >& xSource, + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + Reference< XComponentContext > const & xContext ) + // throw ( InvalidRegistryException, CannotRegisterImplementationException, RuntimeException ) +{ + const Sequence< Reference < XRegistryKey > > subKeys = xSource->openKeys(); + + if (!subKeys.hasElements()) + { + throw InvalidRegistryException( + "prepareRegistry(): source registry is empty" ); + } + + for (const Reference < XRegistryKey >& xImplKey : subKeys) + { + Reference < XRegistryKey > xKey = xImplKey->openKey( + slash_UNO_slash_SERVICES ); + + if (xKey.is()) + { + // update entries in SERVICES section + const Sequence< Reference < XRegistryKey > > serviceKeys = xKey->openKeys(); + + OUString implName = xImplKey->getKeyName().copy(1); + sal_Int32 firstDot = implName.indexOf('/'); + + if (firstDot >= 0) + implName = implName.copy(firstDot + 1); + + sal_Int32 offset = xKey->getKeyName().getLength() + 1; + + for (const Reference < XRegistryKey >& rServiceKey : serviceKeys) + { + OUString serviceName = rServiceKey->getKeyName().copy(offset); + + createUniqueSubEntry( + xDest->getRootKey()->createKey( + slash_SERVICES + serviceName ), + implName); + } + + xKey = xImplKey->openKey( slash_UNO ); + if (xKey.is()) + { + const Sequence< Reference < XRegistryKey > > subKeys2 = xKey->openKeys(); + + for (const Reference < XRegistryKey >& rSubKey2 : subKeys2) + { + if (rSubKey2->getKeyName() != OUStringConcatenation(xImplKey->getKeyName() + slash_UNO_slash_SERVICES) && + rSubKey2->getKeyName() != OUStringConcatenation(xImplKey->getKeyName() + slash_UNO_slash_REGISTRY_LINKS ) && + rSubKey2->getKeyName() != OUStringConcatenation(xImplKey->getKeyName() + slash_UNO_slash_SINGLETONS )) + { + prepareUserKeys(xDest, xKey, rSubKey2, implName, true); + } + } + } + } + + // update LOCATION entry + xKey = xImplKey->createKey( slash_UNO_slash_LOCATION ); + + if (xKey.is()) + { + xKey->setAsciiValue(locationUrl); + } + + // update ACTIVATOR entry + xKey = xImplKey->createKey( slash_UNO_slash_ACTIVATOR ); + + if (xKey.is()) + { + xKey->setAsciiValue(implementationLoaderUrl); + } + + xKey = xImplKey->openKey( slash_UNO_slash_SERVICES ); + + if (xKey.is() && (xKey->getValueType() == RegistryValueType_ASCIILIST)) + { + // update link entries in REGISTRY_LINKS section + const Sequence<OUString> linkNames = xKey->getAsciiListValue(); + + for (const OUString& rLinkName : linkNames) + { + prepareLink(xDest, xImplKey, rLinkName); + } + } + + insert_singletons( xDest, xImplKey, xContext ); + } +} + + +void findImplementations( const Reference < XRegistryKey > & xSource, + std::vector<OUString>& implNames) +{ + bool isImplKey = false; + + try + { + Reference < XRegistryKey > xKey = xSource->openKey( + slash_UNO_slash_SERVICES ); + + if (xKey.is() && xKey->getKeyNames().hasElements()) + { + isImplKey = true; + + OUString implName = xSource->getKeyName().copy(1).replace('/', '.'); + sal_Int32 firstDot = implName.indexOf('.'); + + if (firstDot >= 0) + implName = implName.copy(firstDot + 1); + + implNames.push_back(implName); + } + } + catch(InvalidRegistryException&) + { + } + + if (isImplKey) return; + + try + { + const Sequence< Reference < XRegistryKey > > subKeys = xSource->openKeys(); + + for (const Reference < XRegistryKey >& rSubKey : subKeys) + { + findImplementations(rSubKey, implNames); + } + } + catch(InvalidRegistryException&) + { + } +} + + +class ImplementationRegistration + : public WeakImplHelper< XImplementationRegistration2, XServiceInfo, XInitialization > +{ +public: + explicit ImplementationRegistration( const Reference < XComponentContext > & rSMgr ); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XImplementationRegistration + virtual void SAL_CALL registerImplementation( + const OUString& implementationLoader, + const OUString& location, + const Reference < XSimpleRegistry > & xReg) override; + + virtual sal_Bool SAL_CALL revokeImplementation( + const OUString& location, + const Reference < XSimpleRegistry >& xReg) override; + + virtual Sequence< OUString > SAL_CALL getImplementations( + const OUString& implementationLoader, + const OUString& location) override; + virtual Sequence< OUString > SAL_CALL checkInstantiation( + const OUString& implementationName) override; + + // XImplementationRegistration2 + virtual void SAL_CALL registerImplementationWithLocation( + const OUString& implementationLoader, + const OUString& location, + const OUString& registeredLocation, + const Reference < XSimpleRegistry > & xReg) override; + + // XInitialization + virtual void SAL_CALL initialize( + const css::uno::Sequence< css::uno::Any >& aArguments ) override; + +private: // helper methods + void prepareRegister( + const OUString& implementationLoader, + const OUString& location, + const OUString& registeredLocation, + const Reference < XSimpleRegistry > & xReg); + // throw( CannotRegisterImplementationException, RuntimeException ) + + static void doRegister( const Reference < XMultiComponentFactory >& xSMgr, + const Reference < XComponentContext > &xCtx, + const Reference < XImplementationLoader >& xAct, + const Reference < XSimpleRegistry >& xDest, + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const OUString& registeredLocationUrl); + /* throw ( InvalidRegistryException, + MergeConflictException, + CannotRegisterImplementationException, RuntimeException ) */ + + static void doRevoke( const Reference < XSimpleRegistry >& xDest, + std::u16string_view locationUrl ); + // throw( InvalidRegistryException, RuntimeException ) + Reference< XSimpleRegistry > getRegistryFromServiceManager() const; + + static Reference< XSimpleRegistry > createTemporarySimpleRegistry( + const Reference< XMultiComponentFactory > &rSMgr, + const Reference < XComponentContext > & rCtx ); + +private: // members + Reference < XMultiComponentFactory > m_xSMgr; + Reference < XComponentContext > m_xCtx; +}; + + +// ImplementationRegistration() + +ImplementationRegistration::ImplementationRegistration( const Reference < XComponentContext > & xCtx ) + : m_xSMgr( xCtx->getServiceManager() ) + , m_xCtx( xCtx ) +{} + +// XServiceInfo +OUString ImplementationRegistration::getImplementationName() +{ + return "com.sun.star.comp.stoc.ImplementationRegistration"; +} + +// XServiceInfo +sal_Bool ImplementationRegistration::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > ImplementationRegistration::getSupportedServiceNames() +{ + return { "com.sun.star.registry.ImplementationRegistration" }; +} + +Reference< XSimpleRegistry > ImplementationRegistration::getRegistryFromServiceManager() const +{ + Reference < XPropertySet > xPropSet( m_xSMgr, UNO_QUERY ); + Reference < XSimpleRegistry > xRegistry; + + if( xPropSet.is() ) { + + try { // the implementation does not support XIntrospectionAccess ! + + Any aAny = xPropSet->getPropertyValue( Registry ); + + if( aAny.getValueType().getTypeClass() == TypeClass_INTERFACE ) { + aAny >>= xRegistry; + } + } + catch( UnknownPropertyException & ) { + // empty reference is error signal ! + } + } + + return xRegistry; +} + + +// XInitialization + +void ImplementationRegistration::initialize( + const css::uno::Sequence< css::uno::Any >& aArgs ) +{ + + if( aArgs.getLength() != 4 ) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() expects 4 parameters, got " + OUString::number( aArgs.getLength() ), + Reference<XInterface > (), 0 ); + } + + Reference< XImplementationLoader > rLoader; + OUString loaderServiceName; + OUString locationUrl; + Reference< XSimpleRegistry > rReg; + + // 1st argument : An instance of an implementation loader + if( aArgs.getConstArray()[0].getValueType().getTypeClass() == TypeClass_INTERFACE ) { + aArgs.getConstArray()[0] >>= rLoader; + } + if( !rLoader.is()) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() invalid first parameter," + "expected " + cppu::UnoType<decltype(rLoader)>::get().getTypeName() + + ", got " + aArgs.getConstArray()[0].getValueTypeName(), + Reference< XInterface > (), 0 ); + } + + // 2nd argument : The service name of the loader. This name is written into the registry + if( aArgs.getConstArray()[1].getValueType().getTypeClass() == TypeClass_STRING ) { + aArgs.getConstArray()[1] >>= loaderServiceName; + } + if( loaderServiceName.isEmpty() ) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() invalid second parameter," + "expected string, got " + aArgs.getConstArray()[1].getValueTypeName(), + Reference< XInterface > (), 0 ); + } + + // 3rd argument : The file name of the dll, that contains the loader + if( aArgs.getConstArray()[2].getValueType().getTypeClass() == TypeClass_STRING ) { + aArgs.getConstArray()[2] >>= locationUrl; + } + if( locationUrl.isEmpty() ) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() invalid third parameter," + "expected string, got " + aArgs.getConstArray()[2].getValueTypeName(), + Reference< XInterface > (), 0 ); + } + + // 4th argument : The registry, the service should be written to + if( aArgs.getConstArray()[3].getValueType().getTypeClass() == TypeClass_INTERFACE ) { + aArgs.getConstArray()[3] >>= rReg; + } + + if( !rReg.is() ) { + rReg = getRegistryFromServiceManager(); + if( !rReg.is() ) { + throw IllegalArgumentException( + "ImplementationRegistration::initialize() invalid fourth parameter," + "expected " + cppu::UnoType<decltype(rReg)>::get().getTypeName() + + ", got " + aArgs.getConstArray()[3].getValueTypeName(), + Reference< XInterface > (), 0 ); + } + } + + doRegister(m_xSMgr, m_xCtx, rLoader , rReg, loaderServiceName , locationUrl, locationUrl); +} + + +// virtual function registerImplementationWithLocation of XImplementationRegistration2 + +void ImplementationRegistration::registerImplementationWithLocation( + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const OUString& registeredLocationUrl, + const Reference < XSimpleRegistry > & xReg) +{ + prepareRegister( + implementationLoaderUrl, locationUrl, registeredLocationUrl, xReg); +} + +// helper function +void ImplementationRegistration::prepareRegister( + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const OUString& registeredLocationUrl, + const Reference < XSimpleRegistry > & xReg) + // throw( CannotRegisterImplementationException, RuntimeException ) +{ + OUString activatorName; + + if (!implementationLoaderUrl.isEmpty()) + { + activatorName = implementationLoaderUrl.getToken(0, ':'); + } else + { + // check locationUrl to find out what kind of loader is needed + // set implLoaderUrl + } + + if( !m_xSMgr.is() ) { + throw CannotRegisterImplementationException( + "ImplementationRegistration::registerImplementation() " + "no componentcontext available to instantiate loader" ); + } + + try + { + Reference < XImplementationLoader > xAct( + m_xSMgr->createInstanceWithContext(activatorName, m_xCtx) , UNO_QUERY ); + if (!xAct.is()) + { + throw CannotRegisterImplementationException( + "ImplementationRegistration::registerImplementation() - The service " + + activatorName + " cannot be instantiated" ); + } + + Reference < XSimpleRegistry > xRegistry; + + if (xReg.is()) + { + // registry supplied by user + xRegistry = xReg; + } + else + { + xRegistry = getRegistryFromServiceManager(); + } + + if ( xRegistry.is()) + { + doRegister(m_xSMgr, m_xCtx, xAct, xRegistry, implementationLoaderUrl, + locationUrl, registeredLocationUrl); + } + + } + catch( CannotRegisterImplementationException & ) + { + throw; + } + catch( const InvalidRegistryException & e ) + { + throw CannotRegisterImplementationException( + "ImplementationRegistration::registerImplementation() " + "InvalidRegistryException during registration (" + e.Message + ")" ); + } + catch( const MergeConflictException & e ) + { + throw CannotRegisterImplementationException( + "ImplementationRegistration::registerImplementation() " + "MergeConflictException during registration (" + e.Message + ")" ); + } + +} + + +// virtual function registerImplementation of XImplementationRegistration + +void ImplementationRegistration::registerImplementation( + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const Reference < XSimpleRegistry > & xReg) +{ + prepareRegister(implementationLoaderUrl, locationUrl, locationUrl, xReg); +} + + +// virtual function revokeImplementation of XImplementationRegistration + +sal_Bool ImplementationRegistration::revokeImplementation(const OUString& location, + const Reference < XSimpleRegistry >& xReg) +{ + bool ret = false; + + Reference < XSimpleRegistry > xRegistry; + + if (xReg.is()) { + xRegistry = xReg; + } + else { + Reference < XPropertySet > xPropSet( m_xSMgr, UNO_QUERY ); + if( xPropSet.is() ) { + try { + Any aAny = xPropSet->getPropertyValue( Registry ); + + if( aAny.getValueType().getTypeClass() == TypeClass_INTERFACE ) + { + aAny >>= xRegistry; + } + } + catch ( UnknownPropertyException & ) { + } + } + } + + if (xRegistry.is()) + { + try + { + doRevoke(xRegistry, location); + ret = true; + } + catch( InvalidRegistryException & ) + { + // no way to transport the error, as no exception is specified and a runtime + // exception is not appropriate. + OSL_FAIL( "InvalidRegistryException during revokeImplementation" ); + } + } + + return ret; +} + + +// virtual function getImplementations of XImplementationRegistration + +Sequence< OUString > ImplementationRegistration::getImplementations( + const OUString & implementationLoaderUrl, + const OUString & locationUrl) +{ + OUString activatorName; + + if (!implementationLoaderUrl.isEmpty()) + { + activatorName = implementationLoaderUrl.getToken(0, ':'); + } else + { + // check locationUrl to find out what kind of loader is needed + // set implementationLoaderUrl + } + + if( m_xSMgr.is() ) { + + Reference < XImplementationLoader > xAct( + m_xSMgr->createInstanceWithContext( activatorName, m_xCtx ), UNO_QUERY ); + + if (xAct.is()) + { + + Reference < XSimpleRegistry > xReg = + createTemporarySimpleRegistry( m_xSMgr, m_xCtx); + + if (xReg.is()) + { + try + { + xReg->open(OUString() /* in mem */, false, true); + Reference < XRegistryKey > xImpl; + + { // only necessary for deleting the temporary variable of rootkey + xImpl = xReg->getRootKey()->createKey( slash_IMPLEMENTATIONS ); + } + if (xAct->writeRegistryInfo(xImpl, implementationLoaderUrl, locationUrl)) + { + std::vector<OUString> implNames; + + findImplementations(xImpl, implNames); + + if (!implNames.empty()) + { + Sequence<OUString> seqImpl(comphelper::containerToSequence(implNames)); + xImpl->closeKey(); + return seqImpl; + } + } + + xImpl->closeKey(); + } + catch(MergeConflictException&) + { + } + catch(InvalidRegistryException&) + { + } + } + } + } + + return Sequence<OUString>(); +} + + +// virtual function checkInstantiation of XImplementationRegistration + +Sequence< OUString > ImplementationRegistration::checkInstantiation(const OUString&) +{ + OSL_FAIL( "ImplementationRegistration::checkInstantiation not implemented" ); + return Sequence<OUString>(); +} + + +// helper function doRegistration + + +void ImplementationRegistration::doRevoke( + const Reference < XSimpleRegistry >& xDest, + std::u16string_view locationUrl) + // throw ( InvalidRegistryException, RuntimeException ) +{ + if( !xDest.is() ) + return; + + std::vector<OUString> aNames; + + Reference < XRegistryKey > xRootKey( xDest->getRootKey() ); + + Reference < XRegistryKey > xKey = + xRootKey->openKey( slash_IMPLEMENTATIONS ); + if (xKey.is() && xKey->isValid()) + { + deleteAllImplementations(xDest, xKey, locationUrl, aNames); + } + + xKey = xRootKey->openKey( slash_SERVICES ); + if (xKey.is()) + { + for (auto const& name : aNames) + { + deleteAllServiceEntries(xDest, xKey, name); + } + } + + xKey = xRootKey->openKey( "/SINGLETONS" ); + if (xKey.is() && xKey->isValid()) + { + delete_all_singleton_entries( xKey, aNames ); + } + + if (xRootKey.is()) + xRootKey->closeKey(); + if (xKey.is() && xKey->isValid() ) + xKey->closeKey(); +} + +void ImplementationRegistration::doRegister( + const Reference< XMultiComponentFactory > & xSMgr, + const Reference< XComponentContext > &xCtx, + const Reference < XImplementationLoader > & xAct, + const Reference < XSimpleRegistry >& xDest, + const OUString& implementationLoaderUrl, + const OUString& locationUrl, + const OUString& registeredLocationUrl) + /* throw ( InvalidRegistryException, + MergeConflictException, + CannotRegisterImplementationException, RuntimeException ) */ +{ + Reference < XSimpleRegistry > xReg = + createTemporarySimpleRegistry( xSMgr, xCtx ); + Reference < XRegistryKey > xSourceKey; + + if (!(xAct.is() && xReg.is() && xDest.is())) + return; + + try + { + xReg->open(OUString() /* in mem */, false, true); + + { // only necessary for deleting the temporary variable of rootkey + xSourceKey = xReg->getRootKey()->createKey( slash_IMPLEMENTATIONS ); + } + + bool bSuccess = + xAct->writeRegistryInfo(xSourceKey, implementationLoaderUrl, locationUrl); + if ( !bSuccess ) + { + throw CannotRegisterImplementationException( + "ImplementationRegistration::doRegistration() component registration signaled failure" ); + } + + prepareRegistry(xDest, xSourceKey, implementationLoaderUrl, registeredLocationUrl, xCtx); + + xSourceKey->closeKey(); + + xSourceKey = xReg->getRootKey(); + Reference < XRegistryKey > xDestKey = xDest->getRootKey(); + stoc_impreg::mergeKeys( xDestKey, xSourceKey ); + xDestKey->closeKey(); + xSourceKey->closeKey(); + + + // Cleanup Source registry. + if ( xSourceKey->isValid() ) + xSourceKey->closeKey(); + } + catch(CannotRegisterImplementationException&) + { + if ( xSourceKey->isValid() ) + xSourceKey->closeKey(); + // and throw again + throw; + } +} + + +Reference< XSimpleRegistry > ImplementationRegistration::createTemporarySimpleRegistry( + const Reference< XMultiComponentFactory > &rSMgr, + const Reference < XComponentContext > & xCtx) +{ + + Reference < XSimpleRegistry > xReg( + rSMgr->createInstanceWithContext( + com_sun_star_registry_SimpleRegistry, xCtx ), + UNO_QUERY); + OSL_ASSERT( xReg.is() ); + return xReg; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_ImplementationRegistration_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ImplementationRegistration(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/implementationregistration/mergekeys.cxx b/stoc/source/implementationregistration/mergekeys.cxx new file mode 100644 index 000000000..da7da509e --- /dev/null +++ b/stoc/source/implementationregistration/mergekeys.cxx @@ -0,0 +1,176 @@ +/* -*- 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 <vector> + +#include <osl/diagnose.h> + +#include <com/sun/star/registry/XRegistryKey.hpp> + +#include "mergekeys.hxx" + +using namespace ::osl; +using namespace css::uno; +using namespace ::com::sun::star; + +namespace stoc_impreg +{ + +namespace { + +struct Link +{ + OUString m_name; + OUString m_target; + + Link( OUString const & name, OUString const & target ) + : m_name( name ) + , m_target( target ) + {} +}; + +} + +typedef ::std::vector< Link > t_links; + + +static void mergeKeys( + Reference< registry::XRegistryKey > const & xDest, + Reference< registry::XRegistryKey > const & xSource, + t_links & links ) + // throw( registry::InvalidRegistryException, registry::MergeConflictException, RuntimeException ) +{ + if (!xSource.is() || !xSource->isValid()) { + throw registry::InvalidRegistryException( + "source key is null or invalid!" ); + } + if (!xDest.is() || !xDest->isValid()) { + throw registry::InvalidRegistryException( + "destination key is null or invalid!" ); + } + + // write value + switch (xSource->getValueType()) + { + case registry::RegistryValueType_NOT_DEFINED: + break; + case registry::RegistryValueType_LONG: + xDest->setLongValue( xSource->getLongValue() ); + break; + case registry::RegistryValueType_ASCII: + xDest->setAsciiValue( xSource->getAsciiValue() ); + break; + case registry::RegistryValueType_STRING: + xDest->setStringValue( xSource->getStringValue() ); + break; + case registry::RegistryValueType_BINARY: + xDest->setBinaryValue( xSource->getBinaryValue() ); + break; + case registry::RegistryValueType_LONGLIST: + xDest->setLongListValue( xSource->getLongListValue() ); + break; + case registry::RegistryValueType_ASCIILIST: + xDest->setAsciiListValue( xSource->getAsciiListValue() ); + break; + case registry::RegistryValueType_STRINGLIST: + xDest->setStringListValue( xSource->getStringListValue() ); + break; + default: + OSL_ASSERT(false); + break; + } + + // sub keys + Sequence< OUString > sourceKeys( xSource->getKeyNames() ); + OUString const * pSourceKeys = sourceKeys.getConstArray(); + for ( sal_Int32 nPos = sourceKeys.getLength(); nPos--; ) + { + // key name + OUString name( pSourceKeys[ nPos ] ); + sal_Int32 nSlash = name.lastIndexOf( '/' ); + if (nSlash >= 0) + { + name = name.copy( nSlash +1 ); + } + + if (xSource->getKeyType( name ) == registry::RegistryKeyType_KEY) + { + // try to open existing dest key or create new one + Reference< registry::XRegistryKey > xDestKey( xDest->createKey( name ) ); + Reference< registry::XRegistryKey > xSourceKey( xSource->openKey( name ) ); + mergeKeys( xDestKey, xSourceKey, links ); + xSourceKey->closeKey(); + xDestKey->closeKey(); + } + else // link + { + // remove existing key + Reference< registry::XRegistryKey > xDestKey( xDest->openKey( name ) ); + if (xDestKey.is() && xDestKey->isValid()) // something to remove + { + xDestKey->closeKey(); + if (xDest->getKeyType( name ) == registry::RegistryKeyType_LINK) + { + xDest->deleteLink( name ); + } + else + { + xDest->deleteKey( name ); + } + } + + links.push_back( Link( + pSourceKeys[ nPos ], // abs path + xSource->getResolvedName( name ) // abs resolved name + ) ); + } + } +} + + +void mergeKeys( + Reference< registry::XRegistryKey > const & xDest, + Reference< registry::XRegistryKey > const & xSource ) + // throw( registry::InvalidRegistryException, registry::MergeConflictException, RuntimeException ) +{ + if (!xDest.is() || !xDest->isValid()) { + throw registry::InvalidRegistryException( + "destination key is null or invalid!" ); + } + if (xDest->isReadOnly()) + { + throw registry::InvalidRegistryException( + "destination registry is read-only! cannot merge!" ); + } + + t_links links; + links.reserve( 16 ); + mergeKeys( xDest, xSource, links ); + + for ( size_t nPos = links.size(); nPos--; ) + { + Link const & r = links[ nPos ]; + OSL_VERIFY( xDest->createLink( r.m_name, r.m_target ) ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/implementationregistration/mergekeys.hxx b/stoc/source/implementationregistration/mergekeys.hxx new file mode 100644 index 000000000..3f54e5712 --- /dev/null +++ b/stoc/source/implementationregistration/mergekeys.hxx @@ -0,0 +1,46 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_STOC_SOURCE_IMPLEMENTATIONREGISTRATION_MERGEKEYS_HXX +#define INCLUDED_STOC_SOURCE_IMPLEMENTATIONREGISTRATION_MERGEKEYS_HXX + +#include <sal/config.h> +#include <sal/types.h> + +namespace com::sun::star::registry { + class XRegistryKey; +} +namespace com::sun::star::uno { template <typename > class Reference; } + +namespace stoc_impreg { + +// throws css::registry::InvalidRegistryException, +// css::registry::MergeConflictException, +// css::uno::RuntimeException: +void mergeKeys( + css::uno::Reference< css::registry::XRegistryKey > + const & xDest, + css::uno::Reference< css::registry::XRegistryKey > + const & xSource); + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/inspect/introspection.component b/stoc/source/inspect/introspection.component new file mode 100644 index 000000000..84e121fad --- /dev/null +++ b/stoc/source/inspect/introspection.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.Introspection" + constructor="com_sun_star_comp_stoc_Introspection_get_implementation" + single-instance="true"> + <service name="com.sun.star.beans.Introspection"/> + <singleton name="com.sun.star.beans.theIntrospection"/> + </implementation> +</component> diff --git a/stoc/source/inspect/introspection.cxx b/stoc/source/inspect/introspection.cxx new file mode 100644 index 000000000..1a92d5b4c --- /dev/null +++ b/stoc/source/inspect/introspection.cxx @@ -0,0 +1,2420 @@ +/* -*- 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 <sal/config.h> + +#include <cassert> +#include <cstddef> +#include <limits> +#include <map> +#include <memory> +#include <mutex> +#include <set> + +#include <o3tl/any.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> +#include <salhelper/simplereferenceobject.hxx> + +#include <com/sun/star/lang/NoSuchMethodException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XUnoTunnel.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> +#include <com/sun/star/reflection/XIdlClass.hpp> +#include <com/sun/star/reflection/XIdlField2.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <com/sun/star/beans/UnknownPropertyException.hpp> +#include <com/sun/star/beans/Property.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/XFastPropertySet.hpp> +#include <com/sun/star/beans/XIntrospection.hpp> +#include <com/sun/star/beans/XIntrospectionAccess.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/beans/XExactName.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/PropertyConcept.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> + +#include <rtl/ref.hxx> +#include <rtl/ustrbuf.hxx> +#include <unordered_map> + +using namespace css::uno; +using namespace css::lang; +using namespace css::reflection; +using namespace css::container; +using namespace css::beans; +using namespace css::beans::PropertyAttribute; +using namespace css::beans::PropertyConcept; +using namespace css::beans::MethodConcept; +using namespace cppu; +using namespace osl; + +namespace +{ + +typedef WeakImplHelper< XIntrospectionAccess, XMaterialHolder, XExactName, + XPropertySet, XFastPropertySet, XPropertySetInfo, + XNameContainer, XIndexContainer, XEnumerationAccess, + XIdlArray, XUnoTunnel > IntrospectionAccessHelper; + + +// Special value for Method-Concept, to be able to mark "normal" functions +#define MethodConcept_NORMAL_IMPL 0x80000000 + + +// Method to assert, if a class is derived from another class +bool isDerivedFrom( const Reference<XIdlClass>& xToTestClass, const Reference<XIdlClass>& xDerivedFromClass ) +{ + const Sequence< Reference<XIdlClass> > aClassesSeq = xToTestClass->getSuperclasses(); + + return std::any_of(aClassesSeq.begin(), aClassesSeq.end(), + [&xDerivedFromClass](const Reference<XIdlClass>& rxClass) { + return xDerivedFromClass->equals( rxClass ) + || isDerivedFrom( rxClass, xDerivedFromClass ); + }); +} + + +// *** Classification of Properties (no enum, to be able to use Sequence) *** +// Properties from a PropertySet-Interface +#define MAP_PROPERTY_SET 0 +// Properties from Fields +#define MAP_FIELD 1 +// Properties that get described with get/set methods +#define MAP_GETSET 2 +// Properties with only a set method +#define MAP_SETONLY 3 + + +// Increments by which the size of sequences get adjusted +#define ARRAY_SIZE_STEP 20 + + +//*** IntrospectionAccessStatic_Impl *** + +// Equals to the old IntrospectionAccessImpl, forms now a static +// part of the new Instance-related ImplIntrospectionAccess + +// Hashtable for the search of names +typedef std::unordered_map +< + OUString, + sal_Int32 +> +IntrospectionNameMap; + + +// Hashtable to assign exact names to the Lower-Case +// converted names, for the support of XExactName +typedef std::unordered_map +< + OUString, + OUString +> +LowerToExactNameMap; + + +class IntrospectionAccessStatic_Impl: public salhelper::SimpleReferenceObject +{ + friend class Implementation; + friend class ImplIntrospectionAccess; + + // Holding CoreReflection + Reference< XIdlReflection > mxCoreReflection; + + // InterfaceSequences, to save additional information in a property + // for example the Field at MAP_FIELD, the get/set-Methods at MAP_GETSET, et cetera + std::vector< Reference<XInterface> > aInterfaceSeq1; + std::vector< Reference<XInterface> > aInterfaceSeq2; + + // Hashtables for names + IntrospectionNameMap maPropertyNameMap; + IntrospectionNameMap maMethodNameMap; + LowerToExactNameMap maLowerToExactNameMap; + + // Vector of all Properties, also for delivering from getProperties() + std::vector<Property> maAllPropertySeq; + + // Mapping of properties to Access-Types + std::vector<sal_Int16> maMapTypeSeq; + + // Classification of found methods + std::vector<sal_Int32> maPropertyConceptSeq; + + // Number of Properties + sal_Int32 mnPropCount; + + // Number of Properties, which are assigned to particular concepts + //sal_Int32 mnDangerousPropCount; + sal_Int32 mnPropertySetPropCount; + sal_Int32 mnAttributePropCount; + sal_Int32 mnMethodPropCount; + + // Flags which indicate if various interfaces are present + bool mbFastPropSet; + bool mbElementAccess; + bool mbNameAccess; + bool mbNameReplace; + bool mbNameContainer; + bool mbIndexAccess; + bool mbIndexReplace; + bool mbIndexContainer; + bool mbEnumerationAccess; + bool mbIdlArray; + bool mbUnoTunnel; + + // Original handles of FastPropertySets + std::unique_ptr<sal_Int32[]> mpOrgPropertyHandleArray; + + // MethodSequence, that accepts all methods + std::vector< Reference<XIdlMethod> > maAllMethodSeq; + + // Classification of found methods + std::vector<sal_Int32> maMethodConceptSeq; + + // Number of methods + sal_Int32 mnMethCount; + + // Sequence of Listener, that can be registered + std::vector< Type > maSupportedListenerSeq; + + // Helper-methods for adjusting sizes of Sequences + void checkPropertyArraysSize( sal_Int32 iNextIndex ); + static void checkInterfaceArraySize( std::vector< Reference<XInterface> >& rSeq, std::vector<Reference<XInterface>>& rInterfaceVec, + sal_Int32 iNextIndex ); + +public: + explicit IntrospectionAccessStatic_Impl( Reference< XIdlReflection > const & xCoreReflection_ ); + sal_Int32 getPropertyIndex( const OUString& aPropertyName ) const; + sal_Int32 getMethodIndex( const OUString& aMethodName ) const; + + // Methods of XIntrospectionAccess (OLD, now only Impl) + void setPropertyValue(const Any& obj, const OUString& aPropertyName, const Any& aValue) const; +// void setPropertyValue(Any& obj, const OUString& aPropertyName, const Any& aValue) const; + Any getPropertyValue(const Any& obj, const OUString& aPropertyName) const; + void setPropertyValueByIndex(const Any& obj, sal_Int32 nIndex, const Any& aValue) const; +// void setPropertyValueByIndex(Any& obj, sal_Int32 nIndex, const Any& aValue) const; + Any getPropertyValueByIndex(const Any& obj, sal_Int32 nIndex) const; + + const std::vector<Property>& getProperties() const { return maAllPropertySeq; } + const std::vector< Reference<XIdlMethod> >& getMethods() const { return maAllMethodSeq; } + const std::vector< Type >& getSupportedListeners() const { return maSupportedListenerSeq; } + const std::vector<sal_Int32>& getPropertyConcepts() const { return maPropertyConceptSeq; } + const std::vector<sal_Int32>& getMethodConcepts() const { return maMethodConceptSeq; } +}; + + +// Ctor +IntrospectionAccessStatic_Impl::IntrospectionAccessStatic_Impl( Reference< XIdlReflection > const & xCoreReflection_ ) + : mxCoreReflection( xCoreReflection_ ) +{ + aInterfaceSeq1.resize( ARRAY_SIZE_STEP ); + aInterfaceSeq2.resize( ARRAY_SIZE_STEP ); + + // Property-Data + maAllPropertySeq.resize( ARRAY_SIZE_STEP ); + maMapTypeSeq.resize( ARRAY_SIZE_STEP ); + maPropertyConceptSeq.resize( ARRAY_SIZE_STEP ); + + mbFastPropSet = false; + mbElementAccess = false; + mbNameAccess = false; + mbNameReplace = false; + mbNameContainer = false; + mbIndexAccess = false; + mbIndexReplace = false; + mbIndexContainer = false; + mbEnumerationAccess = false; + mbIdlArray = false; + mbUnoTunnel = false; + + mpOrgPropertyHandleArray = nullptr; + + mnPropCount = 0; + //mnDangerousPropCount = 0; + mnPropertySetPropCount = 0; + mnAttributePropCount = 0; + mnMethodPropCount = 0; + + // Method-Data + mnMethCount = 0; +} + +sal_Int32 IntrospectionAccessStatic_Impl::getPropertyIndex( const OUString& aPropertyName ) const +{ + auto aIt = maPropertyNameMap.find(aPropertyName); + if (aIt != maPropertyNameMap.end()) + return aIt->second; + + return -1; +} + +sal_Int32 IntrospectionAccessStatic_Impl::getMethodIndex( const OUString& aMethodName ) const +{ + auto aIt = maMethodNameMap.find(aMethodName); + if (aIt != maMethodNameMap.end()) + { + return aIt->second; + } + + // #95159 Check if full qualified name matches + sal_Int32 nSearchFrom = aMethodName.getLength(); + while( true ) + { + // Strategy: Search back until the first '_' is found + sal_Int32 nFound = aMethodName.lastIndexOf( '_', nSearchFrom ); + if( nFound == -1 ) + break; + + OUString aPureMethodName = aMethodName.copy( nFound + 1 ); + + aIt = maMethodNameMap.find( aPureMethodName ); + if (aIt != maMethodNameMap.end()) + { + // Check if it can be a type? + // Problem: Does not work if package names contain _ ?! + OUString aStr = aMethodName.copy( 0, nFound ); + OUString aTypeName = aStr.replace( '_', '.' ); + Reference< XIdlClass > xClass = mxCoreReflection->forName( aTypeName ); + if( xClass.is() ) + { + // If this is a valid class it could be the right method + + // Could be the right method, type has to be checked + const sal_Int32 iHashResult = aIt->second; + + const Reference<XIdlMethod> xMethod = maAllMethodSeq[iHashResult]; + + Reference< XIdlClass > xMethClass = xMethod->getDeclaringClass(); + if( xClass->equals( xMethClass ) ) + { + return iHashResult; + } + else + { + // Could also be another method with the same name + // Iterate over all methods + size_t nLen = maAllMethodSeq.size(); + for (size_t i = 0; i < nLen; ++i) + { + const Reference<XIdlMethod> xMethod2 = maAllMethodSeq[ i ]; + if( xMethod2->getName() == aPureMethodName ) + { + Reference< XIdlClass > xMethClass2 = xMethod2->getDeclaringClass(); + + if( xClass->equals( xMethClass2 ) ) + { + return i; + } + } + } + } + } + } + + nSearchFrom = nFound - 1; + if( nSearchFrom < 0 ) + break; + } + return -1; +} + +void IntrospectionAccessStatic_Impl::setPropertyValue( const Any& obj, const OUString& aPropertyName, const Any& aValue ) const +//void IntrospectionAccessStatic_Impl::setPropertyValue( Any& obj, const OUString& aPropertyName, const Any& aValue ) const +{ + sal_Int32 i = getPropertyIndex( aPropertyName ); + if( i == -1 ) + throw UnknownPropertyException(aPropertyName); + setPropertyValueByIndex( obj, i, aValue ); +} + +void IntrospectionAccessStatic_Impl::setPropertyValueByIndex(const Any& obj, sal_Int32 nSequenceIndex, const Any& aValue) const +//void IntrospectionAccessStatic_Impl::setPropertyValueByIndex( Any& obj, sal_Int32 nSequenceIndex, const Any& aValue) const +{ + // Is the passed object something that fits? + Reference<XInterface> xInterface; + if( !(obj >>= xInterface) ) + { + TypeClass eObjType = obj.getValueType().getTypeClass(); + if( nSequenceIndex >= mnPropCount) + throw IllegalArgumentException( + "IntrospectionAccessStatic_Impl::setPropertyValueByIndex(), index > propertyCount, " + + OUString::number(nSequenceIndex) + " > " + OUString::number(mnPropCount), + Reference<XInterface>(), 0); + if( eObjType != TypeClass_STRUCT && eObjType != TypeClass_EXCEPTION ) + throw IllegalArgumentException( + "IntrospectionAccessStatic_Impl::setPropertyValueByIndex(), expected struct or exception, got" + + obj.getValueType().getTypeName(), Reference<XInterface>(), 0); + } + + // Test flags + if( (maAllPropertySeq[ nSequenceIndex ].Attributes & READONLY) != 0 ) + { + throw UnknownPropertyException( + "IntrospectionAccessStatic_Impl::setPropertyValueByIndex(), property at index " + OUString::number(nSequenceIndex) + " is readonly"); + } + + switch( maMapTypeSeq[ nSequenceIndex ] ) + { + case MAP_PROPERTY_SET: + { + // Get Property + const Property& rProp = maAllPropertySeq[ nSequenceIndex ]; + + // Convert Interface-Parameter to the correct type + bool bUseCopy = false; + Any aRealValue; + + if( auto valInterface = o3tl::tryAccess< + css::uno::Reference<css::uno::XInterface>>(aValue) ) + { + Type aPropType = rProp.Type; + OUString aTypeName( aPropType.getTypeName() ); + Reference< XIdlClass > xPropClass = mxCoreReflection->forName( aTypeName ); + //Reference<XIdlClass> xPropClass = rProp.Type; + if( xPropClass.is() && xPropClass->getTypeClass() == TypeClass_INTERFACE ) + { + if( valInterface->is() ) + { + //Any queryInterface( const Type& rType ); + aRealValue = (*valInterface)->queryInterface( aPropType ); + if( aRealValue.hasValue() ) + bUseCopy = true; + } + } + } + + // Do we have a FastPropertySet and a valid Handle? + // CAUTION: At this point we exploit that the PropertySet + // gets queried at the beginning of the Introspection-Process. + sal_Int32 nOrgHandle; + if( mbFastPropSet && ( nOrgHandle = mpOrgPropertyHandleArray[ nSequenceIndex ] ) != -1 ) + { + // Retrieve PropertySet-Interface + Reference<XFastPropertySet> xFastPropSet = + Reference<XFastPropertySet>::query( xInterface ); + if( xFastPropSet.is() ) + { + xFastPropSet->setFastPropertyValue( nOrgHandle, bUseCopy ? aRealValue : aValue ); + } + else + { + // throw UnknownPropertyException + } + } + // else take the normal one + else + { + // Retrieve PropertySet-Interface + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( xInterface ); + if( xPropSet.is() ) + { + xPropSet->setPropertyValue( rProp.Name, bUseCopy ? aRealValue : aValue ); + } + else + { + // throw UnknownPropertyException + } + } + } + break; + + case MAP_FIELD: + { + Reference<XIdlField> xField = static_cast<XIdlField*>(aInterfaceSeq1[ nSequenceIndex ].get()); + Reference<XIdlField2> xField2(xField, UNO_QUERY); + if( xField2.is() ) + { + xField2->set( const_cast<Any&>(obj), aValue ); + // IllegalArgumentException + // NullPointerException + } else + if( xField.is() ) + { + xField->set( obj, aValue ); + // IllegalArgumentException + // NullPointerException + } + else + { + // throw IllegalArgumentException(); + } + } + break; + + case MAP_GETSET: + case MAP_SETONLY: + { + // Fetch set method + Reference<XIdlMethod> xMethod = static_cast<XIdlMethod*>(aInterfaceSeq2[ nSequenceIndex ].get()); + if( xMethod.is() ) + { + Sequence<Any> args( 1 ); + args.getArray()[0] = aValue; + xMethod->invoke( obj, args ); + } + else + { + // throw IllegalArgumentException(); + } + } + break; + } +} + +Any IntrospectionAccessStatic_Impl::getPropertyValue( const Any& obj, const OUString& aPropertyName ) const +{ + sal_Int32 i = getPropertyIndex( aPropertyName ); + if( i != -1 ) + return getPropertyValueByIndex( obj, i ); + + throw UnknownPropertyException(aPropertyName); +} + +Any IntrospectionAccessStatic_Impl::getPropertyValueByIndex(const Any& obj, sal_Int32 nSequenceIndex) const +{ + Any aRet; + + // Is there anything suitable in the passed object? + Reference<XInterface> xInterface; + if( !(obj >>= xInterface) ) + { + TypeClass eObjType = obj.getValueType().getTypeClass(); + if( nSequenceIndex >= mnPropCount || ( eObjType != TypeClass_STRUCT && eObjType != TypeClass_EXCEPTION ) ) + { + // throw IllegalArgumentException(); + return aRet; + } + } + + switch( maMapTypeSeq[ nSequenceIndex ] ) + { + case MAP_PROPERTY_SET: + { + // Acquire property + const Property& rProp = maAllPropertySeq[ nSequenceIndex ]; + + // Do we have a FastPropertySet and a valid handle? + // NOTE: At this point is exploited that the PropertySet + // is queried at the beginning of introspection process. + sal_Int32 nOrgHandle; + if( mbFastPropSet && ( nOrgHandle = mpOrgPropertyHandleArray[ nSequenceIndex ] ) != -1 ) + { + // Fetch the PropertySet interface + Reference<XFastPropertySet> xFastPropSet = + Reference<XFastPropertySet>::query( xInterface ); + if( xFastPropSet.is() ) + { + aRet = xFastPropSet->getFastPropertyValue( nOrgHandle); + } + else + { + // throw UnknownPropertyException + return aRet; + } + } + // Otherwise use the normal one + else + { + // Fetch the PropertySet interface + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( xInterface ); + if( xPropSet.is() ) + { + aRet = xPropSet->getPropertyValue( rProp.Name ); + } + else + { + // throw UnknownPropertyException + return aRet; + } + } + } + break; + + case MAP_FIELD: + { + Reference<XIdlField> xField = static_cast<XIdlField*>(aInterfaceSeq1[ nSequenceIndex ].get()); + if( xField.is() ) + { + aRet = xField->get( obj ); + // IllegalArgumentException + // NullPointerException + } + else + { + // throw IllegalArgumentException(); + return aRet; + } + } + break; + + case MAP_GETSET: + { + // Fetch get method + Reference<XIdlMethod> xMethod = static_cast<XIdlMethod*>(aInterfaceSeq1[ nSequenceIndex ].get()); + if( xMethod.is() ) + { + Sequence<Any> args; + aRet = xMethod->invoke( obj, args ); + } + else + { + // throw IllegalArgumentException(); + return aRet; + } + } + break; + + case MAP_SETONLY: + // Get method does not exist + // throw WriteOnlyPropertyException(); + return aRet; + } + return aRet; +} + + +// Helper method to adjust the size of the vectors +void IntrospectionAccessStatic_Impl::checkPropertyArraysSize( sal_Int32 iNextIndex ) +{ + sal_Int32 nLen = static_cast<sal_Int32>(maAllPropertySeq.size()); + if( iNextIndex >= nLen ) + { + maAllPropertySeq.resize( nLen + ARRAY_SIZE_STEP ); + maMapTypeSeq.resize( nLen + ARRAY_SIZE_STEP ); + maPropertyConceptSeq.resize( nLen + ARRAY_SIZE_STEP ); + } +} + +void IntrospectionAccessStatic_Impl::checkInterfaceArraySize( std::vector< Reference<XInterface> >& rSeq, + std::vector<Reference<XInterface>>& rInterfaceVec, sal_Int32 iNextIndex ) +{ + sal_Int32 nLen = rSeq.size(); + if( iNextIndex >= nLen ) + { + // Synchronize new size with ARRAY_SIZE_STEP + sal_Int32 nMissingSize = iNextIndex - nLen + 1; + sal_Int32 nSteps = nMissingSize / ARRAY_SIZE_STEP + 1; + sal_Int32 nNewSize = nLen + nSteps * ARRAY_SIZE_STEP; + + rSeq.resize( nNewSize ); + rInterfaceVec = rSeq; + } +} + + +//*** ImplIntrospectionAccess *** + + +// New Impl class as part of the introspection conversion to instance-bound +// Introspection with property access via XPropertySet. The old class +// ImplIntrospectionAccess lives on as IntrospectionAccessStatic_Impl +class ImplIntrospectionAccess : public IntrospectionAccessHelper +{ + friend class Implementation; + + // Object under examination + Any maInspectedObject; + + // As interface + Reference<XInterface> mxIface; + + // Static introspection data + rtl::Reference< IntrospectionAccessStatic_Impl > mpStaticImpl; + + // Last Sequence that came with getProperties (optimization) + Sequence<Property> maLastPropertySeq; + sal_Int32 mnLastPropertyConcept; + + // Last Sequence that came with getMethods (optimization) + Sequence<Reference<XIdlMethod> > maLastMethodSeq; + sal_Int32 mnLastMethodConcept; + + // Guards the caching of queried interfaces + std::mutex m_aMutex; + + // Original interfaces of the objects + Reference<XElementAccess> mxObjElementAccess; + Reference<XNameContainer> mxObjNameContainer; + Reference<XNameReplace> mxObjNameReplace; + Reference<XNameAccess> mxObjNameAccess; + Reference<XIndexContainer> mxObjIndexContainer; + Reference<XIndexReplace> mxObjIndexReplace; + Reference<XIndexAccess> mxObjIndexAccess; + Reference<XEnumerationAccess> mxObjEnumerationAccess; + Reference<XIdlArray> mxObjIdlArray; + + Reference<XElementAccess> getXElementAccess(); + Reference<XNameContainer> getXNameContainer(); + Reference<XNameReplace> getXNameReplace(); + Reference<XNameAccess> getXNameAccess(); + Reference<XIndexContainer> getXIndexContainer(); + Reference<XIndexReplace> getXIndexReplace(); + Reference<XIndexAccess> getXIndexAccess(); + Reference<XEnumerationAccess> getXEnumerationAccess(); + Reference<XIdlArray> getXIdlArray(); + + void cacheXNameContainer(); + void cacheXIndexContainer(); + +public: + ImplIntrospectionAccess( const Any& obj, rtl::Reference< IntrospectionAccessStatic_Impl > const & pStaticImpl_ ); + + // Methods from XIntrospectionAccess + virtual sal_Int32 SAL_CALL getSuppliedMethodConcepts() override; + virtual sal_Int32 SAL_CALL getSuppliedPropertyConcepts() override; + virtual Property SAL_CALL getProperty(const OUString& Name, sal_Int32 PropertyConcepts) override; + virtual sal_Bool SAL_CALL hasProperty(const OUString& Name, sal_Int32 PropertyConcepts) override; + virtual Sequence< Property > SAL_CALL getProperties(sal_Int32 PropertyConcepts) override; + virtual Reference<XIdlMethod> SAL_CALL getMethod(const OUString& Name, sal_Int32 MethodConcepts) override; + virtual sal_Bool SAL_CALL hasMethod(const OUString& Name, sal_Int32 MethodConcepts) override; + virtual Sequence< Reference<XIdlMethod> > SAL_CALL getMethods(sal_Int32 MethodConcepts) override; + virtual Sequence< Type > SAL_CALL getSupportedListeners() override; + using OWeakObject::queryAdapter; + virtual Reference<XInterface> SAL_CALL queryAdapter( const Type& rType ) override; + + // Methods from XMaterialHolder + virtual Any SAL_CALL getMaterial() override; + + // Methods from XExactName + virtual OUString SAL_CALL getExactName( const OUString& rApproximateName ) override; + + // Methods from XInterface + virtual Any SAL_CALL queryInterface( const Type& rType ) override; + virtual void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override { OWeakObject::release(); } + + // Methods from XPropertySet + virtual Reference<XPropertySetInfo> SAL_CALL getPropertySetInfo() override; + virtual void SAL_CALL setPropertyValue(const OUString& aPropertyName, const Any& aValue) override; + virtual Any SAL_CALL getPropertyValue(const OUString& aPropertyName) override; + virtual void SAL_CALL addPropertyChangeListener(const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener) override; + virtual void SAL_CALL removePropertyChangeListener(const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener) override; + virtual void SAL_CALL addVetoableChangeListener(const OUString& aPropertyName, const Reference<XVetoableChangeListener>& aListener) override; + virtual void SAL_CALL removeVetoableChangeListener(const OUString& aPropertyName, const Reference<XVetoableChangeListener>& aListener) override; + + // Methods from XFastPropertySet + virtual void SAL_CALL setFastPropertyValue(sal_Int32 nHandle, const Any& aValue) override; + virtual Any SAL_CALL getFastPropertyValue(sal_Int32 nHandle) override; + + // Methods from XPropertySetInfo + virtual Sequence< Property > SAL_CALL getProperties() override; + virtual Property SAL_CALL getPropertyByName(const OUString& Name) override; + virtual sal_Bool SAL_CALL hasPropertyByName(const OUString& Name) override; + + // Methods from XElementAccess + virtual Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // Methods from XNameAccess + virtual Any SAL_CALL getByName(const OUString& Name) override; + virtual Sequence< OUString > SAL_CALL getElementNames() override; + virtual sal_Bool SAL_CALL hasByName(const OUString& Name) override; + + // Methods from XNameReplace + virtual void SAL_CALL replaceByName(const OUString& Name, const Any& Element) override; + + // Methods from XNameContainer + virtual void SAL_CALL insertByName(const OUString& Name, const Any& Element) override; + virtual void SAL_CALL removeByName(const OUString& Name) override; + + // Methods from XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override; + virtual Any SAL_CALL getByIndex(sal_Int32 Index) override; + + // Methods from XIndexReplace + virtual void SAL_CALL replaceByIndex(sal_Int32 Index, const Any& Element) override; + + // Methods from XIndexContainer + virtual void SAL_CALL insertByIndex(sal_Int32 Index, const Any& Element) override; + virtual void SAL_CALL removeByIndex(sal_Int32 Index) override; + + // Methods from XEnumerationAccess + virtual Reference<XEnumeration> SAL_CALL createEnumeration() override; + + // Methods from XIdlArray + virtual void SAL_CALL realloc(Any& array, sal_Int32 length) override; + virtual sal_Int32 SAL_CALL getLen(const Any& array) override; + virtual Any SAL_CALL get(const Any& array, sal_Int32 index) override; + virtual void SAL_CALL set(Any& array, sal_Int32 index, const Any& value) override; + + // Methods from XUnoTunnel + virtual sal_Int64 SAL_CALL getSomething( const Sequence< sal_Int8 >& aIdentifier ) override; +}; + +ImplIntrospectionAccess::ImplIntrospectionAccess + ( const Any& obj, rtl::Reference< IntrospectionAccessStatic_Impl > const & pStaticImpl_ ) + : maInspectedObject( obj ), mpStaticImpl( pStaticImpl_ ) , + mnLastPropertyConcept(-1), mnLastMethodConcept(-1) //, maAdapter() +{ + // Save object as an interface if possible + maInspectedObject >>= mxIface; +} + +Reference<XElementAccess> ImplIntrospectionAccess::getXElementAccess() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjElementAccess.is() ) + { + aGuard.unlock(); + Reference<XElementAccess> xElementAccess( mxIface, UNO_QUERY ); + aGuard.lock(); + if( !mxObjElementAccess.is() ) + mxObjElementAccess = xElementAccess; + } + return mxObjElementAccess; +} + +void ImplIntrospectionAccess::cacheXNameContainer() +{ + Reference<XNameContainer> xNameContainer; + Reference<XNameReplace> xNameReplace; + Reference<XNameAccess> xNameAccess; + if (mpStaticImpl->mbNameContainer) + { + xNameContainer.set( mxIface, UNO_QUERY ); + xNameReplace = xNameContainer; + xNameAccess = xNameContainer; + } + else if (mpStaticImpl->mbNameReplace) + { + xNameReplace.set( mxIface, UNO_QUERY ); + xNameAccess = xNameReplace; + } + else if (mpStaticImpl->mbNameAccess) + { + xNameAccess.set( mxIface, UNO_QUERY ); + } + + { + std::unique_lock aGuard( m_aMutex ); + if( !mxObjNameContainer.is() ) + mxObjNameContainer = xNameContainer; + if( !mxObjNameReplace.is() ) + mxObjNameReplace = xNameReplace; + if( !mxObjNameAccess.is() ) + mxObjNameAccess = xNameAccess; + } +} + +Reference<XNameContainer> ImplIntrospectionAccess::getXNameContainer() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjNameContainer.is() ) + { + aGuard.unlock(); + cacheXNameContainer(); + } + return mxObjNameContainer; +} + +Reference<XNameReplace> ImplIntrospectionAccess::getXNameReplace() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjNameReplace.is() ) + { + aGuard.unlock(); + cacheXNameContainer(); + } + return mxObjNameReplace; +} + +Reference<XNameAccess> ImplIntrospectionAccess::getXNameAccess() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjNameAccess.is() ) + { + aGuard.unlock(); + cacheXNameContainer(); + } + return mxObjNameAccess; +} + +void ImplIntrospectionAccess::cacheXIndexContainer() +{ + Reference<XIndexContainer> xIndexContainer; + Reference<XIndexReplace> xIndexReplace; + Reference<XIndexAccess> xIndexAccess; + if (mpStaticImpl->mbIndexContainer) + { + xIndexContainer.set( mxIface, UNO_QUERY ); + xIndexReplace = xIndexContainer; + xIndexAccess = xIndexContainer; + } + else if (mpStaticImpl->mbIndexReplace) + { + xIndexReplace.set( mxIface, UNO_QUERY ); + xIndexAccess = xIndexReplace; + } + else if (mpStaticImpl->mbIndexAccess) + { + xIndexAccess.set( mxIface, UNO_QUERY ); + } + + { + std::unique_lock aGuard( m_aMutex ); + if( !mxObjIndexContainer.is() ) + mxObjIndexContainer = xIndexContainer; + if( !mxObjIndexReplace.is() ) + mxObjIndexReplace = xIndexReplace; + if( !mxObjIndexAccess.is() ) + mxObjIndexAccess = xIndexAccess; + } +} + +Reference<XIndexContainer> ImplIntrospectionAccess::getXIndexContainer() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjIndexContainer.is() ) + { + aGuard.unlock(); + cacheXIndexContainer(); + } + return mxObjIndexContainer; +} + +Reference<XIndexReplace> ImplIntrospectionAccess::getXIndexReplace() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjIndexReplace.is() ) + { + aGuard.unlock(); + cacheXIndexContainer(); + } + return mxObjIndexReplace; +} + +Reference<XIndexAccess> ImplIntrospectionAccess::getXIndexAccess() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjIndexAccess.is() ) + { + aGuard.unlock(); + cacheXIndexContainer(); + } + return mxObjIndexAccess; +} + +Reference<XEnumerationAccess> ImplIntrospectionAccess::getXEnumerationAccess() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjEnumerationAccess.is() ) + { + aGuard.unlock(); + Reference<XEnumerationAccess> xEnumerationAccess( mxIface, UNO_QUERY ); + aGuard.lock(); + if( !mxObjEnumerationAccess.is() ) + mxObjEnumerationAccess = xEnumerationAccess; + } + return mxObjEnumerationAccess; +} + +Reference<XIdlArray> ImplIntrospectionAccess::getXIdlArray() +{ + std::unique_lock aGuard( m_aMutex ); + + if( !mxObjIdlArray.is() ) + { + aGuard.unlock(); + Reference<XIdlArray> xIdlArray( mxIface, UNO_QUERY ); + aGuard.lock(); + if( !mxObjIdlArray.is() ) + mxObjIdlArray = xIdlArray; + } + return mxObjIdlArray; +} + +// Methods from XInterface +Any SAL_CALL ImplIntrospectionAccess::queryInterface( const Type& rType ) +{ + Any aRet( ::cppu::queryInterface( + rType, + static_cast< XIntrospectionAccess * >( this ), + static_cast< XMaterialHolder * >( this ), + static_cast< XExactName * >( this ), + static_cast< XPropertySet * >( this ), + static_cast< XFastPropertySet * >( this ), + static_cast< XPropertySetInfo * >( this ) ) ); + if( !aRet.hasValue() ) + aRet = OWeakObject::queryInterface( rType ); + + if( !aRet.hasValue() ) + { + // Wrapper for the object interfaces + ( mpStaticImpl->mbElementAccess && (aRet = ::cppu::queryInterface + ( rType, static_cast< XElementAccess* >( static_cast< XNameAccess* >( this ) ) ) ).hasValue() ) + || ( mpStaticImpl->mbNameAccess && (aRet = ::cppu::queryInterface( rType, static_cast< XNameAccess* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbNameReplace && (aRet = ::cppu::queryInterface( rType, static_cast< XNameReplace* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbNameContainer && (aRet = ::cppu::queryInterface( rType, static_cast< XNameContainer* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbIndexAccess && (aRet = ::cppu::queryInterface( rType, static_cast< XIndexAccess* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbIndexReplace && (aRet = ::cppu::queryInterface( rType, static_cast< XIndexReplace* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbIndexContainer && (aRet = ::cppu::queryInterface( rType, static_cast< XIndexContainer* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbEnumerationAccess && (aRet = ::cppu::queryInterface( rType, static_cast< XEnumerationAccess* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbIdlArray && (aRet = ::cppu::queryInterface( rType, static_cast< XIdlArray* >( this ) ) ).hasValue() ) + || ( mpStaticImpl->mbUnoTunnel && (aRet = ::cppu::queryInterface( rType, static_cast< XUnoTunnel* >( this ) ) ).hasValue() ); + } + return aRet; +} + + +//*** Implementation of ImplIntrospectionAdapter *** + + +// Methods from XPropertySet +Reference<XPropertySetInfo> ImplIntrospectionAccess::getPropertySetInfo() +{ + return static_cast<XPropertySetInfo *>(this); +} + +void ImplIntrospectionAccess::setPropertyValue(const OUString& aPropertyName, const Any& aValue) +{ + mpStaticImpl->setPropertyValue( maInspectedObject, aPropertyName, aValue ); +} + +Any ImplIntrospectionAccess::getPropertyValue(const OUString& aPropertyName) +{ + return mpStaticImpl->getPropertyValue( maInspectedObject, aPropertyName ); +} + +void ImplIntrospectionAccess::addPropertyChangeListener(const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener) +{ + if( mxIface.is() ) + { + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( mxIface ); + //Reference<XPropertySet> xPropSet( mxIface, USR_QUERY ); + if( xPropSet.is() ) + xPropSet->addPropertyChangeListener(aPropertyName, aListener); + } +} + +void ImplIntrospectionAccess::removePropertyChangeListener(const OUString& aPropertyName, const Reference<XPropertyChangeListener>& aListener) +{ + if( mxIface.is() ) + { + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( mxIface ); + //Reference<XPropertySet> xPropSet( mxIface, USR_QUERY ); + if( xPropSet.is() ) + xPropSet->removePropertyChangeListener(aPropertyName, aListener); + } +} + +void ImplIntrospectionAccess::addVetoableChangeListener(const OUString& aPropertyName, const Reference<XVetoableChangeListener>& aListener) +{ + if( mxIface.is() ) + { + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( mxIface ); + //Reference<XPropertySet> xPropSet( mxIface, USR_QUERY ); + if( xPropSet.is() ) + xPropSet->addVetoableChangeListener(aPropertyName, aListener); + } +} + +void ImplIntrospectionAccess::removeVetoableChangeListener(const OUString& aPropertyName, const Reference<XVetoableChangeListener>& aListener) +{ + if( mxIface.is() ) + { + Reference<XPropertySet> xPropSet = + Reference<XPropertySet>::query( mxIface ); + if( xPropSet.is() ) + xPropSet->removeVetoableChangeListener(aPropertyName, aListener); + } +} + + +// Methods from XFastPropertySet +void ImplIntrospectionAccess::setFastPropertyValue(sal_Int32, const Any&) +{ +} + +Any ImplIntrospectionAccess::getFastPropertyValue(sal_Int32) +{ + return Any(); +} + +// Methods from XPropertySetInfo +Sequence< Property > ImplIntrospectionAccess::getProperties() +{ + return comphelper::containerToSequence(mpStaticImpl->getProperties()); +} + +Property ImplIntrospectionAccess::getPropertyByName(const OUString& Name) +{ + return getProperty( Name, PropertyConcept::ALL ); +} + +sal_Bool ImplIntrospectionAccess::hasPropertyByName(const OUString& Name) +{ + return hasProperty( Name, PropertyConcept::ALL ); +} + +// Methods from XElementAccess +Type ImplIntrospectionAccess::getElementType() +{ + return getXElementAccess()->getElementType(); +} + +sal_Bool ImplIntrospectionAccess::hasElements() +{ + return getXElementAccess()->hasElements(); +} + +// Methods from XNameAccess +Any ImplIntrospectionAccess::getByName(const OUString& Name) +{ + return getXNameAccess()->getByName( Name ); +} + +Sequence< OUString > ImplIntrospectionAccess::getElementNames() +{ + return getXNameAccess()->getElementNames(); +} + +sal_Bool ImplIntrospectionAccess::hasByName(const OUString& Name) +{ + return getXNameAccess()->hasByName( Name ); +} + +// Methods from XNameContainer +void ImplIntrospectionAccess::insertByName(const OUString& Name, const Any& Element) +{ + getXNameContainer()->insertByName( Name, Element ); +} + +void ImplIntrospectionAccess::replaceByName(const OUString& Name, const Any& Element) +{ + getXNameReplace()->replaceByName( Name, Element ); +} + +void ImplIntrospectionAccess::removeByName(const OUString& Name) +{ + getXNameContainer()->removeByName( Name ); +} + +// Methods from XIndexAccess +// Already in XNameAccess: virtual Reference<XIdlClass> getElementType() const +sal_Int32 ImplIntrospectionAccess::getCount() +{ + return getXIndexAccess()->getCount(); +} + +Any ImplIntrospectionAccess::getByIndex(sal_Int32 Index) +{ + return getXIndexAccess()->getByIndex( Index ); +} + +// Methods from XIndexContainer +void ImplIntrospectionAccess::insertByIndex(sal_Int32 Index, const Any& Element) +{ + getXIndexContainer()->insertByIndex( Index, Element ); +} + +void ImplIntrospectionAccess::replaceByIndex(sal_Int32 Index, const Any& Element) +{ + getXIndexReplace()->replaceByIndex( Index, Element ); +} + +void ImplIntrospectionAccess::removeByIndex(sal_Int32 Index) +{ + getXIndexContainer()->removeByIndex( Index ); +} + +// Methods from XEnumerationAccess +// Already in XNameAccess: virtual Reference<XIdlClass> getElementType() const; +Reference<XEnumeration> ImplIntrospectionAccess::createEnumeration() +{ + return getXEnumerationAccess()->createEnumeration(); +} + +// Methods from XIdlArray +void ImplIntrospectionAccess::realloc(Any& array, sal_Int32 length) +{ + getXIdlArray()->realloc( array, length ); +} + +sal_Int32 ImplIntrospectionAccess::getLen(const Any& array) +{ + return getXIdlArray()->getLen( array ); +} + +Any ImplIntrospectionAccess::get(const Any& array, sal_Int32 index) +{ + return getXIdlArray()->get( array, index ); +} + +void ImplIntrospectionAccess::set(Any& array, sal_Int32 index, const Any& value) +{ + getXIdlArray()->set( array, index, value ); +} + +// Methods from XUnoTunnel +sal_Int64 ImplIntrospectionAccess::getSomething( const Sequence< sal_Int8 >& aIdentifier ) +{ + if (Reference<XUnoTunnel> xUnoTunnel{ mxIface, css::uno::UNO_QUERY }) + return xUnoTunnel->getSomething(aIdentifier); + return 0; +} + + +//*** Implementation of ImplIntrospectionAccess *** + +// Methods from XIntrospectionAccess +sal_Int32 ImplIntrospectionAccess::getSuppliedMethodConcepts() +{ + return MethodConcept::DANGEROUS | + PROPERTY | + LISTENER | + ENUMERATION | + NAMECONTAINER | + INDEXCONTAINER; +} + +sal_Int32 ImplIntrospectionAccess::getSuppliedPropertyConcepts() +{ + return PropertyConcept::DANGEROUS | + PROPERTYSET | + ATTRIBUTES | + METHODS; +} + +Property ImplIntrospectionAccess::getProperty(const OUString& Name, sal_Int32 PropertyConcepts) +{ + Property aRet; + sal_Int32 i = mpStaticImpl->getPropertyIndex( Name ); + bool bFound = false; + if( i != -1 ) + { + sal_Int32 nConcept = mpStaticImpl->getPropertyConcepts()[ i ]; + if( (PropertyConcepts & nConcept) != 0 ) + { + aRet = mpStaticImpl->getProperties()[ i ]; + bFound = true; + } + } + if( !bFound ) + throw NoSuchElementException(Name); + return aRet; +} + +sal_Bool ImplIntrospectionAccess::hasProperty(const OUString& Name, sal_Int32 PropertyConcepts) +{ + sal_Int32 i = mpStaticImpl->getPropertyIndex( Name ); + bool bRet = false; + if( i != -1 ) + { + sal_Int32 nConcept = mpStaticImpl->getPropertyConcepts()[ i ]; + if( (PropertyConcepts & nConcept) != 0 ) + bRet = true; + } + return bRet; +} + +Sequence< Property > ImplIntrospectionAccess::getProperties(sal_Int32 PropertyConcepts) +{ + // If all supported concepts are required, simply pass through the sequence + sal_Int32 nAllSupportedMask = PROPERTYSET | + ATTRIBUTES | + METHODS; + if( ( PropertyConcepts & nAllSupportedMask ) == nAllSupportedMask ) + { + return comphelper::containerToSequence(mpStaticImpl->getProperties()); + } + + // Same sequence as last time? + if( mnLastPropertyConcept == PropertyConcepts ) + { + return maLastPropertySeq; + } + + // Number of properties to be delivered + sal_Int32 nCount = 0; + + // There are currently no DANGEROUS properties + // if( PropertyConcepts & DANGEROUS ) + // nCount += mpStaticImpl->mnDangerousPropCount; + if( PropertyConcepts & PROPERTYSET ) + nCount += mpStaticImpl->mnPropertySetPropCount; + if( PropertyConcepts & ATTRIBUTES ) + nCount += mpStaticImpl->mnAttributePropCount; + if( PropertyConcepts & METHODS ) + nCount += mpStaticImpl->mnMethodPropCount; + + // Realloc sequence according to the required number + maLastPropertySeq.realloc( nCount ); + Property* pDestProps = maLastPropertySeq.getArray(); + + // Go through all the properties and apply according to the concept + const std::vector<Property>& rPropSeq = mpStaticImpl->getProperties(); + const std::vector<sal_Int32>& rConcepts = mpStaticImpl->getPropertyConcepts(); + sal_Int32 nLen = static_cast<sal_Int32>(rPropSeq.size()); + + sal_Int32 iDest = 0; + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + sal_Int32 nConcept = rConcepts[ i ]; + if( nConcept & PropertyConcepts ) + pDestProps[ iDest++ ] = rPropSeq[ i ]; + } + + // Remember PropertyConcept representing maLastPropertySeq + mnLastPropertyConcept = PropertyConcepts; + + // Supply assembled Sequence + return maLastPropertySeq; +} + +Reference<XIdlMethod> ImplIntrospectionAccess::getMethod(const OUString& Name, sal_Int32 MethodConcepts) +{ + Reference<XIdlMethod> xRet; + sal_Int32 i = mpStaticImpl->getMethodIndex( Name ); + if( i != -1 ) + { + + sal_Int32 nConcept = mpStaticImpl->getMethodConcepts()[ i ]; + if( (MethodConcepts & nConcept) != 0 ) + { + xRet = mpStaticImpl->getMethods()[i]; + } + } + if( !xRet.is() ) + throw NoSuchMethodException(Name); + return xRet; +} + +sal_Bool ImplIntrospectionAccess::hasMethod(const OUString& Name, sal_Int32 MethodConcepts) +{ + sal_Int32 i = mpStaticImpl->getMethodIndex( Name ); + bool bRet = false; + if( i != -1 ) + { + sal_Int32 nConcept = mpStaticImpl->getMethodConcepts()[ i ]; + if( (MethodConcepts & nConcept) != 0 ) + bRet = true; + } + return bRet; +} + +Sequence< Reference<XIdlMethod> > ImplIntrospectionAccess::getMethods(sal_Int32 MethodConcepts) +{ + // If all supported concepts are required, simply pass through the sequence + sal_Int32 nAllSupportedMask = MethodConcept::DANGEROUS | + PROPERTY | + LISTENER | + ENUMERATION | + NAMECONTAINER | + INDEXCONTAINER | + MethodConcept_NORMAL_IMPL; + if( ( MethodConcepts & nAllSupportedMask ) == nAllSupportedMask ) + { + return comphelper::containerToSequence(mpStaticImpl->getMethods()); + } + + // Same sequence as last time? + if( mnLastMethodConcept == MethodConcepts ) + { + return maLastMethodSeq; + } + + // Get method sequences + const std::vector< Reference<XIdlMethod> >& aMethodSeq = mpStaticImpl->getMethods(); + sal_Int32 nLen = static_cast<sal_Int32>(aMethodSeq.size()); + + // Realloc sequence according to the required number + // Unlike Properties, the number can not be determined by counters in + // inspect() beforehand, since methods can belong to several concepts + maLastMethodSeq.realloc( nLen ); + Reference<XIdlMethod>* pDestMethods = maLastMethodSeq.getArray(); + + // Go through all the methods and apply according to the concept + sal_Int32 iDest = 0; + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + sal_Int32 nConcept = mpStaticImpl->getMethodConcepts()[ i ]; + if( nConcept & MethodConcepts ) + pDestMethods[ iDest++ ] = aMethodSeq[ i ]; + } + + // Bring to the correct length + maLastMethodSeq.realloc( iDest ); + + // Remember MethodConcept representing maLastMethodSeq + mnLastMethodConcept = MethodConcepts; + + // Supply assembled Sequence + return maLastMethodSeq; +} + +Sequence< Type > ImplIntrospectionAccess::getSupportedListeners() +{ + return comphelper::containerToSequence(mpStaticImpl->getSupportedListeners()); +} + +Reference<XInterface> SAL_CALL ImplIntrospectionAccess::queryAdapter( const Type& rType ) +{ + Reference<XInterface> xRet; + if( rType == cppu::UnoType<XInterface>::get() + || rType == cppu::UnoType<XPropertySet>::get() + || rType == cppu::UnoType<XFastPropertySet>::get() + || rType == cppu::UnoType<XPropertySetInfo>::get() + || rType == cppu::UnoType<XElementAccess>::get() + || rType == cppu::UnoType<XNameAccess>::get() + || rType == cppu::UnoType<XNameReplace>::get() + || rType == cppu::UnoType<XNameContainer>::get() + || rType == cppu::UnoType<XIndexAccess>::get() + || rType == cppu::UnoType<XIndexReplace>::get() + || rType == cppu::UnoType<XIndexContainer>::get() + || rType == cppu::UnoType<XEnumerationAccess>::get() + || rType == cppu::UnoType<XIdlArray>::get() + || rType == cppu::UnoType<XUnoTunnel>::get() ) + { + queryInterface( rType ) >>= xRet; + } + return xRet; +} + +// Methods from XMaterialHolder +Any ImplIntrospectionAccess::getMaterial() +{ + return maInspectedObject; +} + +// Methods from XExactName +OUString ImplIntrospectionAccess::getExactName( const OUString& rApproximateName ) +{ + OUString aRetStr; + LowerToExactNameMap::iterator aIt = + mpStaticImpl->maLowerToExactNameMap.find( rApproximateName.toAsciiLowerCase() ); + if (aIt != mpStaticImpl->maLowerToExactNameMap.end()) + aRetStr = (*aIt).second; + return aRetStr; +} + +struct TypeKey { + TypeKey( + css::uno::Reference<css::beans::XPropertySetInfo> const & theProperties, + std::vector<css::uno::Type> const & theTypes): + properties(theProperties) + { + //TODO: Could even sort the types lexicographically first, to increase + // the chance of matches between different implementations' getTypes(), + // but the old scheme of using getImplementationId() would have missed + // those matches, too: + OUStringBuffer b(static_cast<int>(theTypes.size() * 64)); + for (const css::uno::Type& rType : theTypes) { + b.append(rType.getTypeName()); + b.append('*'); // arbitrary delimiter not used by type grammar + } + types = b.makeStringAndClear(); + } + + css::uno::Reference<css::beans::XPropertySetInfo> properties; + OUString types; +}; + +struct TypeKeyLess { + bool operator ()(TypeKey const & key1, TypeKey const & key2) const { + if (key1.properties.get() < key2.properties.get()) { + return true; + } + if (key1.properties.get() > key2.properties.get()) { + return false; + } + return key1.types < key2.types; + } +}; + +template<typename Key, typename Less> class Cache { +public: + rtl::Reference<IntrospectionAccessStatic_Impl> find(Key const & key) const { + typename Map::const_iterator i(map_.find(key)); + if (i == map_.end()) { + return rtl::Reference<IntrospectionAccessStatic_Impl>(); + } else { + if (i->second.hits < std::numeric_limits<unsigned>::max()) { + ++i->second.hits; + } + assert(i->second.access.is()); + return i->second.access; + } + } + + void insert( + Key const & key, + rtl::Reference<IntrospectionAccessStatic_Impl> const & access) + { + assert(access.is()); + typename Map::size_type const MAX = 100; + assert(map_.size() <= MAX); + if (map_.size() == MAX) { + typename Map::iterator del = std::min_element(map_.begin(), map_.end(), + [](const typename Map::value_type& a, const typename Map::value_type& b) { + return a.second.hits < b.second.hits; + }); + map_.erase(del); + } + bool ins = map_.emplace(key, Data(access)).second; + assert(ins); (void)ins; + } + + void clear() { map_.clear(); } + +private: + struct Data { + explicit Data( + rtl::Reference<IntrospectionAccessStatic_Impl> const & theAccess): + access(theAccess), hits(1) + {} + + rtl::Reference<IntrospectionAccessStatic_Impl> access; + mutable unsigned hits; + }; + + typedef std::map<Key, Data, Less> Map; + + Map map_; +}; + +typedef + cppu::WeakComponentImplHelper< + css::lang::XServiceInfo, css::beans::XIntrospection> + Implementation_Base; + +class Implementation: private cppu::BaseMutex, public Implementation_Base { +public: + explicit Implementation( + css::uno::Reference<css::uno::XComponentContext> const & context): + Implementation_Base(m_aMutex), + reflection_(css::reflection::theCoreReflection::get(context)) + {} + +private: + virtual void SAL_CALL disposing() override { + osl::MutexGuard g(m_aMutex); + reflection_.clear(); + typeCache_.clear(); + } + + virtual OUString SAL_CALL getImplementationName() override + { return "com.sun.star.comp.stoc.Introspection"; } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { return cppu::supportsService(this, ServiceName); } + + virtual css::uno::Sequence<OUString> SAL_CALL + getSupportedServiceNames() override + { + Sequence<OUString> s { "com.sun.star.beans.Introspection" }; + return s; + } + + virtual css::uno::Reference<css::beans::XIntrospectionAccess> SAL_CALL + inspect(css::uno::Any const & aObject) override; + + css::uno::Reference<css::reflection::XIdlReflection> reflection_; + Cache<TypeKey, TypeKeyLess> typeCache_; +}; + +css::uno::Reference<css::beans::XIntrospectionAccess> Implementation::inspect( + css::uno::Any const & aObject) +{ + css::uno::Reference<css::reflection::XIdlReflection> reflection; + { + osl::MutexGuard g(m_aMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) { + throw css::lang::DisposedException( + getImplementationName(), static_cast<OWeakObject *>(this)); + } + reflection = reflection_; + } + css::uno::Any aToInspectObj; + css::uno::Type t; + if (aObject >>= t) { + css::uno::Reference<css::reflection::XIdlClass> c( + reflection->forName(t.getTypeName())); + if (!c.is()) { + SAL_WARN("stoc", "cannot reflect type " << t.getTypeName()); + return css::uno::Reference<css::beans::XIntrospectionAccess>(); + } + aToInspectObj <<= c; + } else { + aToInspectObj = aObject; + } + + // Examine object + TypeClass eType = aToInspectObj.getValueType().getTypeClass(); + if( eType != TypeClass_INTERFACE && eType != TypeClass_STRUCT && eType != TypeClass_EXCEPTION ) + return css::uno::Reference<css::beans::XIntrospectionAccess>(); + + if( auto x = o3tl::tryAccess<Reference<XInterface>>(aToInspectObj) ) + { + if( !x->is() ) + return css::uno::Reference<css::beans::XIntrospectionAccess>(); + } + + // Pointer to possibly needed new IntrospectionAccessStatic_Impl instance + rtl::Reference< IntrospectionAccessStatic_Impl > pAccess; + + // Check: Is a matching access object already cached? + std::vector< Reference<XIdlClass> > SupportedClassSeq; + std::vector< Type > SupportedTypesSeq; + Reference<XTypeProvider> xTypeProvider; + Reference<XPropertySetInfo> xPropSetInfo; + Reference<XPropertySet> xPropSet; + + // Look for interfaces XTypeProvider and PropertySet + if( eType == TypeClass_INTERFACE ) + { + xTypeProvider.set( aToInspectObj, UNO_QUERY ); + if( xTypeProvider.is() ) + { + SupportedTypesSeq = comphelper::sequenceToContainer<std::vector<Type>>(xTypeProvider->getTypes()); + } else { + SAL_WARN( + "stoc", + "object of type \"" << aToInspectObj.getValueTypeName() + << "\" lacks XTypeProvider"); + SupportedTypesSeq = { aToInspectObj.getValueType() }; + } + // Now try to get the PropertySetInfo + xPropSet.set( aToInspectObj, UNO_QUERY ); + if( xPropSet.is() ) + xPropSetInfo = xPropSet->getPropertySetInfo(); + + } else { + SupportedTypesSeq = { aToInspectObj.getValueType() }; + } + + { + osl::MutexGuard g(m_aMutex); + if (rBHelper.bDisposed || rBHelper.bInDispose) { + throw css::lang::DisposedException( + getImplementationName(), static_cast<OWeakObject *>(this)); + } + TypeKey key(xPropSetInfo, SupportedTypesSeq); + pAccess = typeCache_.find(key); + if (pAccess.is()) { + return new ImplIntrospectionAccess(aToInspectObj, pAccess); + } + pAccess = new IntrospectionAccessStatic_Impl(reflection); + typeCache_.insert(key, pAccess); + } + + // No access cached -> create new + std::vector<Property>& rAllPropArray = pAccess->maAllPropertySeq; + std::vector<Reference<XInterface>>& rInterfaces1 = pAccess->aInterfaceSeq1; + std::vector<Reference<XInterface>>& rInterfaces2 = pAccess->aInterfaceSeq2; + std::vector<sal_Int16>& rMapTypeArray = pAccess->maMapTypeSeq; + std::vector<sal_Int32>& rPropertyConceptArray = pAccess->maPropertyConceptSeq; + + // References to important data from pAccess + sal_Int32& rPropCount = pAccess->mnPropCount; + IntrospectionNameMap& rPropNameMap = pAccess->maPropertyNameMap; + IntrospectionNameMap& rMethodNameMap = pAccess->maMethodNameMap; + LowerToExactNameMap& rLowerToExactNameMap = pAccess->maLowerToExactNameMap; + + + //*** Perform analysis *** + + if( eType == TypeClass_INTERFACE ) + { + size_t nTypeCount = SupportedTypesSeq.size(); + if( nTypeCount ) + { + SupportedClassSeq.resize( nTypeCount ); + + for( size_t i = 0 ; i < nTypeCount ; i++ ) + SupportedClassSeq[i] = reflection->forName( SupportedTypesSeq[i].getTypeName() ); + } + + // First look for particular interfaces that are of particular + // importance to the introspection + + // Is XPropertySet present? + if( xPropSet.is() && xPropSetInfo.is() ) + { + // Is there also a FastPropertySet? + Reference<XFastPropertySet> xDummy( aToInspectObj, UNO_QUERY ); + bool bFast = pAccess->mbFastPropSet = xDummy.is(); + + Sequence<Property> aPropSeq = xPropSetInfo->getProperties(); + const Property* pProps = aPropSeq.getConstArray(); + sal_Int32 nLen = aPropSeq.getLength(); + + // For a FastPropertySet we must remember the original handles + if( bFast ) + pAccess->mpOrgPropertyHandleArray.reset( new sal_Int32[ nLen ] ); + + for( sal_Int32 i = 0 ; i < nLen ; i++ ) + { + // Put property in its own list + pAccess->checkPropertyArraysSize( rPropCount ); + Property& rProp = rAllPropArray[ rPropCount ]; + rProp = pProps[ i ]; + + if( bFast ) + pAccess->mpOrgPropertyHandleArray[ i ] = rProp.Handle; + + // Enter PropCount as a handle for its own FastPropertySet + rProp.Handle = rPropCount; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_PROPERTY_SET; + rPropertyConceptArray[ rPropCount ] = PROPERTYSET; + pAccess->mnPropertySetPropCount++; + + // Enter name in hash table if not already known + OUString aPropName = rProp.Name; + + // Do we already have the name? + IntrospectionNameMap::iterator aIt = rPropNameMap.find( aPropName ); + if( aIt == rPropNameMap.end() ) + { + // New entry in the hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + } + else + { + SAL_WARN( "stoc", "Introspection: Property \"" << + aPropName << "\" found more than once in PropertySet" ); + } + + // Adjust count + rPropCount++; + } + } + + // Indices in the export table + sal_Int32 iAllExportedMethod = 0; + sal_Int32 iAllSupportedListener = 0; + + std::set<OUString> seen; + + // Flag, whether XInterface methods should be recorded + // (this must be done only once, allowed initially) + bool bXInterfaceIsInvalid = false; + + // Flag whether the XInterface methods have already been recorded. If + // sal_True, bXInterfaceIsInvalid is activated at the end of the interface + // loop, and XInterface methods are cut off thereafter. + bool bFoundXInterface = false; + + size_t nClassCount = SupportedClassSeq.size(); + for( size_t nIdx = 0 ; nIdx < nClassCount; nIdx++ ) + { + Reference<XIdlClass> xImplClass2 = SupportedClassSeq[nIdx]; + while( xImplClass2.is() ) + { + // Fetch interfaces from the implementation + Sequence< Reference<XIdlClass> > aClassSeq = xImplClass2->getInterfaces(); + sal_Int32 nIfaceCount = aClassSeq.getLength(); + + aClassSeq.realloc( nIfaceCount + 1 ); + aClassSeq.getArray()[ nIfaceCount ] = xImplClass2; + + for( const Reference<XIdlClass>& rxIfaceClass : std::as_const(aClassSeq) ) + { + if (!seen.insert(rxIfaceClass->getName()).second) { + continue; + } + + // 2. Register fields as properties + + // Get fields + const Sequence< Reference<XIdlField> > fields = rxIfaceClass->getFields(); + + for( const Reference<XIdlField>& xField : fields ) + { + Reference<XIdlClass> xPropType = xField->getType(); + + // Is the property sequence big enough? + pAccess->checkPropertyArraysSize( rPropCount ); + + // Enter in own property array + Property& rProp = rAllPropArray[ rPropCount ]; + OUString aFieldName = xField->getName(); + rProp.Name = aFieldName; + rProp.Handle = rPropCount; + Type aFieldType( xPropType->getTypeClass(), xPropType->getName() ); + rProp.Type = aFieldType; + FieldAccessMode eAccessMode = xField->getAccessMode(); + rProp.Attributes = (eAccessMode == FieldAccessMode_READONLY || + eAccessMode == FieldAccessMode_CONST) + ? READONLY : 0; + + // Enter name in hash table + OUString aPropName = rProp.Name; + + // Do we have the name already? + IntrospectionNameMap::iterator aIt = rPropNameMap.find( aPropName ); + if (aIt != rPropNameMap.end()) + continue; + + // New entry in the hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + + // Remember field + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq1, + rInterfaces1, rPropCount ); + rInterfaces1[ rPropCount ] = xField; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_FIELD; + rPropertyConceptArray[ rPropCount ] = ATTRIBUTES; + pAccess->mnAttributePropCount++; + + // Adjust count + rPropCount++; + } + + + // 3. Methods + + // Get and remember all methods + Sequence< Reference<XIdlMethod> > methods = rxIfaceClass->getMethods(); + const Reference<XIdlMethod>* pSourceMethods = methods.getConstArray(); + sal_Int32 nSourceMethodCount = methods.getLength(); + + // 3. a) Search get/set and listener methods + + // Create field for information about the methods, so that methods which are not + // related to properties or listeners can easily be found later. + // New: initialise MethodConceptArray + enum MethodType + { + STANDARD_METHOD, // normal method, not related to properties or listeners + GETSET_METHOD, // belongs to a get/set property + ADD_LISTENER_METHOD, // add method of a listener interface + REMOVE_LISTENER_METHOD, // remove method of a listener interface + INVALID_METHOD // method whose class is not considered, e.g. XPropertySet + }; + std::unique_ptr<MethodType[]> pMethodTypes( new MethodType[ nSourceMethodCount ] ); + std::unique_ptr<sal_Int32[]> pLocalMethodConcepts( new sal_Int32[ nSourceMethodCount ] ); + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + pMethodTypes[ i ] = STANDARD_METHOD; + pLocalMethodConcepts[ i ] = 0; + } + + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + // Address method + const Reference<XIdlMethod>& rxMethod_i = pSourceMethods[i]; + sal_Int32& rMethodConcept_i = pLocalMethodConcepts[ i ]; + + // Fetch name + OUString aMethName = rxMethod_i->getName(); + + // Catalogue methods + // Filter all (?) methods of XInterface so e.g. acquire and release + // can not be called from scripting + OUString className( + rxMethod_i->getDeclaringClass()->getName()); + if (className == "com.sun.star.uno.XInterface") { + bFoundXInterface = true; + + if( bXInterfaceIsInvalid ) + { + pMethodTypes[ i ] = INVALID_METHOD; + continue; + } + else + { + if( aMethName != "queryInterface" ) + { + rMethodConcept_i |= MethodConcept::DANGEROUS; + continue; + } + } + } else if (className == "com.sun.star.uno.XAggregation") + { + if( aMethName == "setDelegator" ) + { + rMethodConcept_i |= MethodConcept::DANGEROUS; + continue; + } + } else if (className + == "com.sun.star.container.XElementAccess") + { + rMethodConcept_i |= ( NAMECONTAINER | + INDEXCONTAINER | + ENUMERATION ); + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XNameContainer") + { + rMethodConcept_i |= NAMECONTAINER; + pAccess->mbNameContainer = true; + pAccess->mbNameReplace = true; + pAccess->mbNameAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XNameReplace") + { + rMethodConcept_i |= NAMECONTAINER; + pAccess->mbNameReplace = true; + pAccess->mbNameAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XNameAccess") + { + rMethodConcept_i |= NAMECONTAINER; + pAccess->mbNameAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XIndexContainer") + { + rMethodConcept_i |= INDEXCONTAINER; + pAccess->mbIndexContainer = true; + pAccess->mbIndexReplace = true; + pAccess->mbIndexAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XIndexReplace") + { + rMethodConcept_i |= INDEXCONTAINER; + pAccess->mbIndexReplace = true; + pAccess->mbIndexAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XIndexAccess") + { + rMethodConcept_i |= INDEXCONTAINER; + pAccess->mbIndexAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.container.XEnumerationAccess") + { + rMethodConcept_i |= ENUMERATION; + pAccess->mbEnumerationAccess = true; + pAccess->mbElementAccess = true; + } else if (className + == "com.sun.star.reflection.XIdlArray") + { + pAccess->mbIdlArray = true; + } else if (className + == "com.sun.star.lang.XUnoTunnel") + { + pAccess->mbUnoTunnel = true; + } + + // If the name is too short, it isn't anything + if( aMethName.getLength() <= 3 ) + continue; + + // Is it a get method? + OUString aPropName; + if( aMethName.startsWith("get", &aPropName) ) + { + // Get methods must not have any parameters + Sequence< Reference<XIdlClass> > getParams = rxMethod_i->getParameterTypes(); + if( getParams.hasElements() ) + { + continue; + } + + // Do we have the name already? + IntrospectionNameMap::iterator aIt = rPropNameMap.find( aPropName ); + if (aIt != rPropNameMap.end()) + { + /* TODO + SAL_INFO("stoc",( + String( "Introspection: Property \"" ) + + OOUStringToString( aPropName, CHARSET_SYSTEM ) + + String( "\" found more than once" ) ); + */ + continue; + } + + // It is already at least a read-only property + rMethodConcept_i |= PROPERTY; + + pMethodTypes[i] = GETSET_METHOD; + Reference<XIdlClass> xGetRetType = rxMethod_i->getReturnType(); + + // Is the property sequence big enough? + pAccess->checkPropertyArraysSize( rPropCount ); + + // Write it in its property array + Property& rProp = rAllPropArray[ rPropCount ]; + rProp.Name = aPropName; + rProp.Handle = rPropCount; + rProp.Type = Type( xGetRetType->getTypeClass(), xGetRetType->getName() ); + rProp.Attributes = READONLY; + + // New entry in the hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + + // Remember get method + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq1, + rInterfaces1, rPropCount ); + rInterfaces1[ rPropCount ] = rxMethod_i; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_GETSET; + rPropertyConceptArray[ rPropCount ] = METHODS; + pAccess->mnMethodPropCount++; + + // Search for matching set method + sal_Int32 k; + for( k = 0 ; k < nSourceMethodCount ; k++ ) + { + // Address method + const Reference<XIdlMethod>& rxMethod_k = pSourceMethods[k]; + + // Accept only methods that are not already assigned + if( k == i || pMethodTypes[k] != STANDARD_METHOD ) + continue; + + // Get name and evaluate + OUString aMethName2 = rxMethod_k->getName(); + OUString aPropName2; + if (!(aMethName2.startsWith("set", &aPropName2) + && aPropName2 == aPropName)) + continue; + + // A set method must return void + Reference<XIdlClass> xSetRetType = rxMethod_k->getReturnType(); + if( xSetRetType->getTypeClass() != TypeClass_VOID ) + { + continue; + } + + // A set method may only have one parameter + Sequence< Reference<XIdlClass> > setParams = rxMethod_k->getParameterTypes(); + sal_Int32 nParamCount = setParams.getLength(); + if( nParamCount != 1 ) + { + continue; + } + + // Next, the return type must correspond to the parameter type + const Reference<XIdlClass>* pParamArray2 = setParams.getConstArray(); + Reference<XIdlClass> xParamType = pParamArray2[ 0 ]; + if( xParamType->equals( xGetRetType ) ) + { + pLocalMethodConcepts[ k ] = PROPERTY; + + pMethodTypes[k] = GETSET_METHOD; + + // Delete read-only flag again + rProp.Attributes &= ~READONLY; + + // Remember set method + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq2, + rInterfaces2, rPropCount ); + rInterfaces2[ rPropCount ] = rxMethod_k; + } + } + + // Adjust count + rPropCount++; + } + + // Is it an add listener method? + else if( aMethName.startsWith("add", &aPropName) ) + { + // Does it end with "Listener"? + OUString aListenerName; + if( !aPropName.endsWith("Listener", &aListenerName) ) + continue; + + // TODO: More accurate tests could still be carried out here + // - Return type + // - Number and type of parameters + + + // Search for matching remove method, otherwise not applicable + sal_Int32 k; + for( k = 0 ; k < nSourceMethodCount ; k++ ) + { + // Address method + const Reference<XIdlMethod>& rxMethod_k = pSourceMethods[k]; + + // Accept only methods that are not already assigned + if( k == i || pMethodTypes[k] != STANDARD_METHOD ) + continue; + + // Get name and evaluate + OUString aMethName2 = rxMethod_k->getName(); + OUString aListenerName2; + if (!(aMethName2.startsWith( + "remove", &aPropName) + && aPropName.endsWith( + "Listener", &aListenerName2) + && aListenerName2 == aListenerName)) + continue; + + // TODO: More accurate tests could still be carried out here + // - Return type + // - Number and type of parameters + + + // Methods are recognised as a listener interface + rMethodConcept_i |= LISTENER; + pLocalMethodConcepts[ k ] |= LISTENER; + + pMethodTypes[i] = ADD_LISTENER_METHOD; + pMethodTypes[k] = REMOVE_LISTENER_METHOD; + } + } + } + + + // A set method could still exist without a corresponding get method, + // this must be a write-only property + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + // Address method + const Reference<XIdlMethod>& rxMethod_i = pSourceMethods[i]; + + // Accept only methods that are not already assigned + if( pMethodTypes[i] != STANDARD_METHOD ) + continue; + + // Get name + OUString aMethName = rxMethod_i->getName(); + + // If the name is too short, it isn't anything + if( aMethName.getLength() <= 3 ) + continue; + + // Is it a set method without associated get method? + OUString aPropName; + if( aMethName.startsWith("set", &aPropName) ) + { + // A set method must return void + Reference<XIdlClass> xSetRetType = rxMethod_i->getReturnType(); + if( xSetRetType->getTypeClass() != TypeClass_VOID ) + { + continue; + } + + // A set method may only have one parameter + Sequence< Reference<XIdlClass> > setParams = rxMethod_i->getParameterTypes(); + sal_Int32 nParamCount = setParams.getLength(); + if( nParamCount != 1 ) + { + continue; + } + + // Do we have the name already? + IntrospectionNameMap::iterator aIt = rPropNameMap.find( aPropName ); + if (aIt != rPropNameMap.end()) + { + /* TODO: + SAL_INFO("stoc",( + String( "Introspection: Property \"" ) + + OOUStringToString( aPropName, CHARSET_SYSTEM ) + + String( "\" found more than once" ) ); + */ + continue; + } + + // Now we know it's a write only property + pLocalMethodConcepts[ i ] = PROPERTY; + + pMethodTypes[i] = GETSET_METHOD; + Reference<XIdlClass> xGetRetType = setParams.getConstArray()[0]; + + // Is the property sequence big enough? + pAccess->checkPropertyArraysSize( rPropCount ); + + // Write it in its property array + Property& rProp = rAllPropArray[ rPropCount ]; + rProp.Name = aPropName; + rProp.Handle = rPropCount; + rProp.Type = Type( xGetRetType->getTypeClass(), xGetRetType->getName() ); + rProp.Attributes = 0; // PROPERTY_WRITEONLY ??? + + // New entry in the hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + + // Remember set method + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq2, + rInterfaces2, rPropCount ); + rInterfaces2[ rPropCount ] = rxMethod_i; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_SETONLY; + rPropertyConceptArray[ rPropCount ] = METHODS; + pAccess->mnMethodPropCount++; + + // Adjust count + rPropCount++; + } + } + + + // 4. Place methods in overall sequence + + // How many methods in the method sequence + sal_Int32 nExportedMethodCount = 0; + sal_Int32 nSupportedListenerCount = 0; + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + if( pMethodTypes[ i ] != INVALID_METHOD ) + { + nExportedMethodCount++; + } + if( pMethodTypes[ i ] == ADD_LISTENER_METHOD ) + { + nSupportedListenerCount++; + } + } + + // Enlarge sequences in the access object accordingly + pAccess->maAllMethodSeq.resize( nExportedMethodCount + iAllExportedMethod ); + pAccess->maMethodConceptSeq.resize( nExportedMethodCount + iAllExportedMethod ); + pAccess->maSupportedListenerSeq.resize( nSupportedListenerCount + iAllSupportedListener ); + + // Write in methods + for( sal_Int32 i = 0 ; i < nSourceMethodCount ; i++ ) + { + if( pMethodTypes[ i ] != INVALID_METHOD ) + { + // Address method + const Reference<XIdlMethod>& rxMethod = pSourceMethods[i]; + + // Enter name in hash table if not already known + OUString aMethName2 = rxMethod->getName(); + IntrospectionNameMap::iterator aIt = rMethodNameMap.find( aMethName2 ); + if( aIt == rMethodNameMap.end() ) + { + // Enter + rMethodNameMap[ aMethName2 ] = iAllExportedMethod; + + // Maintain table for XExactName + rLowerToExactNameMap[ aMethName2.toAsciiLowerCase() ] = aMethName2; + } + else + { + sal_Int32 iHashResult = aIt->second; + + Reference<XIdlMethod> xExistingMethod = pAccess->maAllMethodSeq[iHashResult]; + + Reference< XIdlClass > xExistingMethClass = + xExistingMethod->getDeclaringClass(); + Reference< XIdlClass > xNewMethClass = rxMethod->getDeclaringClass(); + if( xExistingMethClass->equals( xNewMethClass ) ) + continue; + } + + pAccess->maAllMethodSeq[iAllExportedMethod] = rxMethod; + + // If a concept has been set, is the method "normal"? + sal_Int32& rMethodConcept_i = pLocalMethodConcepts[ i ]; + if( !rMethodConcept_i ) + rMethodConcept_i = MethodConcept_NORMAL_IMPL; + pAccess->maMethodConceptSeq[ iAllExportedMethod ] = rMethodConcept_i; + iAllExportedMethod++; + } + if( pMethodTypes[ i ] == ADD_LISTENER_METHOD ) + { + // Determine class of listener + const Reference<XIdlMethod>& rxMethod = pSourceMethods[i]; + + // Enter void as default class + css::uno::Reference<css::reflection::XIdlClass> + xListenerClass( + reflection->forName( + cppu::UnoType<void>::get() + .getTypeName())); + // Old: Reference<XIdlClass> xListenerClass = Void_getReflection()->getIdlClass(); + + // Option 1: Search for parameters for a listener class + // Disadvantage: Superclasses should be searched recursively + const Sequence< Reference<XIdlClass> > aParams = rxMethod->getParameterTypes(); + + css::uno::Reference<css::reflection::XIdlClass> + xEventListenerClass( + reflection->forName( + cppu::UnoType< + css::lang::XEventListener>::get() + .getTypeName())); + // Old: Reference<XIdlClass> xEventListenerClass = XEventListener_getReflection()->getIdlClass(); + auto pParam = std::find_if(aParams.begin(), aParams.end(), + [&xEventListenerClass](const Reference<XIdlClass>& rxClass) { + // Are we derived from a listener? + return rxClass->equals( xEventListenerClass ) + || isDerivedFrom( rxClass, xEventListenerClass ); + }); + if (pParam != aParams.end()) + { + xListenerClass = *pParam; + } + + // Option 2: Unload the name of the method + // Disadvantage: Does not work with test listeners, where it does not exist + //aMethName = rxMethod->getName(); + //aListenerName = aMethName.Copy( 3, aMethName.Len()-8-3 ); + //Reference<XIdlClass> xListenerClass = reflection->forName( aListenerName ); + Type aListenerType( TypeClass_INTERFACE, xListenerClass->getName() ); + pAccess->maSupportedListenerSeq[ iAllSupportedListener ] = aListenerType; + iAllSupportedListener++; + } + } + + // When there were XInterface methods in this run, + // ignore them in the future + if( bFoundXInterface ) + bXInterfaceIsInvalid = true; + } + + // Do superclasses exist? Then continue here + Sequence< Reference<XIdlClass> > aSuperClassSeq = xImplClass2->getSuperclasses(); + + // Currently only one superclass is considered + if( aSuperClassSeq.getLength() >= 1 ) + { + xImplClass2 = aSuperClassSeq.getConstArray()[0]; + OSL_ENSURE( xImplClass2.is(), "super class null" ); + } + else + { + xImplClass2 = nullptr; + } + } + } + + // Apply number of exported methods and adapt Sequences + // (can be different because duplicate methods are thrown + // out only after the determination of nExportedMethodCount) + sal_Int32& rMethCount = pAccess->mnMethCount; + rMethCount = iAllExportedMethod; + pAccess->maAllMethodSeq.resize( rMethCount ); + pAccess->maMethodConceptSeq.resize( rMethCount ); + + // Resize the property sequences + pAccess->maAllPropertySeq.resize( rPropCount ); + pAccess->maPropertyConceptSeq.resize( rPropCount ); + pAccess->maMapTypeSeq.resize( rPropCount ); + } + // Register struct fields as properties + else //if( eType == TypeClass_STRUCT ) + { + // Is it an interface or a struct? + //Reference<XIdlClass> xClassRef = aToInspectObj.getReflection()->getIdlClass(); + css::uno::Reference<css::reflection::XIdlClass> xClassRef( + reflection->forName(aToInspectObj.getValueTypeName())); + if( !xClassRef.is() ) + { + SAL_WARN( "stoc", "Can't get XIdlClass from Reflection" ); + return new ImplIntrospectionAccess(aToInspectObj, pAccess); + } + + // Get fields + const Sequence< Reference<XIdlField> > fields = xClassRef->getFields(); + + for( const Reference<XIdlField>& xField : fields ) + { + Reference<XIdlClass> xPropType = xField->getType(); + OUString aPropName = xField->getName(); + + // Is the property sequence big enough? + pAccess->checkPropertyArraysSize( rPropCount ); + + // Write it in its property array + Property& rProp = rAllPropArray[ rPropCount ]; + rProp.Name = aPropName; + rProp.Handle = rPropCount; + rProp.Type = Type( xPropType->getTypeClass(), xPropType->getName() ); + FieldAccessMode eAccessMode = xField->getAccessMode(); + rProp.Attributes = (eAccessMode == FieldAccessMode_READONLY || + eAccessMode == FieldAccessMode_CONST) + ? READONLY : 0; + + //FieldAccessMode eAccessMode = xField->getAccessMode(); + //rProp.Attributes = (eAccessMode == FieldAccessMode::READONLY || eAccessMode == CONST) + //? PropertyAttribute::READONLY : 0; + + // Write name in hash table + rPropNameMap[ aPropName ] = rPropCount; + + // Maintain table for XExactName + rLowerToExactNameMap[ aPropName.toAsciiLowerCase() ] = aPropName; + + // Remember field + IntrospectionAccessStatic_Impl::checkInterfaceArraySize( pAccess->aInterfaceSeq1, + rInterfaces1, rPropCount ); + rInterfaces1[ rPropCount ] = xField; + + // Remember type of property + rMapTypeArray[ rPropCount ] = MAP_FIELD; + rPropertyConceptArray[ rPropCount ] = ATTRIBUTES; + pAccess->mnAttributePropCount++; + + // Adjust count + rPropCount++; + } + } + + // Set property sequence to the correct length + pAccess->maAllPropertySeq.resize( pAccess->mnPropCount ); + + return new ImplIntrospectionAccess(aToInspectObj, pAccess); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_Introspection_get_implementation( + css::uno::XComponentContext * context, + css::uno::Sequence<css::uno::Any> const & arguments) +{ + SAL_WARN_IF( + arguments.hasElements(), "stoc", "unexpected singleton arguments"); + return cppu::acquire(new Implementation(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/invocation/invocation.component b/stoc/source/invocation/invocation.component new file mode 100644 index 000000000..b5027a4d6 --- /dev/null +++ b/stoc/source/invocation/invocation.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.Invocation" + constructor="stoc_InvocationService_get_implementation"> + <service name="com.sun.star.script.Invocation"/> + </implementation> +</component> diff --git a/stoc/source/invocation/invocation.cxx b/stoc/source/invocation/invocation.cxx new file mode 100644 index 000000000..5463c059e --- /dev/null +++ b/stoc/source/invocation/invocation.cxx @@ -0,0 +1,1097 @@ +/* -*- 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 <comphelper/sequence.hxx> +#include <cppuhelper/queryinterface.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/implbase.hxx> + +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/script/XInvocation2.hpp> +#include <com/sun/star/reflection/XIdlReflection.hpp> +#include <com/sun/star/reflection/theCoreReflection.hpp> +#include <com/sun/star/container/XNameContainer.hpp> +#include <com/sun/star/container/XIndexContainer.hpp> +#include <com/sun/star/container/XEnumerationAccess.hpp> +#include <com/sun/star/beans/XExactName.hpp> +#include <com/sun/star/beans/XMaterialHolder.hpp> +#include <com/sun/star/beans/theIntrospection.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/beans/MethodConcept.hpp> +#include <com/sun/star/beans/PropertyConcept.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XTypeProvider.hpp> + +#include <memory> +#include <vector> + +using namespace css::uno; +using namespace css::lang; +using namespace css::script; +using namespace css::reflection; +using namespace css::beans; +using namespace css::container; +using namespace cppu; +using namespace osl; + +namespace stoc_inv +{ + + +// TODO: Implement centrally +static Reference<XIdlClass> TypeToIdlClass( const Type& rType, const Reference< XIdlReflection > & xRefl ) +{ + return xRefl->forName( rType.getTypeName() ); +} + +namespace { + +class Invocation_Impl + : public OWeakObject + , public XInvocation2 + , public XNameContainer + , public XIndexContainer + , public XEnumerationAccess + , public XExactName + , public XMaterialHolder + , public XTypeProvider +{ +public: + Invocation_Impl( const Any & rAdapted, const Reference<XTypeConverter> &, + const Reference<XIntrospection> &, + const Reference<XIdlReflection> &, + bool bFromOLE ); + + // XInterface + virtual Any SAL_CALL queryInterface( const Type & aType) override; + virtual void SAL_CALL acquire() noexcept override { OWeakObject::acquire(); } + virtual void SAL_CALL release() noexcept override { OWeakObject::release(); } + + + // XTypeProvider + virtual Sequence< css::uno::Type > SAL_CALL getTypes( ) override; + virtual Sequence< sal_Int8 > SAL_CALL getImplementationId( ) override; + + // XMaterialHolder + virtual Any SAL_CALL getMaterial() override; + + // 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; + + // XInvocation2 + virtual Sequence< OUString > SAL_CALL getMemberNames( ) override; + virtual Sequence< InvocationInfo > SAL_CALL getInfo( ) override; + virtual InvocationInfo SAL_CALL getInfoForName( const OUString& aName, sal_Bool bExact ) override; + + // All Access and Container methods are not thread safe + // XElementAccess + virtual Type SAL_CALL getElementType() override + { return _xElementAccess->getElementType(); } + + virtual sal_Bool SAL_CALL hasElements() override + { return _xElementAccess->hasElements(); } + + // XNameContainer + virtual void SAL_CALL insertByName( const OUString& Name, const Any& Element ) override + { _xNameContainer->insertByName( Name, Element ); } + + virtual void SAL_CALL removeByName( const OUString& Name ) override + { _xNameContainer->removeByName( Name ); } + + // XNameReplace + virtual void SAL_CALL replaceByName( const OUString& Name, const Any& Element ) override + { _xNameReplace->replaceByName( Name, Element ); } + + // XNameAccess + virtual Any SAL_CALL getByName( const OUString& Name ) override + { return _xNameAccess->getByName( Name ); } + + virtual Sequence<OUString> SAL_CALL getElementNames() override + { return _xNameAccess->getElementNames(); } + + virtual sal_Bool SAL_CALL hasByName( const OUString& Name ) override + { return _xNameAccess->hasByName( Name ); } + + // XIndexContainer + virtual void SAL_CALL insertByIndex( sal_Int32 Index, const Any& Element ) override + { _xIndexContainer->insertByIndex( Index, Element ); } + + virtual void SAL_CALL removeByIndex( sal_Int32 Index ) override + { _xIndexContainer->removeByIndex( Index ); } + + // XIndexReplace + virtual void SAL_CALL replaceByIndex( sal_Int32 Index, const Any& Element ) override + { _xIndexReplace->replaceByIndex( Index, Element ); } + + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() override + { return _xIndexAccess->getCount(); } + + virtual Any SAL_CALL getByIndex( sal_Int32 Index ) override + { return _xIndexAccess->getByIndex( Index ); } + + // XEnumerationAccess + virtual Reference<XEnumeration> SAL_CALL createEnumeration() override + { return _xEnumerationAccess->createEnumeration(); } + + // XExactName + virtual OUString SAL_CALL getExactName( const OUString& rApproximateName ) override; + + +private: + void setMaterial( const Any& rMaterial ); + + void getInfoSequenceImpl( Sequence< OUString >* pStringSeq, Sequence< InvocationInfo >* pInfoSeq ); + void fillInfoForNameAccess( InvocationInfo& rInfo, const OUString& aName ); + static void fillInfoForProperty( InvocationInfo& rInfo, const Property& rProp ); + static void fillInfoForMethod( InvocationInfo& rInfo, const Reference< XIdlMethod >& xMethod ); + + Reference<XTypeConverter> xTypeConverter; + Reference<XIntrospection> xIntrospection; + Reference<XIdlReflection> xCoreReflection; + + Any _aMaterial; + // _xDirect and (_xIntrospectionAccess, xPropertySet) are exclusive + Reference<XInvocation> _xDirect; + Reference<XInvocation2> _xDirect2; + Reference<XPropertySet> _xPropertySet; + Reference<XIntrospectionAccess> _xIntrospectionAccess; + + // supplied Interfaces + Reference<XNameContainer> _xNameContainer; + Reference<XNameReplace> _xNameReplace; + Reference<XNameAccess> _xNameAccess; + Reference<XIndexContainer> _xIndexContainer; + Reference<XIndexReplace> _xIndexReplace; + Reference<XIndexAccess> _xIndexAccess; + Reference<XEnumerationAccess> _xEnumerationAccess; + Reference<XElementAccess> _xElementAccess; + + + Reference<XExactName> _xENDirect, _xENIntrospection; + + bool mbFromOLE; +}; + +} + +Invocation_Impl::Invocation_Impl +( + const Any & rAdapted, + const Reference<XTypeConverter> & rTC, + const Reference<XIntrospection> & rI, + const Reference<XIdlReflection> & rCR, + bool bFromOLE +) + : xTypeConverter( rTC ) + , xIntrospection( rI ) + , xCoreReflection( rCR ) + , mbFromOLE( bFromOLE ) +{ + setMaterial( rAdapted ); +} + +//### INTERFACE IMPLEMENTATIONS #################################################################### + + +Any SAL_CALL Invocation_Impl::queryInterface( const Type & aType ) +{ + // PropertySet implementation + Any a = ::cppu::queryInterface( aType, + static_cast< XInvocation* >(this), + static_cast< XMaterialHolder* >(this), + static_cast< XTypeProvider * >(this) ); + if( a.hasValue() ) + { + return a; + } + + if( aType == cppu::UnoType<XExactName>::get()) + { + // Invocation does not support XExactName, if direct object supports + // XInvocation, but not XExactName. Except when called from OLE Automation. + if (mbFromOLE || + (_xDirect.is() && _xENDirect.is()) || + (!_xDirect.is() && _xENIntrospection.is())) + { + return Any( Reference< XExactName >( static_cast< XExactName* >(this) ) ); + } + } + else if ( aType == cppu::UnoType<XNameContainer>::get()) + { + if( _xNameContainer.is() ) + return Any( Reference< XNameContainer >( static_cast< XNameContainer* >(this) ) ); + } + else if ( aType == cppu::UnoType<XNameReplace>::get()) + { + if( _xNameReplace.is() ) + return Any( Reference< XNameReplace >( static_cast< XNameReplace* >(this) ) ); + } + else if ( aType == cppu::UnoType<XNameAccess>::get()) + { + if( _xNameAccess.is() ) + return Any( Reference< XNameAccess >( static_cast< XNameAccess* >(this) ) ); + } + else if ( aType == cppu::UnoType<XIndexContainer>::get()) + { + if (_xIndexContainer.is()) + return Any( Reference< XIndexContainer >( static_cast< XIndexContainer* >(this) ) ); + } + else if ( aType == cppu::UnoType<XIndexReplace>::get()) + { + if (_xIndexReplace.is()) + return Any( Reference< XIndexReplace >( static_cast< XIndexReplace* >(this) ) ); + } + else if ( aType == cppu::UnoType<XIndexAccess>::get()) + { + if (_xIndexAccess.is()) + return Any( Reference< XIndexAccess >( static_cast< XIndexAccess* >(this) ) ); + } + else if ( aType == cppu::UnoType<XEnumerationAccess>::get()) + { + if (_xEnumerationAccess.is()) + return Any( Reference< XEnumerationAccess >( static_cast< XEnumerationAccess* >(this) ) ); + } + else if ( aType == cppu::UnoType<XElementAccess>::get()) + { + if (_xElementAccess.is()) + { + return Any( Reference< XElementAccess >( + static_cast< XElementAccess* >(static_cast< XNameContainer* >(this)) ) ); + } + } + else if ( aType == cppu::UnoType<XInvocation2>::get()) + { + // Invocation does not support XInvocation2, if direct object supports + // XInvocation, but not XInvocation2. + if ( mbFromOLE || + ( _xDirect.is() && _xDirect2.is()) || + (!_xDirect.is() && _xIntrospectionAccess.is() ) ) + { + return Any( Reference< XInvocation2 >( static_cast< XInvocation2* >(this) ) ); + } + } + + return OWeakObject::queryInterface( aType ); +} + + +Any Invocation_Impl::getMaterial() +{ + // AB, 12.2.1999 Make sure that the material is taken when possible + // from the direct Invocation of the Introspection, otherwise structs + // are not handled correctly + Reference<XMaterialHolder> xMaterialHolder; + if( _xDirect.is() ) + { + xMaterialHolder.set( _xDirect, UNO_QUERY ); + //_xDirect->queryInterface( XMaterialHolder::getSmartUik(), xMaterialHolder ); + } + else if( _xIntrospectionAccess.is() ) + { + xMaterialHolder.set( _xIntrospectionAccess, UNO_QUERY ); + //_xIntrospectionAccess->queryInterface( XMaterialHolder::getSmartUik(), xMaterialHolder ); + } + if( xMaterialHolder.is() ) + { + return xMaterialHolder->getMaterial(); + } + return _aMaterial; +} + + +void Invocation_Impl::setMaterial( const Any& rMaterial ) +{ + // set the material first and only once + _aMaterial = rMaterial; + + // First do this outside the guard + _xDirect.set( rMaterial, UNO_QUERY ); + + if( !mbFromOLE && _xDirect.is() ) + { + // Consult object directly + _xElementAccess.set( _xDirect, UNO_QUERY ); + _xEnumerationAccess.set( _xDirect, UNO_QUERY ); + _xIndexAccess.set( _xDirect, UNO_QUERY ); + _xIndexReplace.set( _xDirect, UNO_QUERY ); + _xIndexContainer.set( _xDirect, UNO_QUERY ); + _xNameAccess.set( _xDirect, UNO_QUERY ); + _xNameReplace.set( _xDirect, UNO_QUERY ); + _xNameContainer.set( _xDirect, UNO_QUERY ); + _xENDirect.set( _xDirect, UNO_QUERY ); + _xDirect2.set( _xDirect, UNO_QUERY ); + } + else + { + // Make Invocation on the Introspection + if (xIntrospection.is()) + { + _xIntrospectionAccess = xIntrospection->inspect( _aMaterial ); + if( _xIntrospectionAccess.is() ) + { + _xElementAccess.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XElementAccess>::get()), UNO_QUERY ); + + if( _xElementAccess.is() ) + { + _xEnumerationAccess.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XEnumerationAccess>::get()), UNO_QUERY ); + + _xIndexAccess.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XIndexAccess>::get()), UNO_QUERY ); + + if( _xIndexAccess.is() ) + { + _xIndexReplace.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XIndexReplace>::get()), UNO_QUERY ); + + _xIndexContainer.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XIndexContainer>::get()), UNO_QUERY ); + } + + _xNameAccess.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XNameAccess>::get()), UNO_QUERY ); + + if( _xNameAccess.is() ) + { + _xNameReplace.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XNameReplace>::get()), UNO_QUERY ); + + _xNameContainer.set( + _xIntrospectionAccess->queryAdapter( + cppu::UnoType<XNameContainer>::get()), UNO_QUERY ); + } + } + + _xPropertySet.set( _xIntrospectionAccess->queryAdapter( cppu::UnoType<XPropertySet>::get()), + UNO_QUERY ); + + _xENIntrospection.set( _xIntrospectionAccess, UNO_QUERY ); + } + } + } +} + + +OUString Invocation_Impl::getExactName( const OUString& rApproximateName ) +{ + if (_xENDirect.is()) + return _xENDirect->getExactName( rApproximateName ); + + OUString aRet; + if (_xENIntrospection.is()) + aRet = _xENIntrospection->getExactName( rApproximateName ); + return aRet; +} + + +Reference<XIntrospectionAccess> Invocation_Impl::getIntrospection() +{ + if( _xDirect.is() ) + return _xDirect->getIntrospection(); + else + return _xIntrospectionAccess; +} + + +sal_Bool Invocation_Impl::hasMethod( const OUString& Name ) +{ + if (!mbFromOLE && _xDirect.is()) + return _xDirect->hasMethod( Name ); + if( _xIntrospectionAccess.is() ) + return _xIntrospectionAccess->hasMethod( Name, MethodConcept::ALL ^ MethodConcept::DANGEROUS ); + return false; +} + + +sal_Bool Invocation_Impl::hasProperty( const OUString& Name ) +{ + if (_xDirect.is()) + { + bool bRet = _xDirect->hasProperty( Name ); + if (bRet || !mbFromOLE) + return bRet; + } + // PropertySet + if( _xIntrospectionAccess.is() + && _xIntrospectionAccess->hasProperty( Name, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) ) + return true; + // NameAccess + if( _xNameAccess.is() ) + return _xNameAccess->hasByName( Name ); + return false; +} + + +Any Invocation_Impl::getValue( const OUString& PropertyName ) +{ + try + { + if (_xDirect.is()) + return _xDirect->getValue( PropertyName ); + } + catch (Exception &) + { + if (!mbFromOLE) + throw; + } + try + { + // PropertySet + if( _xIntrospectionAccess.is() && _xPropertySet.is() + && _xIntrospectionAccess->hasProperty + ( PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) ) + { + return _xPropertySet->getPropertyValue( PropertyName ); + } + // NameAccess + if( _xNameAccess.is() && _xNameAccess->hasByName( PropertyName ) ) + return _xNameAccess->getByName( PropertyName ); + } + catch (UnknownPropertyException &) + { + throw; + } + catch (RuntimeException &) + { + throw; + } + catch (Exception &) + { + } + + throw UnknownPropertyException( "cannot get value " + PropertyName ); +} + + +void Invocation_Impl::setValue( const OUString& PropertyName, const Any& Value ) +{ + try + { + if (_xDirect.is()) + { + _xDirect->setValue( PropertyName, Value ); + return; + } + } + catch (Exception &) + { + if (!mbFromOLE) + throw; + } + try + { + // Properties + if( _xIntrospectionAccess.is() && _xPropertySet.is() + && _xIntrospectionAccess->hasProperty( + PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) ) + { + Property aProp = _xIntrospectionAccess->getProperty( + PropertyName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ); + Reference < XIdlClass > r = TypeToIdlClass( aProp.Type, xCoreReflection ); + if( r->isAssignableFrom( TypeToIdlClass( Value.getValueType(), xCoreReflection ) ) ) + _xPropertySet->setPropertyValue( PropertyName, Value ); + else if( xTypeConverter.is() ) + _xPropertySet->setPropertyValue( + PropertyName, xTypeConverter->convertTo( Value, aProp.Type ) ); + else + throw RuntimeException( "no type converter service!" ); + } + // NameContainer + else if( _xNameContainer.is() ) + { + // Note: This misfeature deliberately not adapted to apply to objects which + // have XNameReplace but not XNameContainer + Any aConv; + Reference < XIdlClass > r = + TypeToIdlClass( _xNameContainer->getElementType(), xCoreReflection ); + if( r->isAssignableFrom(TypeToIdlClass( Value.getValueType(), xCoreReflection ) ) ) + aConv = Value; + else if( xTypeConverter.is() ) + aConv = xTypeConverter->convertTo( Value, _xNameContainer->getElementType() ); + else + throw RuntimeException( "no type converter service!" ); + + // Replace if present, otherwise insert + if (_xNameContainer->hasByName( PropertyName )) + _xNameContainer->replaceByName( PropertyName, aConv ); + else + _xNameContainer->insertByName( PropertyName, aConv ); + } + else + throw UnknownPropertyException( "no introspection nor name container!" ); + } + catch (UnknownPropertyException &) + { + throw; + } + catch (CannotConvertException &) + { + throw; + } + catch (InvocationTargetException &) + { + throw; + } + catch (RuntimeException &) + { + throw; + } + catch (const Exception & exc) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw InvocationTargetException( + "exception occurred in setValue(): " + exc.Message, + Reference< XInterface >(), anyEx ); + } +} + + +Any Invocation_Impl::invoke( const OUString& FunctionName, const Sequence<Any>& InParams, + Sequence<sal_Int16>& OutIndices, Sequence<Any>& OutParams ) +{ + if (!mbFromOLE && _xDirect.is()) + return _xDirect->invoke( FunctionName, InParams, OutIndices, OutParams ); + + if (_xIntrospectionAccess.is()) + { + // throw NoSuchMethodException if not exist + Reference<XIdlMethod> xMethod = _xIntrospectionAccess->getMethod( + FunctionName, MethodConcept::ALL ^ MethodConcept::DANGEROUS ); + + // ParameterInfos + Sequence<ParamInfo> aFParams = xMethod->getParameterInfos(); + const ParamInfo* pFParams = aFParams.getConstArray(); + sal_Int32 nFParamsLen = aFParams.getLength(); + if (nFParamsLen != InParams.getLength()) + { + throw IllegalArgumentException( + "incorrect number of parameters passed invoking function " + FunctionName + + ": expected " + OUString::number(nFParamsLen) + ", got " + OUString::number(InParams.getLength()), + static_cast<OWeakObject *>(this), sal_Int16(1) ); + } + + // IN Parameter + const Any* pInParams = InParams.getConstArray(); + + // Introspection Invoke Parameter + Sequence<Any> aInvokeParams( nFParamsLen ); + Any* pInvokeParams = aInvokeParams.getArray(); + + // OUT Indices + OutIndices.realloc( nFParamsLen ); + sal_Int16* pOutIndices = OutIndices.getArray(); + sal_uInt32 nOutIndex = 0; + + for ( sal_Int32 nPos = 0; nPos < nFParamsLen; ++nPos ) + { + try + { + const ParamInfo& rFParam = pFParams[nPos]; + const Reference<XIdlClass>& rDestType = rFParam.aType; + + // is IN/INOUT parameter? + if (rFParam.aMode != ParamMode_OUT) + { + if (rDestType->isAssignableFrom( TypeToIdlClass( pInParams[nPos].getValueType(), xCoreReflection ) )) + { + pInvokeParams[nPos] = pInParams[nPos]; + } + else if (xTypeConverter.is()) + { + Type aDestType( rDestType->getTypeClass(), rDestType->getName() ); + pInvokeParams[nPos] = xTypeConverter->convertTo( pInParams[nPos], aDestType ); + } + else + { + CannotConvertException aExc; + aExc.Context = *this; + aExc.Message = "invocation type mismatch!"; + throw aExc; + } + } + + // is OUT/INOUT parameter? + if (rFParam.aMode != ParamMode_IN) + { + pOutIndices[nOutIndex] = static_cast<sal_Int16>(nPos); + if (rFParam.aMode == ParamMode_OUT) + rDestType->createObject( pInvokeParams[nPos] ); // default init + ++nOutIndex; + } + } + catch( CannotConvertException& rExc ) + { + rExc.ArgumentIndex = nPos; // Add optional parameter index + throw; + } + } + + // execute Method + Any aRet = xMethod->invoke( _aMaterial, aInvokeParams ); + + // OUT Params + OutIndices.realloc( nOutIndex ); + OutParams.realloc( nOutIndex ); + + std::transform(std::cbegin(OutIndices), std::cend(OutIndices), OutParams.getArray(), + [&pInvokeParams](const sal_Int16 nIndex) -> Any { return pInvokeParams[nIndex]; }); + + return aRet; + } + + RuntimeException aExc; + aExc.Context = *this; + aExc.Message = "invocation lacking of introspection access!"; + throw aExc; +} + +namespace { + +// Struct to optimize sorting +struct MemberItem +{ + OUString aName; + + // Defines where the member comes from + enum class Mode { NameAccess, PropertySet, Method }; + Mode eMode; + + // Index to respective sequence + // (Index to NameAccess sequence for eMode==Mode::NameAccess etc.) + sal_Int32 nIndex; +}; + +} + +// Implementation of getting name or info +// String sequence will be filled when pStringSeq != NULL +// Info sequence will be filled when pInfoSeq != NULL +void Invocation_Impl::getInfoSequenceImpl +( + Sequence< OUString >* pStringSeq, + Sequence< InvocationInfo >* pInfoSeq +) +{ + //Sequence< OUString > aStrSeq; + //if( !pStringSeq ) + //pStringSeq = &aStrSeq; + + + // Get all needed sequences + Sequence<OUString> aNameAccessNames; + Sequence<Property> aPropertySeq; + Sequence< Reference< XIdlMethod > > aMethodSeq; + + if( _xNameAccess.is() ) + { + aNameAccessNames = _xNameAccess->getElementNames(); + } + + if( _xIntrospectionAccess.is() ) + { + aPropertySeq = _xIntrospectionAccess->getProperties + ( PropertyConcept::ALL - PropertyConcept::DANGEROUS ); + + aMethodSeq = _xIntrospectionAccess->getMethods + ( MethodConcept::ALL - MethodConcept::DANGEROUS ); + } + + sal_Int32 nNameAccessCount = aNameAccessNames.getLength(); + sal_Int32 nPropertyCount = aPropertySeq.getLength(); + sal_Int32 nMethodCount = aMethodSeq.getLength(); + sal_Int32 nTotalCount = nNameAccessCount + nPropertyCount + nMethodCount; + + // Create and fill array of MemberItems + std::unique_ptr< MemberItem []> pItems( new MemberItem[ nTotalCount ] ); + const OUString* pStrings = aNameAccessNames.getConstArray(); + const Property* pProps = aPropertySeq.getConstArray(); + const Reference< XIdlMethod >* pMethods = aMethodSeq.getConstArray(); + + // Fill array of MemberItems + sal_Int32 i, iTotal = 0; + + // Name Access + for( i = 0 ; i < nNameAccessCount ; i++, iTotal++ ) + { + MemberItem& rItem = pItems[ iTotal ]; + rItem.aName = pStrings[ i ]; + rItem.eMode = MemberItem::Mode::NameAccess; + rItem.nIndex = i; + } + + // Property set + for( i = 0 ; i < nPropertyCount ; i++, iTotal++ ) + { + MemberItem& rItem = pItems[ iTotal ]; + rItem.aName = pProps[ i ].Name; + rItem.eMode = MemberItem::Mode::PropertySet; + rItem.nIndex = i; + } + + // Methods + for( i = 0 ; i < nMethodCount ; i++, iTotal++ ) + { + MemberItem& rItem = pItems[ iTotal ]; + Reference< XIdlMethod > xMethod = pMethods[ i ]; + rItem.aName = xMethod->getName(); + rItem.eMode = MemberItem::Mode::Method; + rItem.nIndex = i; + } + + // Setting up result sequences + OUString* pRetStrings = nullptr; + if( pStringSeq ) + { + pStringSeq->realloc( nTotalCount ); + pRetStrings = pStringSeq->getArray(); + } + + InvocationInfo* pRetInfos = nullptr; + if( pInfoSeq ) + { + pInfoSeq->realloc( nTotalCount ); + pRetInfos = pInfoSeq->getArray(); + } + + // Fill result sequences in the correct order of members + for( iTotal = 0 ; iTotal < nTotalCount ; iTotal++ ) + { + MemberItem& rItem = pItems[ iTotal ]; + if( pRetStrings ) + { + pRetStrings[ iTotal ] = rItem.aName; + } + + if( pRetInfos ) + { + if( rItem.eMode == MemberItem::Mode::NameAccess ) + { + fillInfoForNameAccess( pRetInfos[ iTotal ], rItem.aName ); + } + else if( rItem.eMode == MemberItem::Mode::PropertySet ) + { + fillInfoForProperty( pRetInfos[ iTotal ], pProps[ rItem.nIndex ] ); + } + else if( rItem.eMode == MemberItem::Mode::Method ) + { + fillInfoForMethod( pRetInfos[ iTotal ], pMethods[ rItem.nIndex ] ); + } + } + } +} + +// XInvocation2 +Sequence< OUString > SAL_CALL Invocation_Impl::getMemberNames( ) +{ + if( _xDirect2.is() ) + { + return _xDirect2->getMemberNames(); + } + Sequence< OUString > aRetSeq; + getInfoSequenceImpl( &aRetSeq, nullptr ); + return aRetSeq; +} + +Sequence< InvocationInfo > SAL_CALL Invocation_Impl::getInfo( ) +{ + if( _xDirect2.is() ) + { + return _xDirect2->getInfo(); + } + Sequence< InvocationInfo > aRetSeq; + getInfoSequenceImpl( nullptr, &aRetSeq ); + return aRetSeq; +} + +InvocationInfo SAL_CALL Invocation_Impl::getInfoForName( const OUString& aName, sal_Bool bExact ) +{ + if( _xDirect2.is() ) + { + return _xDirect2->getInfoForName( aName, bExact ); + } + + bool bFound = false; + OUString aExactName = aName; + InvocationInfo aRetInfo; + + if( bExact ) + aExactName = getExactName( aName ); + if( !aExactName.isEmpty() ) + { + if( _xIntrospectionAccess->hasMethod( aExactName, MethodConcept::ALL ^ MethodConcept::DANGEROUS ) ) + { + Reference<XIdlMethod> xMethod = _xIntrospectionAccess->getMethod + ( aExactName, MethodConcept::ALL ^ MethodConcept::DANGEROUS ); + fillInfoForMethod( aRetInfo, xMethod ); + bFound = true; + } + else + { + if( _xIntrospectionAccess.is() && _xIntrospectionAccess->hasProperty + ( aExactName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ) ) + { + Property aProp = _xIntrospectionAccess->getProperty + ( aExactName, PropertyConcept::ALL ^ PropertyConcept::DANGEROUS ); + fillInfoForProperty( aRetInfo, aProp ); + bFound = true; + } + // NameAccess + else if( _xNameAccess.is() && _xNameAccess->hasByName( aExactName ) ) + { + fillInfoForNameAccess( aRetInfo, aExactName ); + bFound = true; + } + } + } + if( !bFound ) + { + throw IllegalArgumentException( + "getExactName(), Unknown name " + aName, + static_cast<XWeak *>(static_cast<OWeakObject *>(this)), 0 ); + } + return aRetInfo; +} + +// Helper functions to fill InvocationInfo for XNameAccess +void Invocation_Impl::fillInfoForNameAccess +( + InvocationInfo& rInfo, + const OUString& aName +) +{ + rInfo.aName = aName; + rInfo.eMemberType = MemberType_PROPERTY; + rInfo.PropertyAttribute = 0; + if( !_xNameContainer.is() ) + { + rInfo.PropertyAttribute = PropertyAttribute::READONLY; + } + rInfo.aType = _xNameAccess->getElementType(); +} + +void Invocation_Impl::fillInfoForProperty +( + InvocationInfo& rInfo, + const Property& rProp +) +{ + rInfo.aName = rProp.Name; + rInfo.eMemberType = MemberType_PROPERTY; + rInfo.PropertyAttribute = rProp.Attributes; + rInfo.aType = rProp.Type; +} + +void Invocation_Impl::fillInfoForMethod +( + InvocationInfo& rInfo, + const Reference< XIdlMethod >& xMethod +) +{ + rInfo.aName = xMethod->getName(); + rInfo.eMemberType = MemberType_METHOD; + Reference< XIdlClass > xReturnClass = xMethod->getReturnType(); + Type aReturnType( xReturnClass->getTypeClass(), xReturnClass->getName() ); + rInfo.aType = aReturnType; + Sequence<ParamInfo> aParamInfos = xMethod->getParameterInfos(); + sal_Int32 nParamCount = aParamInfos.getLength(); + if( nParamCount <= 0 ) + return; + + const ParamInfo* pInfo = aParamInfos.getConstArray(); + + rInfo.aParamTypes.realloc( nParamCount ); + Type* pParamTypes = rInfo.aParamTypes.getArray(); + rInfo.aParamModes.realloc( nParamCount ); + ParamMode* pParamModes = rInfo.aParamModes.getArray(); + + for( sal_Int32 i = 0 ; i < nParamCount ; i++ ) + { + Reference< XIdlClass > xParamClass = pInfo[i].aType; + Type aParamType( xParamClass->getTypeClass(), xParamClass->getName() ); + pParamTypes[ i ] = aParamType; + pParamModes[ i ] = pInfo[i].aMode; + } +} + + +// XTypeProvider +Sequence< Type > SAL_CALL Invocation_Impl::getTypes() +{ + static Sequence<Type> s_types = [this]() { + std::vector<Type> tmp { + cppu::UnoType<XTypeProvider>::get(), + cppu::UnoType<XWeak>::get(), + cppu::UnoType<XInvocation>::get(), + cppu::UnoType<XMaterialHolder>::get() }; + + // Invocation does not support XExactName if direct object supports + // XInvocation, but not XExactName. + if ((_xDirect.is() && _xENDirect.is()) || (!_xDirect.is() && _xENIntrospection.is())) + tmp.push_back(cppu::UnoType<XExactName>::get()); + if (_xNameContainer.is()) + tmp.push_back(cppu::UnoType<XNameContainer>::get()); + if (_xNameReplace.is()) + tmp.push_back(cppu::UnoType<XNameReplace>::get()); + if (_xNameAccess.is()) + tmp.push_back(cppu::UnoType<XNameAccess>::get()); + if (_xIndexContainer.is()) + tmp.push_back(cppu::UnoType<XIndexContainer>::get()); + if (_xIndexReplace.is()) + tmp.push_back(cppu::UnoType<XIndexReplace>::get()); + if (_xIndexAccess.is()) + tmp.push_back(cppu::UnoType<XIndexAccess>::get()); + if (_xEnumerationAccess.is()) + tmp.push_back(cppu::UnoType<XEnumerationAccess>::get()); + if (_xElementAccess.is()) + tmp.push_back(cppu::UnoType<XElementAccess>::get()); + // Invocation does not support XInvocation2, if direct object supports + // XInvocation, but not XInvocation2. + if ((_xDirect.is() && _xDirect2.is()) || (!_xDirect.is() && _xIntrospectionAccess.is())) + tmp.push_back(cppu::UnoType<XInvocation2>::get()); + + return comphelper::containerToSequence(tmp); + }(); + return s_types; +} + +Sequence< sal_Int8 > SAL_CALL Invocation_Impl::getImplementationId( ) +{ + return css::uno::Sequence<sal_Int8>(); +} + +namespace { + +class InvocationService + : public WeakImplHelper< XSingleServiceFactory, XServiceInfo > +{ +public: + explicit InvocationService( const Reference<XComponentContext> & xCtx ); + + // XServiceInfo + OUString SAL_CALL getImplementationName() override; + sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XSingleServiceFactory + Reference<XInterface> SAL_CALL createInstance() override; + Reference<XInterface> SAL_CALL createInstanceWithArguments( + const Sequence<Any>& rArguments ) override; +private: + Reference<XComponentContext> mxCtx; + Reference<XMultiComponentFactory> mxSMgr; + Reference<XTypeConverter> xTypeConverter; + Reference<XIntrospection> xIntrospection; + Reference<XIdlReflection> xCoreReflection; +}; + +} + +InvocationService::InvocationService( const Reference<XComponentContext> & xCtx ) + : mxCtx( xCtx ) + , mxSMgr( xCtx->getServiceManager() ) + , xCoreReflection( css::reflection::theCoreReflection::get(mxCtx) ) +{ + xTypeConverter.set( + mxSMgr->createInstanceWithContext( "com.sun.star.script.Converter", xCtx ), + UNO_QUERY ); + xIntrospection = theIntrospection::get(xCtx); +} + +// XServiceInfo +OUString InvocationService::getImplementationName() +{ + return "com.sun.star.comp.stoc.Invocation"; +} + +// XServiceInfo +sal_Bool InvocationService::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > InvocationService::getSupportedServiceNames() +{ + return { "com.sun.star.script.Invocation" }; +} + + +Reference<XInterface> InvocationService::createInstance() +{ + //TODO:throw( Exception("no default construction of invocation adapter possible!", *this) ); + return Reference<XInterface>(); // dummy +} + + +Reference<XInterface> InvocationService::createInstanceWithArguments( + const Sequence<Any>& rArguments ) +{ + if (rArguments.getLength() == 2) + { + OUString aArg1; + if ((rArguments[1] >>= aArg1) && + aArg1 == "FromOLE") + { + return Reference< XInterface > + ( *new Invocation_Impl( *rArguments.getConstArray(), + xTypeConverter, xIntrospection, xCoreReflection, true ) ); + } + } + if (rArguments.getLength() == 1) + { + return Reference< XInterface > + ( *new Invocation_Impl( *rArguments.getConstArray(), + xTypeConverter, xIntrospection, xCoreReflection, false ) ); + } + + //TODO:throw( Exception("no default construction of invocation adapter possible!", *this) ); + return Reference<XInterface>(); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_InvocationService_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new InvocationService(context)); +} + +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/invocation_adapterfactory/iafactory.cxx b/stoc/source/invocation_adapterfactory/iafactory.cxx new file mode 100644 index 000000000..894b00f42 --- /dev/null +++ b/stoc/source/invocation_adapterfactory/iafactory.cxx @@ -0,0 +1,882 @@ +/* -*- 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 <osl/diagnose.h> +#include <osl/interlck.h> +#include <osl/mutex.hxx> +#include <o3tl/sorted_vector.hxx> +#include <sal/log.hxx> + +#include <uno/dispatcher.h> +#include <uno/data.h> +#include <uno/any2.h> +#include <uno/lbnames.h> +#include <uno/mapping.hxx> + +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory.hpp> +#include <com/sun/star/script/XInvocationAdapterFactory2.hpp> +#include <com/sun/star/script/XInvocation.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/reflection/InvocationTargetException.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <unordered_map> +#include <vector> + +using namespace ::std; +using namespace ::osl; +using namespace ::com::sun::star; +using namespace css::uno; + +namespace stoc_invadp +{ + + +namespace { + +struct hash_ptr +{ + size_t operator() ( void * p ) const + { return reinterpret_cast<size_t>(p); } +}; + +} + +typedef o3tl::sorted_vector< void * > t_ptr_set; +typedef std::unordered_map< void *, t_ptr_set, hash_ptr > t_ptr_map; + +namespace { + +class FactoryImpl + : public ::cppu::WeakImplHelper< lang::XServiceInfo, + script::XInvocationAdapterFactory, + script::XInvocationAdapterFactory2 > +{ +public: + Mapping m_aUno2Cpp; + Mapping m_aCpp2Uno; + uno_Interface * m_pConverter; + + typelib_TypeDescription * m_pInvokMethodTD; + typelib_TypeDescription * m_pSetValueTD; + typelib_TypeDescription * m_pGetValueTD; + typelib_TypeDescription * m_pAnySeqTD; + typelib_TypeDescription * m_pShortSeqTD; + typelib_TypeDescription * m_pConvertToTD; + + Mutex m_mutex; + t_ptr_map m_receiver2adapters; + + explicit FactoryImpl( Reference< XComponentContext > const & xContext ); + virtual ~FactoryImpl() override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XInvocationAdapterFactory + virtual Reference< XInterface > SAL_CALL createAdapter( + const Reference< script::XInvocation > & xReceiver, const Type & rType ) override; + // XInvocationAdapterFactory2 + virtual Reference< XInterface > SAL_CALL createAdapter( + const Reference< script::XInvocation > & xReceiver, + const Sequence< Type > & rTypes ) override; +}; +struct AdapterImpl; + +struct InterfaceAdapterImpl : public uno_Interface +{ + AdapterImpl * m_pAdapter; + typelib_InterfaceTypeDescription * m_pTypeDescr; +}; + +struct AdapterImpl +{ + oslInterlockedCount m_nRef; + FactoryImpl * m_pFactory; + void * m_key; // map key + uno_Interface * m_pReceiver; // XInvocation receiver + + std::vector<InterfaceAdapterImpl> m_vInterfaces; + + // XInvocation calls + void getValue( + const typelib_TypeDescription * pMemberType, + void * pReturn, uno_Any ** ppException ); + void setValue( + const typelib_TypeDescription * pMemberType, + void * pArgs[], uno_Any ** ppException ); + void invoke( + const typelib_TypeDescription * pMemberType, + void * pReturn, void * pArgs[], uno_Any ** ppException ); + + bool coerce_assign( + void * pDest, typelib_TypeDescriptionReference * pType, + uno_Any * pSource, uno_Any * pExc ); + inline bool coerce_construct( + void * pDest, typelib_TypeDescriptionReference * pType, + uno_Any * pSource, uno_Any * pExc ); + + inline void acquire(); + inline void release(); + inline ~AdapterImpl(); + inline AdapterImpl( + void * key, Reference< script::XInvocation > const & xReceiver, + const Sequence< Type > & rTypes, + FactoryImpl * pFactory ); + + // Copy assignment is forbidden and not implemented. + AdapterImpl (const AdapterImpl &) = delete; + AdapterImpl & operator= (const AdapterImpl &) = delete; +}; + +} + +inline AdapterImpl::~AdapterImpl() +{ + for ( size_t nPos = m_vInterfaces.size(); nPos--; ) + { + ::typelib_typedescription_release( + &m_vInterfaces[ nPos ].m_pTypeDescr->aBase ); + } + + (*m_pReceiver->release)( m_pReceiver ); + m_pFactory->release(); +} + +inline void AdapterImpl::acquire() +{ + osl_atomic_increment( &m_nRef ); +} + +inline void AdapterImpl::release() +{ + bool delete_this = false; + { + MutexGuard guard( m_pFactory->m_mutex ); + if (! osl_atomic_decrement( &m_nRef )) + { + t_ptr_map::iterator iFind( + m_pFactory->m_receiver2adapters.find( m_key ) ); + OSL_ASSERT( m_pFactory->m_receiver2adapters.end() != iFind ); + t_ptr_set & adapter_set = iFind->second; + if (adapter_set.erase( this ) != 1) { + OSL_ASSERT( false ); + } + if (adapter_set.empty()) + { + m_pFactory->m_receiver2adapters.erase( iFind ); + } + delete_this = true; + } + } + if (delete_this) + delete this; +} + + +static void constructRuntimeException( + uno_Any * pExc, const OUString & rMsg ) +{ + RuntimeException exc( rMsg ); + // no conversion needed due to binary compatibility + no convertible type + ::uno_type_any_construct( + pExc, &exc, cppu::UnoType<decltype(exc)>::get().getTypeLibType(), nullptr ); +} + + +static bool type_equals( + typelib_TypeDescriptionReference * pType1, + typelib_TypeDescriptionReference * pType2 ) +{ + return (pType1 == pType2 || + (pType1->pTypeName->length == pType2->pTypeName->length && + 0 == ::rtl_ustr_compare( + pType1->pTypeName->buffer, pType2->pTypeName->buffer ))); +} + + +bool AdapterImpl::coerce_assign( + void * pDest, typelib_TypeDescriptionReference * pType, uno_Any * pSource, + uno_Any * pOutExc ) +{ + if (typelib_TypeClass_ANY == pType->eTypeClass) + { + ::uno_type_any_assign( + static_cast<uno_Any *>(pDest), pSource->pData, pSource->pType, nullptr, nullptr ); + return true; + } + if (::uno_type_assignData( + pDest, pType, pSource->pData, pSource->pType, nullptr, nullptr, nullptr )) + { + return true; + } + else // try type converter + { + uno_Any ret; + void * args[ 2 ]; + args[ 0 ] = pSource; + args[ 1 ] = &pType; + uno_Any exc; + uno_Any * p_exc = &exc; + + // converTo() + (*m_pFactory->m_pConverter->pDispatcher)( + m_pFactory->m_pConverter, + m_pFactory->m_pConvertToTD, &ret, args, &p_exc ); + + if (p_exc) // exception occurred + { + OSL_ASSERT( + p_exc->pType->eTypeClass == typelib_TypeClass_EXCEPTION ); + if (typelib_typedescriptionreference_isAssignableFrom( cppu::UnoType<RuntimeException>::get().getTypeLibType(), + p_exc->pType )) + { + // is RuntimeException or derived: rethrow + uno_type_any_construct( + pOutExc, p_exc->pData, p_exc->pType, nullptr ); + } + else + { + // set runtime exception + constructRuntimeException( + pOutExc, "type coercion failed: " + + static_cast< Exception const * >( + p_exc->pData )->Message ); + } + ::uno_any_destruct( p_exc, nullptr ); + // pOutExc constructed + return false; + } + else + { + bool succ = ::uno_type_assignData( + pDest, pType, ret.pData, ret.pType, nullptr, nullptr, nullptr ); + ::uno_any_destruct( &ret, nullptr ); + OSL_ENSURE( + succ, "### conversion succeeded, but assignment failed!?" ); + if (! succ) + { + // set runtime exception + constructRuntimeException( + pOutExc, + "type coercion failed: " + "conversion succeeded, but assignment failed?!" ); + } + return succ; + } + } +} + +inline bool AdapterImpl::coerce_construct( + void * pDest, typelib_TypeDescriptionReference * pType, uno_Any * pSource, + uno_Any * pExc ) +{ + if (typelib_TypeClass_ANY == pType->eTypeClass) + { + ::uno_type_copyData( pDest, pSource, pType, nullptr ); + return true; + } + if (type_equals( pType, pSource->pType)) + { + ::uno_type_copyData( pDest, pSource->pData, pType, nullptr ); + return true; + } + ::uno_type_constructData( pDest, pType ); + return coerce_assign( pDest, pType, pSource, pExc ); +} + + +static void handleInvokExc( uno_Any * pDest, uno_Any * pSource ) +{ + OUString const & name = + OUString::unacquired( &pSource->pType->pTypeName ); + + if ( name == "com.sun.star.reflection.InvocationTargetException" ) + { + // unwrap invocation target exception + uno_Any * target_exc = + &static_cast< reflection::InvocationTargetException * >( + pSource->pData )->TargetException; + ::uno_type_any_construct( + pDest, target_exc->pData, target_exc->pType, nullptr ); + } + else // all other exceptions are wrapped to RuntimeException + { + if (typelib_TypeClass_EXCEPTION == pSource->pType->eTypeClass) + { + constructRuntimeException( + pDest, static_cast<Exception const *>(pSource->pData)->Message ); + } + else + { + constructRuntimeException( + pDest, "no exception has been thrown via invocation?!" ); + } + } +} + +void AdapterImpl::getValue( + const typelib_TypeDescription * pMemberType, + void * pReturn, uno_Any ** ppException ) +{ + uno_Any aInvokRet; + void * pInvokArgs[1]; + pInvokArgs[0] = const_cast<rtl_uString **>( + &reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberType)->pMemberName); + uno_Any aInvokExc; + uno_Any * pInvokExc = &aInvokExc; + + // getValue() + (*m_pReceiver->pDispatcher)( + m_pReceiver, m_pFactory->m_pGetValueTD, + &aInvokRet, pInvokArgs, &pInvokExc ); + + if (pInvokExc) // getValue() call exception + { + handleInvokExc( *ppException, pInvokExc ); + ::uno_any_destruct( pInvokExc, nullptr ); // cleanup + } + else // invocation call succeeded + { + if (coerce_construct( + pReturn, + reinterpret_cast<typelib_InterfaceAttributeTypeDescription const *>( + pMemberType)->pAttributeTypeRef, + &aInvokRet, *ppException )) + { + *ppException = nullptr; // no exceptions be thrown + } + ::uno_any_destruct( &aInvokRet, nullptr ); + } +} + +void AdapterImpl::setValue( + const typelib_TypeDescription * pMemberType, + void * pArgs[], uno_Any ** ppException ) +{ + uno_Any aInvokVal; + ::uno_type_any_construct( + &aInvokVal, pArgs[0], + reinterpret_cast<typelib_InterfaceAttributeTypeDescription const *>( + pMemberType)->pAttributeTypeRef, nullptr ); + + void * pInvokArgs[2]; + pInvokArgs[0] = const_cast<rtl_uString **>( + &reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberType)->pMemberName); + pInvokArgs[1] = &aInvokVal; + uno_Any aInvokExc; + uno_Any * pInvokExc = &aInvokExc; + + // setValue() + (*m_pReceiver->pDispatcher)( + m_pReceiver, m_pFactory->m_pSetValueTD, nullptr, pInvokArgs, &pInvokExc ); + + if (pInvokExc) // setValue() call exception + { + handleInvokExc( *ppException, pInvokExc ); + ::uno_any_destruct( pInvokExc, nullptr ); // cleanup + } + else // invocation call succeeded + { + *ppException = nullptr; // no exceptions be thrown + } + + ::uno_any_destruct( &aInvokVal, nullptr ); // cleanup +} + +void AdapterImpl::invoke( + const typelib_TypeDescription * pMemberType, + void * pReturn, void * pArgs[], uno_Any ** ppException ) +{ + sal_Int32 nParams = + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>(pMemberType)->nParams; + typelib_MethodParameter * pFormalParams = + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>(pMemberType)->pParams; + + // in params + uno_Sequence * pInParamsSeq = nullptr; + ::uno_sequence_construct( + &pInParamsSeq, m_pFactory->m_pAnySeqTD, nullptr, nParams, nullptr ); + uno_Any * pInAnys = reinterpret_cast<uno_Any *>(pInParamsSeq->elements); + sal_Int32 nOutParams = 0; + sal_Int32 nPos; + for ( nPos = nParams; nPos--; ) + { + typelib_MethodParameter const & rParam = pFormalParams[nPos]; + if (rParam.bIn) // is in/inout param + { + ::uno_type_any_assign( + &pInAnys[nPos], pArgs[nPos], rParam.pTypeRef, nullptr, nullptr ); + } + // else: pure out is empty any + + if (rParam.bOut) + ++nOutParams; + } + + // out params, out indices + uno_Sequence * pOutIndices; + uno_Sequence * pOutParams; + // return value + uno_Any aInvokRet; + // perform call + void * pInvokArgs[4]; + pInvokArgs[0] = const_cast<rtl_uString **>( + &reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberType)->pMemberName); + pInvokArgs[1] = &pInParamsSeq; + pInvokArgs[2] = &pOutIndices; + pInvokArgs[3] = &pOutParams; + uno_Any aInvokExc; + uno_Any * pInvokExc = &aInvokExc; + + // invoke() call + (*m_pReceiver->pDispatcher)( + m_pReceiver, m_pFactory->m_pInvokMethodTD, + &aInvokRet, pInvokArgs, &pInvokExc ); + + if (pInvokExc) + { + handleInvokExc( *ppException, pInvokExc ); + ::uno_any_destruct( pInvokExc, nullptr ); // cleanup + } + else // no invocation exception + { + // write changed out params + OSL_ENSURE( + pOutParams->nElements == nOutParams && + pOutIndices->nElements == nOutParams, + "### out params lens differ!" ); + if (pOutParams->nElements == nOutParams && + pOutIndices->nElements == nOutParams) + { + sal_Int16 * pIndices = reinterpret_cast<sal_Int16 *>(pOutIndices->elements); + uno_Any * pOut = reinterpret_cast<uno_Any *>(pOutParams->elements); + for ( nPos = 0; nPos < nOutParams; ++nPos ) + { + sal_Int32 nIndex = pIndices[nPos]; + OSL_ENSURE( nIndex < nParams, "### illegal index!" ); + typelib_MethodParameter const & rParam = pFormalParams[nIndex]; + bool succ; + if (rParam.bIn) // is in/inout param + { + succ = coerce_assign( + pArgs[nIndex], rParam.pTypeRef, &pOut[nPos], + *ppException ); + } + else // pure out + { + succ = coerce_construct( + pArgs[nIndex], rParam.pTypeRef, &pOut[nPos], + *ppException ); + } + if (! succ) // cleanup of out params + { + for ( sal_Int32 n = 0; n <= nPos; ++n ) + { + sal_Int32 nIndex2 = pIndices[n]; + OSL_ENSURE( nIndex2 < nParams, "### illegal index!" ); + typelib_MethodParameter const & rParam2 = + pFormalParams[nIndex2]; + if (! rParam2.bIn) // is pure out param + { + ::uno_type_destructData( + pArgs[nIndex2], rParam2.pTypeRef, nullptr ); + } + } + } + } + if (nPos == pOutIndices->nElements) + { + // out param copy ok; write return value + if (coerce_construct( + pReturn, + reinterpret_cast<typelib_InterfaceMethodTypeDescription const *>( + pMemberType)->pReturnTypeRef, + &aInvokRet, *ppException )) + { + *ppException = nullptr; // no exception + } + } + } + else + { + // set runtime exception + constructRuntimeException( + *ppException, + "out params lengths differ after invocation call!" ); + } + // cleanup invok out params + ::uno_destructData( &pOutIndices, m_pFactory->m_pShortSeqTD, nullptr ); + ::uno_destructData( &pOutParams, m_pFactory->m_pAnySeqTD, nullptr ); + // cleanup invok return value + ::uno_any_destruct( &aInvokRet, nullptr ); + } + // cleanup constructed in params + ::uno_destructData( &pInParamsSeq, m_pFactory->m_pAnySeqTD, nullptr ); +} + +extern "C" +{ + +static void adapter_acquire( uno_Interface * pUnoI ) +{ + static_cast< InterfaceAdapterImpl * >( pUnoI )->m_pAdapter->acquire(); +} + +static void adapter_release( uno_Interface * pUnoI ) +{ + static_cast< InterfaceAdapterImpl * >( pUnoI )->m_pAdapter->release(); +} + +static void adapter_dispatch( + uno_Interface * pUnoI, const typelib_TypeDescription * pMemberType, + void * pReturn, void * pArgs[], uno_Any ** ppException ) +{ + // query to emulated interface + switch (reinterpret_cast<typelib_InterfaceMemberTypeDescription const *>(pMemberType)->nPosition) + { + case 0: // queryInterface() + { + AdapterImpl * that = + static_cast< InterfaceAdapterImpl * >( pUnoI )->m_pAdapter; + *ppException = nullptr; // no exc + typelib_TypeDescriptionReference * pDemanded = + *static_cast<typelib_TypeDescriptionReference **>(pArgs[0]); + // pInterfaces[0] is XInterface + for ( size_t nPos = 0; nPos < that->m_vInterfaces.size(); ++nPos ) + { + typelib_InterfaceTypeDescription * pTD = + that->m_vInterfaces[nPos].m_pTypeDescr; + while (pTD) + { + if (type_equals( pTD->aBase.pWeakRef, pDemanded )) + { + uno_Interface * pUnoI2 = &that->m_vInterfaces[nPos]; + ::uno_any_construct( + static_cast<uno_Any *>(pReturn), &pUnoI2, + &pTD->aBase, nullptr ); + return; + } + pTD = pTD->pBaseTypeDescription; + } + } + ::uno_any_construct( static_cast<uno_Any *>(pReturn), nullptr, nullptr, nullptr ); // clear() + break; + } + case 1: // acquire() + *ppException = nullptr; // no exc + adapter_acquire( pUnoI ); + break; + case 2: // release() + *ppException = nullptr; // no exc + adapter_release( pUnoI ); + break; + + default: + { + AdapterImpl * that = + static_cast< InterfaceAdapterImpl * >( pUnoI )->m_pAdapter; + if (pMemberType->eTypeClass == typelib_TypeClass_INTERFACE_METHOD) + { + that->invoke( pMemberType, pReturn, pArgs, ppException ); + } + else // attribute + { + if (pReturn) + that->getValue( pMemberType, pReturn, ppException ); + else + that->setValue( pMemberType, pArgs, ppException ); + } + } + } +} +} + +AdapterImpl::AdapterImpl( + void * key, Reference< script::XInvocation > const & xReceiver, + const Sequence< Type > & rTypes, + FactoryImpl * pFactory ) + : m_nRef( 1 ), + m_pFactory( pFactory ), + m_key( key ), + m_vInterfaces( rTypes.getLength() ) +{ + // init adapters + const Type * pTypes = rTypes.getConstArray(); + for ( sal_Int32 nPos = rTypes.getLength(); nPos--; ) + { + InterfaceAdapterImpl * pInterface = &m_vInterfaces[nPos]; + pInterface->acquire = adapter_acquire; + pInterface->release = adapter_release; + pInterface->pDispatcher = adapter_dispatch; + pInterface->m_pAdapter = this; + pInterface->m_pTypeDescr = nullptr; + pTypes[nPos].getDescription( + reinterpret_cast<typelib_TypeDescription **>(&pInterface->m_pTypeDescr) ); + OSL_ASSERT( pInterface->m_pTypeDescr ); + if (! pInterface->m_pTypeDescr) + { + for ( sal_Int32 n = 0; n < nPos; ++n ) + { + ::typelib_typedescription_release( + &m_vInterfaces[ n ].m_pTypeDescr->aBase ); + } + throw RuntimeException( + "cannot retrieve all interface type infos!" ); + } + } + + // map receiver + m_pReceiver = static_cast<uno_Interface *>(m_pFactory->m_aCpp2Uno.mapInterface( + xReceiver.get(), cppu::UnoType<decltype(xReceiver)>::get() )); + OSL_ASSERT( nullptr != m_pReceiver ); + if (! m_pReceiver) + { + throw RuntimeException( "cannot map receiver!" ); + } + + m_pFactory->acquire(); +} + + +FactoryImpl::FactoryImpl( Reference< XComponentContext > const & xContext ) + : m_aUno2Cpp(Mapping( UNO_LB_UNO, CPPU_CURRENT_LANGUAGE_BINDING_NAME )), + m_aCpp2Uno(Mapping( CPPU_CURRENT_LANGUAGE_BINDING_NAME, UNO_LB_UNO)), + m_pInvokMethodTD( nullptr ), + m_pSetValueTD( nullptr ), + m_pGetValueTD( nullptr ), + m_pAnySeqTD( nullptr ), + m_pShortSeqTD( nullptr ), + m_pConvertToTD( nullptr ) +{ + // C++/UNO bridge + OSL_ENSURE( + m_aUno2Cpp.is() && m_aCpp2Uno.is(), "### no uno / C++ mappings!" ); + + // type converter + Reference< script::XTypeConverter > xConverter( + xContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.script.Converter", + xContext ), + UNO_QUERY_THROW ); + m_pConverter = static_cast<uno_Interface *>(m_aCpp2Uno.mapInterface( + xConverter.get(), cppu::UnoType<decltype(xConverter)>::get() )); + OSL_ASSERT( nullptr != m_pConverter ); + + // some type info: + // sequence< any > + Type const & rAnySeqType = cppu::UnoType<Sequence< Any >>::get(); + rAnySeqType.getDescription( &m_pAnySeqTD ); + // sequence< short > + const Type & rShortSeqType = + cppu::UnoType<Sequence< sal_Int16 >>::get(); + rShortSeqType.getDescription( &m_pShortSeqTD ); + // script.XInvocation + typelib_TypeDescription * pTD = nullptr; + const Type & rInvType = cppu::UnoType<script::XInvocation>::get(); + TYPELIB_DANGER_GET( &pTD, rInvType.getTypeLibType() ); + typelib_InterfaceTypeDescription * pITD; + pITD = reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD); + if( ! pITD->aBase.bComplete ) + typelib_typedescription_complete( &pTD ); + ::typelib_typedescriptionreference_getDescription( + &m_pInvokMethodTD, pITD->ppMembers[ 1 ] ); // invoke() + ::typelib_typedescriptionreference_getDescription( + &m_pSetValueTD, pITD->ppMembers[ 2 ] ); // setValue() + ::typelib_typedescriptionreference_getDescription( + &m_pGetValueTD, pITD->ppMembers[ 3 ] ); // getValue() + // script.XTypeConverter + const Type & rTCType = + cppu::UnoType<script::XTypeConverter>::get(); + TYPELIB_DANGER_GET( &pTD, rTCType.getTypeLibType() ); + pITD = reinterpret_cast<typelib_InterfaceTypeDescription*>(pTD); + ::typelib_typedescriptionreference_getDescription( + &m_pConvertToTD, pITD->ppMembers[ 0 ] ); // convertTo() + TYPELIB_DANGER_RELEASE( pTD ); + + if (!m_pInvokMethodTD || !m_pSetValueTD || !m_pGetValueTD || + !m_pConvertToTD || + !m_pAnySeqTD || !m_pShortSeqTD) + { + throw RuntimeException( "missing type descriptions!" ); + } +} + +FactoryImpl::~FactoryImpl() +{ + ::typelib_typedescription_release( m_pInvokMethodTD ); + ::typelib_typedescription_release( m_pSetValueTD ); + ::typelib_typedescription_release( m_pGetValueTD ); + ::typelib_typedescription_release( m_pAnySeqTD ); + ::typelib_typedescription_release( m_pShortSeqTD ); + ::typelib_typedescription_release( m_pConvertToTD ); + + (*m_pConverter->release)( m_pConverter ); + +#if OSL_DEBUG_LEVEL > 0 + assert(m_receiver2adapters.empty() && "still adapters out there!?"); +#endif +} + + +static AdapterImpl * lookup_adapter( + t_ptr_set ** pp_adapter_set, + t_ptr_map & map, void * key, Sequence< Type > const & rTypes ) +{ + t_ptr_set & adapters_set = map[ key ]; + *pp_adapter_set = &adapters_set; + if (adapters_set.empty()) + return nullptr; // shortcut + // find matching adapter + Type const * pTypes = rTypes.getConstArray(); + sal_Int32 nTypes = rTypes.getLength(); + for (const auto& rpAdapter : adapters_set) + { + AdapterImpl * that = static_cast< AdapterImpl * >( rpAdapter ); + // iterate through all types if that is a matching adapter + sal_Int32 nPosTypes; + for ( nPosTypes = nTypes; nPosTypes--; ) + { + Type const & rType = pTypes[ nPosTypes ]; + // find in adapter's type list + sal_Int32 nPos; + for ( nPos = that->m_vInterfaces.size(); nPos--; ) + { + if (::typelib_typedescriptionreference_isAssignableFrom( + rType.getTypeLibType(), + that->m_vInterfaces[ nPos ].m_pTypeDescr->aBase.pWeakRef )) + { + // found + break; + } + } + if (nPos < 0) // type not found => next adapter + break; + } + if (nPosTypes < 0) // all types found + return that; + } + return nullptr; +} + +// XInvocationAdapterFactory2 impl + +Reference< XInterface > FactoryImpl::createAdapter( + const Reference< script::XInvocation > & xReceiver, + const Sequence< Type > & rTypes ) +{ + Reference< XInterface > xRet; + if (xReceiver.is() && rTypes.hasElements()) + { + t_ptr_set * adapter_set; + AdapterImpl * that; + Reference< XInterface > xKey( xReceiver, UNO_QUERY ); + { + ClearableMutexGuard guard( m_mutex ); + that = lookup_adapter( + &adapter_set, m_receiver2adapters, xKey.get(), rTypes ); + if (nullptr == that) // no entry + { + guard.clear(); + // create adapter; already acquired: m_nRef == 1 + AdapterImpl * pNew = + new AdapterImpl( xKey.get(), xReceiver, rTypes, this ); + // lookup again + ClearableMutexGuard guard2( m_mutex ); + that = lookup_adapter( + &adapter_set, m_receiver2adapters, xKey.get(), rTypes ); + if (nullptr == that) // again no entry + { + pair< t_ptr_set::const_iterator, bool > i(adapter_set->insert(pNew)); + SAL_WARN_IF( + !i.second, "stoc", + "set already contains " << *(i.first) << " != " << pNew); + that = pNew; + } + else + { + that->acquire(); + guard2.clear(); + delete pNew; // has never been inserted + } + } + else // found adapter + { + that->acquire(); + } + } + // map one interface to C++ + uno_Interface * pUnoI = that->m_vInterfaces.data(); + m_aUno2Cpp.mapInterface( + reinterpret_cast<void **>(&xRet), pUnoI, cppu::UnoType<decltype(xRet)>::get() ); + that->release(); + OSL_ASSERT( xRet.is() ); + if (! xRet.is()) + { + throw RuntimeException( "mapping UNO to C++ failed!" ); + } + } + return xRet; +} +// XInvocationAdapterFactory impl + +Reference< XInterface > FactoryImpl::createAdapter( + const Reference< script::XInvocation > & xReceiver, const Type & rType ) +{ + return createAdapter( xReceiver, Sequence< Type >( &rType, 1 ) ); +} + +// XServiceInfo + +OUString FactoryImpl::getImplementationName() +{ + return "com.sun.star.comp.stoc.InvocationAdapterFactory"; +} + +sal_Bool FactoryImpl::supportsService( const OUString & rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > FactoryImpl::getSupportedServiceNames() +{ + return { "com.sun.star.script.InvocationAdapterFactory" }; +} + +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_invocation_adapter_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new stoc_invadp::FactoryImpl(context)); +} + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/invocation_adapterfactory/invocadapt.component b/stoc/source/invocation_adapterfactory/invocadapt.component new file mode 100644 index 000000000..ec1723e22 --- /dev/null +++ b/stoc/source/invocation_adapterfactory/invocadapt.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.InvocationAdapterFactory" + constructor="stoc_invocation_adapter_get_implementation" single-instance="true"> + <service name="com.sun.star.script.InvocationAdapterFactory"/> + </implementation> +</component> diff --git a/stoc/source/javaloader/javaloader.component b/stoc/source/javaloader/javaloader.component new file mode 100644 index 000000000..e8d1533ba --- /dev/null +++ b/stoc/source/javaloader/javaloader.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.JavaComponentLoader" + constructor="stoc_JavaComponentLoader_get_implementation" + single-instance="true"> + <service name="com.sun.star.loader.Java"/> + <service name="com.sun.star.loader.Java2"/> + </implementation> +</component> diff --git a/stoc/source/javaloader/javaloader.cxx b/stoc/source/javaloader/javaloader.cxx new file mode 100644 index 000000000..9b9195fbd --- /dev/null +++ b/stoc/source/javaloader/javaloader.cxx @@ -0,0 +1,571 @@ +/* -*- 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 <rtl/process.h> +#include <sal/log.hxx> + +#include <uno/environment.h> +#include <uno/lbnames.h> +#include <uno/mapping.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <cppuhelper/exc_hlp.hxx> + +#ifdef LINUX +#undef minor +#undef major +#endif + +#include <com/sun/star/java/XJavaVM.hpp> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <rtl/random.h> +#include <rtl/ustrbuf.hxx> +#include <osl/security.hxx> +#include <osl/thread.hxx> + +#include <cppuhelper/factory.hxx> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/bridge/UnoUrlResolver.hpp> +#include <com/sun/star/container/XHierarchicalNameAccess.hpp> +#include <com/sun/star/loader/XImplementationLoader.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> + +#include <jvmaccess/unovirtualmachine.hxx> +#include <jvmaccess/virtualmachine.hxx> + +// this one is header-only +#include <comphelper/sequence.hxx> + +namespace com::sun::star::registry { class XRegistryKey; } + +using namespace css::java; +using namespace css::lang; +using namespace css::loader; +using namespace css::uno; +using namespace css::registry; + +using namespace ::cppu; +using namespace ::osl; + +namespace stoc_javaloader { + +namespace { + +// from desktop/source/deployment/misc/dp_misc.cxx +OUString generateRandomPipeId() +{ + // compute some good pipe id: + static rtlRandomPool s_hPool = rtl_random_createPool(); + if (s_hPool == nullptr) + throw RuntimeException( "cannot create random pool!?", nullptr ); + sal_uInt8 bytes[ 32 ]; + if (rtl_random_getBytes( + s_hPool, bytes, SAL_N_ELEMENTS(bytes) ) != rtl_Random_E_None) { + throw RuntimeException( "random pool error!?", nullptr ); + } + OUStringBuffer buf; + for (unsigned char byte : bytes) { + buf.append( static_cast<sal_Int32>(byte), 0x10 ); + } + return buf.makeStringAndClear(); +} + +// from desktop/source/deployment/registry/component/dp_component.cxx +/** return a vector of bootstrap variables which have been provided + as command arguments. +*/ +std::vector<OUString> getCmdBootstrapVariables() +{ + std::vector<OUString> ret; + sal_uInt32 count = osl_getCommandArgCount(); + for (sal_uInt32 i = 0; i < count; i++) + { + OUString arg; + osl_getCommandArg(i, &arg.pData); + if (arg.startsWith("-env:")) + ret.push_back(arg); + } + return ret; +} + +// from desktop/source/deployment/misc/dp_misc.cxx +oslProcess raiseProcess( + OUString const & appURL, Sequence<OUString> const & args ) +{ + ::osl::Security sec; + oslProcess hProcess = nullptr; + oslProcessError rc = osl_executeProcess( + appURL.pData, + reinterpret_cast<rtl_uString **>( + const_cast<OUString *>(args.getConstArray()) ), + args.getLength(), + osl_Process_DETACHED, + sec.getHandle(), + nullptr, // => current working dir + nullptr, 0, // => no env vars + &hProcess ); + + switch (rc) { + case osl_Process_E_None: + break; + case osl_Process_E_NotFound: + throw RuntimeException( "image not found!", nullptr ); + case osl_Process_E_TimedOut: + throw RuntimeException( "timeout occurred!", nullptr ); + case osl_Process_E_NoPermission: + throw RuntimeException( "permission denied!", nullptr ); + case osl_Process_E_Unknown: + throw RuntimeException( "unknown error!", nullptr ); + case osl_Process_E_InvalidError: + default: + throw RuntimeException( "unmapped error!", nullptr ); + } + + return hProcess; +} + +// from desktop/source/deployment/registry/component/dp_component.cxx +Reference<XComponentContext> raise_uno_process( + Reference<XComponentContext> const & xContext) +{ + OSL_ASSERT( xContext.is() ); + + OUString const url(css::util::theMacroExpander::get(xContext)->expandMacros("$URE_BIN_DIR/uno")); + + const OUString connectStr = "uno:pipe,name=" + generateRandomPipeId() + ";urp;uno.ComponentContext"; + + // raise core UNO process to register/run a component, + // javavm service uses unorc next to executable to retrieve deployed + // jar typelibs + + std::vector<OUString> args{ +#if OSL_DEBUG_LEVEL == 0 + "--quiet", +#endif + "--singleaccept", + "-u", + connectStr, + // don't inherit from unorc: + "-env:INIFILENAME=" }; + + //now add the bootstrap variables which were supplied on the command line + std::vector<OUString> bootvars = getCmdBootstrapVariables(); + args.insert(args.end(), bootvars.begin(), bootvars.end()); + + oslProcess hProcess; + try { + hProcess = raiseProcess(url, comphelper::containerToSequence(args)); + } + catch (...) { + OUStringBuffer sMsg = "error starting process: " + url; + for (const auto& arg : args) { + sMsg.append(" " + arg); + } + throw css::uno::RuntimeException(sMsg.makeStringAndClear()); + } + try { + // from desktop/source/deployment/misc/dp_misc.cxx + Reference<css::bridge::XUnoUrlResolver> const xUnoUrlResolver( + css::bridge::UnoUrlResolver::create(xContext) ); + + for (int i = 0; i <= 40; ++i) // 20 seconds + { + try { + return Reference<XComponentContext>( + xUnoUrlResolver->resolve(connectStr), + UNO_QUERY_THROW ); + } + catch (const css::connection::NoConnectException &) { + if (i < 40) { + ::osl::Thread::wait( std::chrono::milliseconds(500) ); + } + else throw; + } + } + return nullptr; // warning C4715 + } + catch (...) { + // try to terminate process: + if ( osl_terminateProcess( hProcess ) != osl_Process_E_None ) + { + OSL_ASSERT( false ); + } + throw; + } +} + +class JavaComponentLoader + : protected ::cppu::BaseMutex + , public WeakComponentImplHelper<XImplementationLoader, XServiceInfo> +{ + /** local context */ + css::uno::Reference<XComponentContext> m_xComponentContext; + + /** possible remote process' context (use depends on configuration). + note: lifetime must be effectively "static" as this JavaComponentLoader + has no control over the lifetime of the services created via this + context; hence JavaComponentLoader is a single-instance service. + */ + css::uno::Reference<XComponentContext> m_xRemoteComponentContext; + + /** Do not use m_javaLoader directly. Instead use getJavaLoader. + This is either an in-process loader implemented in Java, + or a remote instance of JavaComponentLoader running in uno process, + acting as a proxy. + */ + css::uno::Reference<XImplementationLoader> m_javaLoader; + /** The returned Reference contains a null pointer if the office is not configured + to run java. + + @exception css::uno::RuntimeException + If the Java implementation of the loader could not be obtained, for reasons other + then that java was not configured the RuntimeException is thrown. + */ + const css::uno::Reference<XImplementationLoader> & getJavaLoader(OUString &); + + +public: + /// @throws RuntimeException + explicit JavaComponentLoader(const css::uno::Reference<XComponentContext> & xCtx); + +public: + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override; + + virtual void SAL_CALL disposing() override; + + // XImplementationLoader + virtual css::uno::Reference<XInterface> SAL_CALL activate( + const OUString& implementationName, const OUString& implementationLoaderUrl, + const OUString& locationUrl, const css::uno::Reference<XRegistryKey>& xKey) override; + virtual sal_Bool SAL_CALL writeRegistryInfo( + const css::uno::Reference<XRegistryKey>& xKey, + const OUString& implementationLoaderUrl, const OUString& locationUrl) override; +}; + +} + +void JavaComponentLoader::disposing() +{ + // Explicitly drop all remote refs to shut down the uno.bin process + // and particularly the connection to it, so that it can't do more calls + // during late shutdown. + m_javaLoader.clear(); + if (m_xRemoteComponentContext.is()) { + Reference<XComponent> const xComp(m_xRemoteComponentContext, UNO_QUERY); + assert(xComp.is()); + xComp->dispose(); + m_xRemoteComponentContext.clear(); + } +} + +const css::uno::Reference<XImplementationLoader> & JavaComponentLoader::getJavaLoader(OUString & rRemoteArg) +{ + static Mutex ourMutex; + MutexGuard aGuard(ourMutex); + + if (m_javaLoader.is()) + return m_javaLoader; + + // check if the JVM should be instantiated out-of-process + if (rRemoteArg.isEmpty()) { + if (!m_xRemoteComponentContext.is()) { + Reference<css::container::XHierarchicalNameAccess> const xConf( + m_xComponentContext->getServiceManager()->createInstanceWithArgumentsAndContext( + "com.sun.star.configuration.ReadOnlyAccess", + { Any(OUString("*")) }, // locale isn't relevant here + m_xComponentContext), + UNO_QUERY); + + // configmgr is not part of URE, so may not exist! + if (xConf.is()) { + Any const value(xConf->getByHierarchicalName( + "org.openoffice.Office.Java/VirtualMachine/RunUnoComponentsOutOfProcess")); + bool b; + if ((value >>= b) && b) { + SAL_INFO("stoc.java", "JavaComponentLoader: starting uno process"); + m_xRemoteComponentContext = raise_uno_process(m_xComponentContext); + } + } + } + if (m_xRemoteComponentContext.is()) { + SAL_INFO("stoc.java", "JavaComponentLoader: creating remote instance to start JVM in uno process"); + // create JVM service in remote uno.bin process + Reference<XImplementationLoader> const xLoader( + m_xRemoteComponentContext->getServiceManager()->createInstanceWithContext( + "com.sun.star.loader.Java2", m_xRemoteComponentContext), + UNO_QUERY_THROW); + assert(xLoader.is()); + m_javaLoader = xLoader; + rRemoteArg = "remote"; + SAL_INFO("stoc.java", "JavaComponentLoader: remote proxy instance created: " << m_javaLoader.get()); + return m_javaLoader; + } + } + + uno_Environment * pJava_environment = nullptr; + uno_Environment * pUno_environment = nullptr; + typelib_InterfaceTypeDescription * pType_XImplementationLoader = nullptr; + + try { + // get a java vm, where we can create a loader + css::uno::Reference<XJavaVM> javaVM_xJavaVM( + m_xComponentContext->getValueByName( + ("/singletons/" + "com.sun.star.java.theJavaVirtualMachine")), + UNO_QUERY_THROW); + + // Use the special protocol of XJavaVM.getJavaVM: If the passed in + // process ID has an extra 17th byte of value one, the returned any + // contains a pointer to a jvmaccess::UnoVirtualMachine, instead of the + // underlying JavaVM pointer: + Sequence<sal_Int8> processID(17); + rtl_getGlobalProcessId(reinterpret_cast<sal_uInt8 *>(processID.getArray())); + processID.getArray()[16] = 1; + + // We get a non-refcounted pointer to a jvmaccess::UnoVirtualMachine + // from the XJavaVM service (the pointer is guaranteed to be valid + // as long as our reference to the XJavaVM service lasts), and + // convert the non-refcounted pointer into a refcounted one + // immediately: + static_assert(sizeof (sal_Int64) + >= sizeof (jvmaccess::UnoVirtualMachine *), "must be at least the same size"); + sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( + static_cast< jvmaccess::UnoVirtualMachine * >(nullptr)); + javaVM_xJavaVM->getJavaVM(processID) >>= nPointer; + rtl::Reference< jvmaccess::UnoVirtualMachine > xVirtualMachine( + reinterpret_cast< jvmaccess::UnoVirtualMachine * >(nPointer)); + if (!xVirtualMachine.is()) + { + //throw RuntimeException( + // "javaloader error - JavaVirtualMachine service could not provide a VM", + // css::uno::Reference<XInterface>()); + // We must not throw a RuntimeException, because this might end the applications. + // It is ok if java components + // are not working because the office can be installed without Java support. + SAL_WARN("stoc", "getJavaVM returned null"); + return m_javaLoader; // null-ref + } + + try + { + jvmaccess::VirtualMachine::AttachGuard aGuard2( + xVirtualMachine->getVirtualMachine()); + JNIEnv * pJNIEnv = aGuard2.getEnvironment(); + + // instantiate the java JavaLoader + jclass jcClassLoader = pJNIEnv->FindClass("java/lang/ClassLoader"); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - could not find class java/lang/ClassLoader"); + jmethodID jmLoadClass = pJNIEnv->GetMethodID( + jcClassLoader, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - could not find method java/lang/ClassLoader.loadClass"); + jvalue arg; + arg.l = pJNIEnv->NewStringUTF( + "com.sun.star.comp.loader.JavaLoader"); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - could not create string"); + jclass jcJavaLoader = static_cast< jclass >( + pJNIEnv->CallObjectMethodA( + static_cast< jobject >(xVirtualMachine->getClassLoader()), + jmLoadClass, &arg)); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - could not find class com/sun/star/comp/loader/JavaLoader"); + jmethodID jmJavaLoader_init = pJNIEnv->GetMethodID(jcJavaLoader, "<init>", "()V"); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - instantiation of com.sun.star.comp.loader.JavaLoader failed"); + jobject joJavaLoader = pJNIEnv->NewObject(jcJavaLoader, jmJavaLoader_init); + if(pJNIEnv->ExceptionOccurred()) + throw RuntimeException( + "javaloader error - instantiation of com.sun.star.comp.loader.JavaLoader failed"); + + // map the java JavaLoader to this environment + OUString sJava("java"); + uno_getEnvironment(&pJava_environment, sJava.pData, + xVirtualMachine.get()); + if(!pJava_environment) + throw RuntimeException( + "javaloader error - no Java environment available"); + + // why is there no convenient constructor? + OUString sCppu_current_lb_name(CPPU_CURRENT_LANGUAGE_BINDING_NAME); + uno_getEnvironment(&pUno_environment, sCppu_current_lb_name.pData, nullptr); + if(!pUno_environment) + throw RuntimeException( + "javaloader error - no C++ environment available"); + + Mapping java_curr(pJava_environment, pUno_environment); + if(!java_curr.is()) + throw RuntimeException( + "javaloader error - no mapping from java to C++ "); + + // release java environment + pJava_environment->release(pJava_environment); + pJava_environment = nullptr; + + // release uno environment + pUno_environment->release(pUno_environment); + pUno_environment = nullptr; + + cppu::UnoType<XImplementationLoader>::get(). + getDescription(reinterpret_cast<typelib_TypeDescription **>(&pType_XImplementationLoader)); + if(!pType_XImplementationLoader) + throw RuntimeException( + "javaloader error - no type information for XImplementationLoader"); + + m_javaLoader.set(static_cast<XImplementationLoader *>(java_curr.mapInterface(joJavaLoader, pType_XImplementationLoader))); + pJNIEnv->DeleteLocalRef( joJavaLoader ); + if(!m_javaLoader.is()) + throw RuntimeException( + "javaloader error - mapping of java XImplementationLoader to c++ failed"); + + typelib_typedescription_release(reinterpret_cast<typelib_TypeDescription *>(pType_XImplementationLoader)); + pType_XImplementationLoader = nullptr; + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::VirtualMachine::AttachGuard::CreationException", + static_cast< cppu::OWeakObject * >(this), anyEx ); + } + + // set the service manager at the javaloader + css::uno::Reference<XInitialization> javaLoader_XInitialization(m_javaLoader, UNO_QUERY_THROW); + + Any any; + any <<= m_xComponentContext->getServiceManager(); + + javaLoader_XInitialization->initialize(Sequence<Any>(&any, 1)); + } + catch(RuntimeException &) { + if(pJava_environment) + pJava_environment->release(pJava_environment); + + if(pUno_environment) + pUno_environment->release(pUno_environment); + + if(pType_XImplementationLoader) + typelib_typedescription_release( + reinterpret_cast<typelib_TypeDescription *>(pType_XImplementationLoader)); + throw; + } + SAL_INFO("stoc", "javaloader.cxx: mapped javaloader - 0x" << m_javaLoader.get()); + return m_javaLoader; +} + +JavaComponentLoader::JavaComponentLoader(const css::uno::Reference<XComponentContext> & xCtx) + : WeakComponentImplHelper(m_aMutex) + , m_xComponentContext(xCtx) +{ + +} + +// XServiceInfo +OUString SAL_CALL JavaComponentLoader::getImplementationName() +{ + return "com.sun.star.comp.stoc.JavaComponentLoader"; +} + +sal_Bool SAL_CALL JavaComponentLoader::supportsService(const OUString & ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL JavaComponentLoader::getSupportedServiceNames() +{ + return { "com.sun.star.loader.Java", "com.sun.star.loader.Java2" }; +} + + +// XImplementationLoader +sal_Bool SAL_CALL JavaComponentLoader::writeRegistryInfo( + const css::uno::Reference<XRegistryKey> & xKey, const OUString & blabla, + const OUString & rLibName) +{ + OUString remoteArg(blabla); + const css::uno::Reference<XImplementationLoader> & loader = getJavaLoader(remoteArg); + if (!loader.is()) + throw CannotRegisterImplementationException("Could not create Java implementation loader"); + return loader->writeRegistryInfo(xKey, remoteArg, rLibName); +} + +css::uno::Reference<XInterface> SAL_CALL JavaComponentLoader::activate( + const OUString & rImplName, const OUString & blabla, const OUString & rLibName, + const css::uno::Reference<XRegistryKey> & xKey) +{ + OUString remoteArg(blabla); + if (rImplName.isEmpty() && blabla.isEmpty() && rLibName.isEmpty()) + { + // preload JVM was requested + (void)getJavaLoader(remoteArg); + return css::uno::Reference<XInterface>(); + } + + const css::uno::Reference<XImplementationLoader> & loader = getJavaLoader(remoteArg); + if (!loader.is()) + throw CannotActivateFactoryException("Could not create Java implementation loader"); + return loader->activate(rImplName, remoteArg, rLibName, xKey); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_JavaComponentLoader_get_implementation( + css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const&) +{ + try { + return cppu::acquire(new JavaComponentLoader(context)); + } + catch(const RuntimeException & runtimeException) { + SAL_INFO( + "stoc", + "could not init javaloader due to " << runtimeException); + throw; + } +} + +} //end namespace + + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/interact.cxx b/stoc/source/javavm/interact.cxx new file mode 100644 index 000000000..d1399c065 --- /dev/null +++ b/stoc/source/javavm/interact.cxx @@ -0,0 +1,107 @@ +/* -*- 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 "interact.hxx" + +#include <com/sun/star/task/XInteractionAbort.hpp> +#include <com/sun/star/task/XInteractionRetry.hpp> +#include <cppuhelper/implbase.hxx> +#include <mutex> + +namespace com::sun::star::task { class XInteractionContinuation; } + +using stoc_javavm::InteractionRequest; + +namespace { + +class AbortContinuation: + public cppu::WeakImplHelper<css::task::XInteractionAbort> +{ +public: + AbortContinuation() {} + AbortContinuation(const AbortContinuation&) = delete; + AbortContinuation& operator=(const AbortContinuation&)= delete; + + virtual void SAL_CALL select() override {} + +private: + virtual ~AbortContinuation() override {} +}; + +} + +class InteractionRequest::RetryContinuation: + public cppu::WeakImplHelper<css::task::XInteractionRetry> +{ +public: + RetryContinuation(): m_bSelected(false) {} + RetryContinuation(const RetryContinuation&) = delete; + RetryContinuation& operator=(const RetryContinuation&) = delete; + + virtual void SAL_CALL select() override; + + bool isSelected() const; + +private: + virtual ~RetryContinuation() override {} + + mutable std::mutex m_aMutex; + bool m_bSelected; +}; + +void SAL_CALL InteractionRequest::RetryContinuation::select() +{ + std::scoped_lock aGuard(m_aMutex); + m_bSelected = true; +} + +bool InteractionRequest::RetryContinuation::isSelected() const +{ + std::scoped_lock aGuard(m_aMutex); + return m_bSelected; +} + +InteractionRequest::InteractionRequest(css::uno::Any const & rRequest): + m_aRequest(rRequest) +{ + m_xRetryContinuation = new RetryContinuation; + m_aContinuations = { new AbortContinuation, m_xRetryContinuation }; +} + +css::uno::Any SAL_CALL InteractionRequest::getRequest() +{ + return m_aRequest; +} + +css::uno::Sequence< css::uno::Reference< css::task::XInteractionContinuation > > +SAL_CALL InteractionRequest::getContinuations() +{ + return m_aContinuations; +} + +bool InteractionRequest::retry() const +{ + return m_xRetryContinuation.is() && m_xRetryContinuation->isSelected(); +} + +InteractionRequest::~InteractionRequest() +{} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/interact.hxx b/stoc/source/javavm/interact.hxx new file mode 100644 index 000000000..35aa97b97 --- /dev/null +++ b/stoc/source/javavm/interact.hxx @@ -0,0 +1,67 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX + +#include <com/sun/star/task/XInteractionRequest.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <rtl/ref.hxx> + +namespace com::sun::star::task { + class XInteractionContinuation; +} + +namespace stoc_javavm { + +class InteractionRequest: + public cppu::WeakImplHelper< css::task::XInteractionRequest > +{ +public: + explicit InteractionRequest(css::uno::Any const & rRequest); + + virtual css::uno::Any SAL_CALL getRequest() override; + + virtual css::uno::Sequence< css::uno::Reference< + css::task::XInteractionContinuation > > SAL_CALL + getContinuations() override; + + bool retry() const; + +private: + class RetryContinuation; + + InteractionRequest(InteractionRequest const &) = delete; + void operator =(const InteractionRequest&) = delete; + + virtual ~InteractionRequest() override; + + css::uno::Any m_aRequest; + css::uno::Sequence< css::uno::Reference< + css::task::XInteractionContinuation > > m_aContinuations; + rtl::Reference< RetryContinuation > m_xRetryContinuation; +}; + +} + +#endif // INCLUDED_STOC_SOURCE_JAVAVM_INTERACT_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/javavm.component b/stoc/source/javavm/javavm.component new file mode 100644 index 000000000..71379052b --- /dev/null +++ b/stoc/source/javavm/javavm.component @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.JavaVirtualMachine" + constructor="stoc_JavaVM_get_implementation" + single-instance="true"> + <service name="com.sun.star.java.JavaVirtualMachine"/> + <singleton name="com.sun.star.java.theJavaVirtualMachine"/> + </implementation> +</component> diff --git a/stoc/source/javavm/javavm.cxx b/stoc/source/javavm/javavm.cxx new file mode 100644 index 000000000..4ed1293f9 --- /dev/null +++ b/stoc/source/javavm/javavm.cxx @@ -0,0 +1,1483 @@ +/* -*- 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 "javavm.hxx" + +#include "interact.hxx" +#include "jvmargs.hxx" + +#include <com/sun/star/beans/NamedValue.hpp> +#include <com/sun/star/container/XContainer.hpp> +#include <com/sun/star/java/JavaNotFoundException.hpp> +#include <com/sun/star/java/InvalidJavaSettingsException.hpp> +#include <com/sun/star/java/RestartRequiredException.hpp> +#include <com/sun/star/java/JavaDisabledException.hpp> +#include <com/sun/star/java/JavaVMCreationFailureException.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/task/XInteractionHandler.hpp> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/util/theMacroExpander.hpp> +#include <comphelper/propertysequence.hxx> +#include <comphelper/SetFlagContextHelper.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <jvmaccess/classpath.hxx> +#include <jvmaccess/unovirtualmachine.hxx> +#include <jvmaccess/virtualmachine.hxx> +#include <rtl/process.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <sal/log.hxx> +#include <uno/current_context.hxx> +#include <jvmfwk/framework.hxx> +#include <i18nlangtag/languagetag.hxx> +#include <jni.h> + +#include <stack> +#include <string.h> +#include <time.h> +#include <memory> +#include <vector> + +// Properties of the javavm can be put +// as a comma separated list in this +// environment variable +#ifdef UNIX +#define TIMEZONE "MEZ" +#else +#define TIMEZONE "MET" +#endif + +/* Within this implementation of the com.sun.star.java.JavaVirtualMachine + * service and com.sun.star.java.theJavaVirtualMachine singleton, the method + * com.sun.star.java.XJavaVM.getJavaVM relies on the following: + * 1 The string "$URE_INTERNAL_JAVA_DIR/" is expanded via the + * com.sun.star.util.theMacroExpander singleton into an internal (see the + * com.sun.star.uri.ExternalUriReferenceTranslator service), hierarchical URI + * reference relative to which the URE JAR files can be addressed. + * 2 The string "$URE_INTERNAL_JAVA_CLASSPATH" is either not expandable via the + * com.sun.star.util.theMacroExpander singleton + * (com.sun.star.lang.IllegalArgumentException), or is expanded via the + * com.sun.star.util.theMacroExpander singleton into a list of zero or more + * internal (see the com.sun.star.uri.ExternalUriReferenceTranslator service) + * URIs, where any space characters (U+0020) are ignored (and, in particular, + * separate adjacent URIs). + * If either of these requirements is not met, getJavaVM raises a + * com.sun.star.uno.RuntimeException. + */ + +using stoc_javavm::JavaVirtualMachine; + +namespace { + + +class NoJavaIniException: public css::uno::Exception +{ +}; + +typedef std::stack< jvmaccess::VirtualMachine::AttachGuard * > GuardStack; + +extern "C" { + +static void destroyAttachGuards(void * pData) +{ + GuardStack * pStack = static_cast< GuardStack * >(pData); + if (pStack != nullptr) + { + while (!pStack->empty()) + { + delete pStack->top(); + pStack->pop(); + } + delete pStack; + } +} + +} + +bool askForRetry(css::uno::Any const & rException) +{ + css::uno::Reference< css::uno::XCurrentContext > xContext( + css::uno::getCurrentContext()); + if (xContext.is()) + { + css::uno::Reference< css::task::XInteractionHandler > xHandler; + xContext->getValueByName("java-vm.interaction-handler") + >>= xHandler; + if (xHandler.is()) + { + rtl::Reference< stoc_javavm::InteractionRequest > xRequest( + new stoc_javavm::InteractionRequest(rException)); + xHandler->handle(xRequest); + return xRequest->retry(); + } + } + return false; +} + +// Only gets the properties if the "Proxy Server" entry in the option dialog is +// set to manual (i.e. not to none) +/// @throws css::uno::Exception +void getINetPropsFromConfig(stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx ) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = xSMgr->createInstanceWithContext( + "com.sun.star.configuration.ConfigurationRegistry", + xCtx ); + if(!xConfRegistry.is()) throw css::uno::RuntimeException("javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple(xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open("org.openoffice.Inet", true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = xConfRegistry_simple->getRootKey(); + +// if ooInetProxyType is not 0 then read the settings + css::uno::Reference<css::registry::XRegistryKey> proxyEnable= xRegistryRootKey->openKey("Settings/ooInetProxyType"); + if( proxyEnable.is() && 0 != proxyEnable->getLongValue()) + { + // read ftp proxy name + css::uno::Reference<css::registry::XRegistryKey> ftpProxy_name = xRegistryRootKey->openKey("Settings/ooInetFTPProxyName"); + if(ftpProxy_name.is() && !ftpProxy_name->getStringValue().isEmpty()) { + OUString ftpHost = "ftp.proxyHost=" + ftpProxy_name->getStringValue(); + + // read ftp proxy port + css::uno::Reference<css::registry::XRegistryKey> ftpProxy_port = xRegistryRootKey->openKey("Settings/ooInetFTPProxyPort"); + if(ftpProxy_port.is() && ftpProxy_port->getLongValue()) { + OUString ftpPort = "ftp.proxyPort=" + OUString::number(ftpProxy_port->getLongValue()); + + pjvm->pushProp(ftpHost); + pjvm->pushProp(ftpPort); + } + } + + // read http proxy name + css::uno::Reference<css::registry::XRegistryKey> httpProxy_name = xRegistryRootKey->openKey("Settings/ooInetHTTPProxyName"); + if(httpProxy_name.is() && !httpProxy_name->getStringValue().isEmpty()) { + OUString httpHost = "http.proxyHost=" + httpProxy_name->getStringValue(); + + // read http proxy port + css::uno::Reference<css::registry::XRegistryKey> httpProxy_port = xRegistryRootKey->openKey("Settings/ooInetHTTPProxyPort"); + if(httpProxy_port.is() && httpProxy_port->getLongValue()) { + OUString httpPort = "http.proxyPort=" + OUString::number(httpProxy_port->getLongValue()); + + pjvm->pushProp(httpHost); + pjvm->pushProp(httpPort); + } + } + + // read https proxy name + css::uno::Reference<css::registry::XRegistryKey> httpsProxy_name = xRegistryRootKey->openKey("Settings/ooInetHTTPSProxyName"); + if(httpsProxy_name.is() && !httpsProxy_name->getStringValue().isEmpty()) { + OUString httpsHost = "https.proxyHost=" + httpsProxy_name->getStringValue(); + + // read https proxy port + css::uno::Reference<css::registry::XRegistryKey> httpsProxy_port = xRegistryRootKey->openKey("Settings/ooInetHTTPSProxyPort"); + if(httpsProxy_port.is() && httpsProxy_port->getLongValue()) { + OUString httpsPort = "https.proxyPort=" + OUString::number(httpsProxy_port->getLongValue()); + + pjvm->pushProp(httpsHost); + pjvm->pushProp(httpsPort); + } + } + + // read nonProxyHosts + css::uno::Reference<css::registry::XRegistryKey> nonProxies_name = xRegistryRootKey->openKey("Settings/ooInetNoProxy"); + if(nonProxies_name.is() && !nonProxies_name->getStringValue().isEmpty()) { + OUString value = nonProxies_name->getStringValue(); + // replace the separator ";" by "|" + value = value.replace(';', '|'); + + OUString httpNonProxyHosts = "http.nonProxyHosts=" + value; + OUString ftpNonProxyHosts = "ftp.nonProxyHosts=" + value; + + pjvm->pushProp(httpNonProxyHosts); + pjvm->pushProp(ftpNonProxyHosts); + } + } + xConfRegistry_simple->close(); +} + +/// @throws css::uno::Exception +void getDefaultLocaleFromConfig( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx ) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = + xSMgr->createInstanceWithContext( "com.sun.star.configuration.ConfigurationRegistry", xCtx ); + if(!xConfRegistry.is()) + throw css::uno::RuntimeException( + "javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple( + xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open("org.openoffice.Setup", true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = xConfRegistry_simple->getRootKey(); + + // Since 1.7 Java knows DISPLAY and FORMAT locales, which match our UI and + // system locale. See + // http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/569b1b644416/src/share/classes/java/util/Locale.java + // https://docs.oracle.com/javase/tutorial/i18n/locale/scope.html + // https://docs.oracle.com/javase/7/docs/api/java/util/Locale.html + + // Read UI language/locale. + css::uno::Reference<css::registry::XRegistryKey> xUILocale = xRegistryRootKey->openKey("L10N/ooLocale"); + if(xUILocale.is() && !xUILocale->getStringValue().isEmpty()) { + LanguageTag aLanguageTag( xUILocale->getStringValue()); + OUString language; + OUString script; + OUString country; + // Java knows nothing but plain old ISO codes, unless Locale.Builder or + // Locale.forLanguageTag() are used, or non-standardized variant field + // content which we ignore. + aLanguageTag.getIsoLanguageScriptCountry( language, script, country); + + if(!language.isEmpty()) { + OUString prop = "user.language=" + language; + pjvm->pushProp(prop); + } + + // As of Java 7 also script is supported. + if(!script.isEmpty()) { + OUString prop = "user.script=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country=" + country; + pjvm->pushProp(prop); + } + + // Java 7 DISPLAY category is our UI language/locale. + if(!language.isEmpty()) { + OUString prop = "user.language.display=" + language; + pjvm->pushProp(prop); + } + + if(!script.isEmpty()) { + OUString prop = "user.script.display=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country.display=" + country; + pjvm->pushProp(prop); + } + } + + // Read system locale. + css::uno::Reference<css::registry::XRegistryKey> xLocale = xRegistryRootKey->openKey("L10N/ooSetupSystemLocale"); + if(xLocale.is() && !xLocale->getStringValue().isEmpty()) { + LanguageTag aLanguageTag( xLocale->getStringValue()); + OUString language; + OUString script; + OUString country; + // Java knows nothing but plain old ISO codes, unless Locale.Builder or + // Locale.forLanguageTag() are used, or non-standardized variant field + // content which we ignore. + aLanguageTag.getIsoLanguageScriptCountry( language, script, country); + + // Java 7 FORMAT category is our system locale. + if(!language.isEmpty()) { + OUString prop = "user.language.format=" + language; + pjvm->pushProp(prop); + } + + if(!script.isEmpty()) { + OUString prop = "user.script.format=" + script; + pjvm->pushProp(prop); + } + + if(!country.isEmpty()) { + OUString prop = "user.country.format=" + country; + pjvm->pushProp(prop); + } + } + + xConfRegistry_simple->close(); +} + +/// @throws css::uno::Exception +void getJavaPropsFromSafetySettings( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext> &xCtx) +{ + css::uno::Reference<css::uno::XInterface> xConfRegistry = + xSMgr->createInstanceWithContext( + "com.sun.star.configuration.ConfigurationRegistry", + xCtx); + if(!xConfRegistry.is()) + throw css::uno::RuntimeException( + "javavm.cxx: couldn't get ConfigurationRegistry", nullptr); + + css::uno::Reference<css::registry::XSimpleRegistry> xConfRegistry_simple( + xConfRegistry, css::uno::UNO_QUERY_THROW); + xConfRegistry_simple->open( + "org.openoffice.Office.Java", + true, false); + css::uno::Reference<css::registry::XRegistryKey> xRegistryRootKey = + xConfRegistry_simple->getRootKey(); + + if (xRegistryRootKey.is()) + { + css::uno::Reference<css::registry::XRegistryKey> key_NetAccess= xRegistryRootKey->openKey("VirtualMachine/NetAccess"); + if (key_NetAccess.is()) + { + sal_Int32 val= key_NetAccess->getLongValue(); + OUString sVal; + switch( val) + { + case 0: sVal = "host"; + break; + case 1: sVal = "unrestricted"; + break; + case 3: sVal = "none"; + break; + } + OUString sProperty = "appletviewer.security.mode=" + sVal; + pjvm->pushProp(sProperty); + } + css::uno::Reference<css::registry::XRegistryKey> key_CheckSecurity= xRegistryRootKey->openKey( + "VirtualMachine/Security"); + if( key_CheckSecurity.is()) + { + bool val = static_cast<bool>(key_CheckSecurity->getLongValue()); + OUString sProperty("stardiv.security.disableSecurity="); + if( val) + sProperty += "false"; + else + sProperty += "true"; + pjvm->pushProp( sProperty); + } + } + xConfRegistry_simple->close(); +} + +void setTimeZone(stoc_javavm::JVM * pjvm) noexcept { + /* A Bug in the Java function + ** struct Hjava_util_Properties * java_lang_System_initProperties( + ** struct Hjava_lang_System *this, + ** struct Hjava_util_Properties *props); + ** This function doesn't detect MEZ, MET or "W. Europe Standard Time" + */ + struct tm *tmData; + time_t clock = time(nullptr); + tzset(); + tmData = localtime(&clock); +#ifdef MACOSX + char * p = tmData->tm_zone; +#elif defined(_MSC_VER) + char * p = _tzname[0]; + (void)tmData; +#else + char * p = tzname[0]; + (void)tmData; +#endif + + if (!strcmp(TIMEZONE, p)) + pjvm->pushProp("user.timezone=ECT"); +} + +/// @throws css::uno::Exception +void initVMConfiguration( + stoc_javavm::JVM * pjvm, + const css::uno::Reference<css::lang::XMultiComponentFactory> & xSMgr, + const css::uno::Reference<css::uno::XComponentContext > &xCtx) +{ + stoc_javavm::JVM jvm; + try { + getINetPropsFromConfig(&jvm, xSMgr, xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "can not get INETProps because of " << exception); + } + + try { + getDefaultLocaleFromConfig(&jvm, xSMgr,xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "can not get locale because of " << exception); + } + + try + { + getJavaPropsFromSafetySettings(&jvm, xSMgr, xCtx); + } + catch(const css::uno::Exception & exception) { + SAL_INFO("stoc", "couldn't get safety settings because of " << exception); + } + + *pjvm= jvm; + + // rhbz#1285356, native look will be gtk2, which crashes + // when gtk3 is already loaded. Until there is a solution + // java-side force look and feel to something that doesn't + // crash when we are using gtk3 + if (getenv("STOC_FORCE_SYSTEM_LAF")) + pjvm->pushProp("swing.systemlaf=javax.swing.plaf.metal.MetalLookAndFeel"); + + setTimeZone(pjvm); +} + +class DetachCurrentThread { +public: + explicit DetachCurrentThread(JavaVM * jvm): m_jvm(jvm) {} + + ~DetachCurrentThread() { + if (m_jvm->DetachCurrentThread() != 0) { + OSL_ASSERT(false); + } + } + + DetachCurrentThread(const DetachCurrentThread&) = delete; + DetachCurrentThread& operator=(const DetachCurrentThread&) = delete; + +private: + JavaVM * m_jvm; +}; + +} + +JavaVirtualMachine::JavaVirtualMachine( + css::uno::Reference< css::uno::XComponentContext > const & rContext): + WeakComponentImplHelper(m_aMutex), + m_xContext(rContext), + m_bDisposed(false), + m_pJavaVm(nullptr), + m_aAttachGuards(destroyAttachGuards) // TODO check for validity +{} + +void SAL_CALL +JavaVirtualMachine::initialize(css::uno::Sequence< css::uno::Any > const & + rArguments) +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", static_cast< cppu::OWeakObject * >(this)); + if (m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "bad call to initialize", + static_cast< cppu::OWeakObject * >(this)); + css::beans::NamedValue val; + if (rArguments.getLength() == 1 && (rArguments[0] >>= val) && val.Name == "UnoVirtualMachine" ) + { + OSL_ENSURE( + sizeof (sal_Int64) >= sizeof (jvmaccess::UnoVirtualMachine *), + "Pointer cannot be represented as sal_Int64"); + sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( + static_cast< jvmaccess::UnoVirtualMachine * >(nullptr)); + val.Value >>= nPointer; + m_xUnoVirtualMachine = + reinterpret_cast< jvmaccess::UnoVirtualMachine * >(nPointer); + } else { + OSL_ENSURE( + sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *), + "Pointer cannot be represented as sal_Int64"); + sal_Int64 nPointer = reinterpret_cast< sal_Int64 >( + static_cast< jvmaccess::VirtualMachine * >(nullptr)); + if (rArguments.getLength() == 1) + rArguments[0] >>= nPointer; + rtl::Reference< jvmaccess::VirtualMachine > vm( + reinterpret_cast< jvmaccess::VirtualMachine * >(nPointer)); + if (vm.is()) { + try { + m_xUnoVirtualMachine = new jvmaccess::UnoVirtualMachine(vm, nullptr); + } catch (jvmaccess::UnoVirtualMachine::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::UnoVirtualMachine::CreationException", + static_cast< cppu::OWeakObject * >(this), anyEx ); + } + } + } + if (!m_xUnoVirtualMachine.is()) { + throw css::lang::IllegalArgumentException( + "sequence of exactly one any containing either (a) a" + " com.sun.star.beans.NamedValue with Name" + " \"UnoVirtualMachine\" and Value a hyper representing a" + " non-null pointer to a jvmaccess:UnoVirtualMachine, or (b)" + " a hyper representing a non-null pointer to a" + " jvmaccess::VirtualMachine required", + static_cast< cppu::OWeakObject * >(this), 0); + } + m_xVirtualMachine = m_xUnoVirtualMachine->getVirtualMachine(); +} + +OUString SAL_CALL JavaVirtualMachine::getImplementationName() +{ + return "com.sun.star.comp.stoc.JavaVirtualMachine"; +} + +sal_Bool SAL_CALL +JavaVirtualMachine::supportsService(OUString const & rServiceName) +{ + return cppu::supportsService(this, rServiceName); +} + +css::uno::Sequence< OUString > SAL_CALL +JavaVirtualMachine::getSupportedServiceNames() +{ + return { "com.sun.star.java.JavaVirtualMachine" }; +} + +css::uno::Any SAL_CALL +JavaVirtualMachine::getJavaVM(css::uno::Sequence< sal_Int8 > const & rProcessId) +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", static_cast< cppu::OWeakObject * >(this)); + css::uno::Sequence< sal_Int8 > aId(16); + rtl_getGlobalProcessId(reinterpret_cast< sal_uInt8 * >(aId.getArray())); + enum ReturnType { + RETURN_JAVAVM, RETURN_VIRTUALMACHINE, RETURN_UNOVIRTUALMACHINE }; + ReturnType returnType = + rProcessId.getLength() == 17 && rProcessId[16] == 0 + ? RETURN_VIRTUALMACHINE + : rProcessId.getLength() == 17 && rProcessId[16] == 1 + ? RETURN_UNOVIRTUALMACHINE + : RETURN_JAVAVM; + css::uno::Sequence< sal_Int8 > aProcessId(rProcessId); + if (returnType != RETURN_JAVAVM) + aProcessId.realloc(16); + if (aId != aProcessId) + return css::uno::Any(); + + std::unique_ptr<JavaInfo> info; + while (!m_xVirtualMachine.is()) // retry until successful + { + stoc_javavm::JVM aJvm; + initVMConfiguration(&aJvm, m_xContext->getServiceManager(), + m_xContext); + const std::vector<OUString> & props = aJvm.getProperties(); + std::vector<OUString> options; + options.reserve(props.size()); + for (auto const& i : props) + { + options.push_back(i.startsWith("-") ? i : "-D" + i); + } + + JNIEnv * pMainThreadEnv = nullptr; + javaFrameworkError errcode = JFW_E_NONE; + + if (getenv("STOC_FORCE_NO_JRE")) + errcode = JFW_E_NO_SELECT; + else + errcode = jfw_startVM(info.get(), options, & m_pJavaVm, + & pMainThreadEnv); + + bool bStarted = false; + switch (errcode) + { + case JFW_E_NONE: bStarted = true; break; + case JFW_E_NO_SELECT: + { + // No Java configured. We silently run the Java configuration + info.reset(); + javaFrameworkError errFind = jfw_findAndSelectJRE(&info); + if (getenv("STOC_FORCE_NO_JRE")) + errFind = JFW_E_NO_JAVA_FOUND; + if (errFind == JFW_E_NONE) + { + continue; + } + else if (errFind == JFW_E_NO_JAVA_FOUND) + { + + //Warning MessageBox: + //%PRODUCTNAME requires a Java runtime environment (JRE) to perform this task. + //Please install a JRE and restart %PRODUCTNAME. + css::java::JavaNotFoundException exc( + "JavaVirtualMachine::getJavaVM failed because" + " No suitable JRE found!", + static_cast< cppu::OWeakObject * >(this)); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + else + { + //An unexpected error occurred + throw css::uno::RuntimeException( + "[JavaVirtualMachine]:An unexpected error occurred" + " while searching for a Java, " + OUString::number(errFind), nullptr); + } + } + case JFW_E_INVALID_SETTINGS: + { + //Warning MessageBox: + // The %PRODUCTNAME configuration has been changed. Under Tools + // - Options - %PRODUCTNAME - Java, select the Java runtime environment + // you want to have used by %PRODUCTNAME. + css::java::InvalidJavaSettingsException exc( + "JavaVirtualMachine::getJavaVM failed because" + " Java settings have changed!", + static_cast< cppu::OWeakObject * >(this)); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + case JFW_E_JAVA_DISABLED: + { + if (comphelper::IsContextFlagActive("DontEnableJava")) + return css::uno::Any(); + + //QueryBox: + //%PRODUCTNAME requires a Java runtime environment (JRE) to perform + //this task. However, use of a JRE has been disabled. Do you want to + //enable the use of a JRE now? + css::java::JavaDisabledException exc( + "JavaVirtualMachine::getJavaVM failed because Java is disabled!", + static_cast< cppu::OWeakObject * >(this)); + if( ! askForRetry(css::uno::Any(exc))) + return css::uno::Any(); + continue; + } + case JFW_E_VM_CREATION_FAILED: + { + //If the creation failed because the JRE has been uninstalled then + //we search another one. As long as there is a javaldx, we should + //never come into this situation. javaldx checks always if the JRE + //still exist. + std::unique_ptr<JavaInfo> aJavaInfo; + if (JFW_E_NONE == jfw_getSelectedJRE(&aJavaInfo)) + { + bool bExist = false; + if (JFW_E_NONE == jfw_existJRE(aJavaInfo.get(), &bExist)) + { + if (!bExist + && ! (aJavaInfo->nRequirements & JFW_REQUIRE_NEEDRESTART)) + { + info.reset(); + javaFrameworkError errFind = jfw_findAndSelectJRE( + &info); + if (errFind == JFW_E_NONE) + { + continue; + } + } + } + } + + //Error: %PRODUCTNAME requires a Java + //runtime environment (JRE) to perform this task. The selected JRE + //is defective. Please select another version or install a new JRE + //and select it under Tools - Options - %PRODUCTNAME - Java. + css::java::JavaVMCreationFailureException exc( + "JavaVirtualMachine::getJavaVM failed because Java is defective!", + static_cast< cppu::OWeakObject * >(this), 0); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + case JFW_E_RUNNING_JVM: + { + //This service should make sure that we do not start java twice. + OSL_ASSERT(false); + break; + } + case JFW_E_NEED_RESTART: + { + //Error: + //For the selected Java runtime environment to work properly, + //%PRODUCTNAME must be restarted. Please restart %PRODUCTNAME now. + css::java::RestartRequiredException exc( + "JavaVirtualMachine::getJavaVM failed because " + "Office must be restarted before Java can be used!", + static_cast< cppu::OWeakObject * >(this)); + askForRetry(css::uno::Any(exc)); + return css::uno::Any(); + } + default: + //RuntimeException: error is somewhere in the java framework. + //An unexpected error occurred + throw css::uno::RuntimeException( + "[JavaVirtualMachine]:An unexpected error occurred" + " while starting Java!", nullptr); + } + + if (bStarted) + { + { + DetachCurrentThread detach(m_pJavaVm); + // necessary to make debugging work; this thread will be + // suspended when the destructor of detach returns + m_xVirtualMachine = new jvmaccess::VirtualMachine( + m_pJavaVm, JNI_VERSION_1_2, true, pMainThreadEnv); + setUpUnoVirtualMachine(pMainThreadEnv); + } + // Listen for changes in the configuration (e.g. proxy settings): + // TODO this is done too late; changes to the configuration done + // after the above call to initVMConfiguration are lost + registerConfigChangesListener(); + + break; + } + } + if (!m_xUnoVirtualMachine.is()) { + try { + jvmaccess::VirtualMachine::AttachGuard guard(m_xVirtualMachine); + setUpUnoVirtualMachine(guard.getEnvironment()); + } catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::VirtualMachine::AttachGuard::CreationException occurred", + static_cast< cppu::OWeakObject * >(this), anyEx ); + } + } + switch (returnType) { + default: // RETURN_JAVAVM + if (m_pJavaVm == nullptr) { + throw css::uno::RuntimeException( + "JavaVirtualMachine service was initialized in a way" + " that the requested JavaVM pointer is not available", + static_cast< cppu::OWeakObject * >(this)); + } + return css::uno::Any(reinterpret_cast< sal_IntPtr >(m_pJavaVm)); + case RETURN_VIRTUALMACHINE: + OSL_ASSERT(sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *)); + return css::uno::Any( + reinterpret_cast< sal_Int64 >( + m_xUnoVirtualMachine->getVirtualMachine().get())); + case RETURN_UNOVIRTUALMACHINE: + OSL_ASSERT(sizeof (sal_Int64) >= sizeof (jvmaccess::VirtualMachine *)); + return css::uno::Any( + reinterpret_cast< sal_Int64 >(m_xUnoVirtualMachine.get())); + } +} + +sal_Bool SAL_CALL JavaVirtualMachine::isVMStarted() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), static_cast< cppu::OWeakObject * >(this)); + return m_xUnoVirtualMachine.is(); +} + +sal_Bool SAL_CALL JavaVirtualMachine::isVMEnabled() +{ + { + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), static_cast< cppu::OWeakObject * >(this)); + } +// stoc_javavm::JVM aJvm; +// initVMConfiguration(&aJvm, m_xContext->getServiceManager(), m_xContext); +// return aJvm.isEnabled(); + //ToDo + bool bEnabled = false; + if (jfw_getEnabled( & bEnabled) != JFW_E_NONE) + throw css::uno::RuntimeException(); + return bEnabled; +} + +sal_Bool SAL_CALL JavaVirtualMachine::isThreadAttached() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + OUString(), static_cast< cppu::OWeakObject * >(this)); + // TODO isThreadAttached only returns true if the thread was attached via + // registerThread: + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + return pStack != nullptr && !pStack->empty(); +} + +void SAL_CALL JavaVirtualMachine::registerThread() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", static_cast< cppu::OWeakObject * >(this)); + if (!m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::registerThread: null VirtualMachine", + static_cast< cppu::OWeakObject * >(this)); + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + if (pStack == nullptr) + { + pStack = new GuardStack; + m_aAttachGuards.setData(pStack); + } + try + { + pStack->push( + new jvmaccess::VirtualMachine::AttachGuard( + m_xUnoVirtualMachine->getVirtualMachine())); + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "JavaVirtualMachine::registerThread: jvmaccess::" + "VirtualMachine::AttachGuard::CreationException", + static_cast< cppu::OWeakObject * >(this), anyEx ); + } +} + +void SAL_CALL JavaVirtualMachine::revokeThread() +{ + osl::MutexGuard aGuard(m_aMutex); + if (m_bDisposed) + throw css::lang::DisposedException( + "", static_cast< cppu::OWeakObject * >(this)); + if (!m_xUnoVirtualMachine.is()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::revokeThread: null VirtualMachine", + static_cast< cppu::OWeakObject * >(this)); + GuardStack * pStack + = static_cast< GuardStack * >(m_aAttachGuards.getData()); + if (pStack == nullptr || pStack->empty()) + throw css::uno::RuntimeException( + "JavaVirtualMachine::revokeThread: no matching registerThread", + static_cast< cppu::OWeakObject * >(this)); + delete pStack->top(); + pStack->pop(); +} + +void SAL_CALL +JavaVirtualMachine::disposing(css::lang::EventObject const & rSource) +{ + osl::MutexGuard aGuard(m_aMutex); + if (rSource.Source == m_xInetConfiguration) + m_xInetConfiguration.clear(); + if (rSource.Source == m_xJavaConfiguration) + m_xJavaConfiguration.clear(); +} + +void SAL_CALL JavaVirtualMachine::elementInserted( + css::container::ContainerEvent const &) +{} + +void SAL_CALL JavaVirtualMachine::elementRemoved( + css::container::ContainerEvent const &) +{} + +// If a user changes the setting, for example for proxy settings, then this +// function will be called from the configuration manager. Even if the .xml +// file does not contain an entry yet and that entry has to be inserted, this +// function will be called. We call java.lang.System.setProperty for the new +// values. +void SAL_CALL JavaVirtualMachine::elementReplaced( + css::container::ContainerEvent const & rEvent) +{ + // TODO Using the new value stored in rEvent is wrong here. If two threads + // receive different elementReplaced calls in quick succession, it is + // unspecified which changes the JVM's system properties last. A correct + // solution must atomically (i.e., protected by a mutex) read the latest + // value from the configuration and set it as a system property at the JVM. + + OUString aAccessor; + rEvent.Accessor >>= aAccessor; + OUString aPropertyName; + OUString aPropertyName2; + OUString aPropertyValue; + bool bSecurityChanged = false; + if ( aAccessor == "ooInetProxyType" ) + { + // Proxy none, manually + sal_Int32 value = 0; + rEvent.Element >>= value; + setINetSettingsInVM(value != 0); + return; + } + else if ( aAccessor == "ooInetHTTPProxyName" ) + { + aPropertyName = "http.proxyHost"; + rEvent.Element >>= aPropertyValue; + } + else if ( aAccessor == "ooInetHTTPProxyPort" ) + { + aPropertyName = "http.proxyPort"; + sal_Int32 n = 0; + rEvent.Element >>= n; + aPropertyValue = OUString::number(n); + } + else if ( aAccessor == "ooInetHTTPSProxyName" ) + { + aPropertyName = "https.proxyHost"; + rEvent.Element >>= aPropertyValue; + } + else if ( aAccessor == "ooInetHTTPSProxyPort" ) + { + aPropertyName = "https.proxyPort"; + sal_Int32 n = 0; + rEvent.Element >>= n; + aPropertyValue = OUString::number(n); + } + else if ( aAccessor == "ooInetFTPProxyName" ) + { + aPropertyName = "ftp.proxyHost"; + rEvent.Element >>= aPropertyValue; + } + else if ( aAccessor == "ooInetFTPProxyPort" ) + { + aPropertyName = "ftp.proxyPort"; + sal_Int32 n = 0; + rEvent.Element >>= n; + aPropertyValue = OUString::number(n); + } + else if ( aAccessor == "ooInetNoProxy" ) + { + aPropertyName = "http.nonProxyHosts"; + aPropertyName2 = "ftp.nonProxyHosts"; + rEvent.Element >>= aPropertyValue; + aPropertyValue = aPropertyValue.replace(';', '|'); + } + else if ( aAccessor == "NetAccess" ) + { + aPropertyName = "appletviewer.security.mode"; + sal_Int32 n = 0; + if (rEvent.Element >>= n) + switch (n) + { + case 0: + aPropertyValue = "host"; + break; + case 1: + aPropertyValue = "unrestricted"; + break; + case 3: + aPropertyValue = "none"; + break; + } + else + return; + bSecurityChanged = true; + } + else if ( aAccessor == "Security" ) + { + aPropertyName = "stardiv.security.disableSecurity"; + bool b; + if (rEvent.Element >>= b) + if (b) + aPropertyValue = "false"; + else + aPropertyValue = "true"; + else + return; + bSecurityChanged = true; + } + else + return; + + rtl::Reference< jvmaccess::VirtualMachine > xVirtualMachine; + { + osl::MutexGuard aGuard(m_aMutex); + if (m_xUnoVirtualMachine.is()) { + xVirtualMachine = m_xUnoVirtualMachine->getVirtualMachine(); + } + } + if (!xVirtualMachine.is()) + return; + + try + { + jvmaccess::VirtualMachine::AttachGuard aAttachGuard( + xVirtualMachine); + JNIEnv * pJNIEnv = aAttachGuard.getEnvironment(); + + // call java.lang.System.setProperty + // String setProperty( String key, String value) + jclass jcSystem= pJNIEnv->FindClass("java/lang/System"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/lang/System", nullptr); + jmethodID jmSetProps= pJNIEnv->GetStaticMethodID( jcSystem, "setProperty","(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.setProperty", nullptr); + + jstring jsPropName= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyName.getStr()), aPropertyName.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + + // remove the property if it does not have a value ( user left the dialog field empty) + // or if the port is set to 0 + aPropertyValue= aPropertyValue.trim(); + if( aPropertyValue.isEmpty() || + ( ( aPropertyName == "ftp.proxyPort" || aPropertyName == "http.proxyPort" /*|| aPropertyName == "socksProxyPort"*/ ) && aPropertyValue == "0" ) + ) + { + // call java.lang.System.getProperties + jmethodID jmGetProps= pJNIEnv->GetStaticMethodID( jcSystem, "getProperties","()Ljava/util/Properties;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getProperties", nullptr); + jobject joProperties= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetProps); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.getProperties", nullptr); + // call java.util.Properties.remove + jclass jcProperties= pJNIEnv->FindClass("java/util/Properties"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/util/Properties", nullptr); + jmethodID jmRemove= pJNIEnv->GetMethodID( jcProperties, "remove", "(Ljava/lang/Object;)Ljava/lang/Object;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.util.Properties.remove", nullptr); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsPropName); + + // special case for ftp.nonProxyHosts and http.nonProxyHosts. The office only + // has a value for two java properties + if (!aPropertyName2.isEmpty()) + { + jstring jsPropName2= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyName2.getStr()), aPropertyName2.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsPropName2); + } + } + else + { + // Change the Value of the property + jstring jsPropValue= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyValue.getStr()), aPropertyValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsPropName, jsPropValue); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + + // special case for ftp.nonProxyHosts and http.nonProxyHosts. The office only + // has a value for two java properties + if (!aPropertyName2.isEmpty()) + { + jstring jsPropName2= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyName2.getStr()), aPropertyName2.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jsPropValue= pJNIEnv->NewString( reinterpret_cast<jchar const *>(aPropertyValue.getStr()), aPropertyValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsPropName2, jsPropValue); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + } + + // If the settings for Security and NetAccess changed then we have to notify the SandboxSecurity + // SecurityManager + // call System.getSecurityManager() + if (bSecurityChanged) + { + jmethodID jmGetSecur= pJNIEnv->GetStaticMethodID( jcSystem,"getSecurityManager","()Ljava/lang/SecurityManager;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getSecurityManager", nullptr); + jobject joSecur= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetSecur); + if (joSecur != nullptr) + { + // Make sure the SecurityManager is our SandboxSecurity + // FindClass("com.sun.star.lib.sandbox.SandboxSecurityManager" only worked at the first time + // this code was executed. Maybe it is a security feature. However, all attempts to debug the + // SandboxSecurity class (maybe the VM invokes checkPackageAccess) failed. +// jclass jcSandboxSec= pJNIEnv->FindClass("com.sun.star.lib.sandbox.SandboxSecurity"); +// if(pJNIEnv->ExceptionOccurred()) throw RuntimeException("JNI:FindClass com.sun.star.lib.sandbox.SandboxSecurity"); +// jboolean bIsSand= pJNIEnv->IsInstanceOf( joSecur, jcSandboxSec); + // The SecurityManagers class Name must be com.sun.star.lib.sandbox.SandboxSecurity + jclass jcSec= pJNIEnv->GetObjectClass( joSecur); + jclass jcClass= pJNIEnv->FindClass("java/lang/Class"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java.lang.Class", nullptr); + jmethodID jmName= pJNIEnv->GetMethodID( jcClass,"getName","()Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.lang.Class.getName", nullptr); + jstring jsClass= static_cast<jstring>(pJNIEnv->CallObjectMethod( jcSec, jmName)); + const jchar* jcharName= pJNIEnv->GetStringChars( jsClass, nullptr); + OUString sName(reinterpret_cast<sal_Unicode const *>(jcharName)); + bool bIsSandbox; + bIsSandbox = sName == "com.sun.star.lib.sandbox.SandboxSecurity"; + pJNIEnv->ReleaseStringChars( jsClass, jcharName); + + if (bIsSandbox) + { + // call SandboxSecurity.reset + jmethodID jmReset= pJNIEnv->GetMethodID( jcSec,"reset","()V"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID com.sun.star.lib.sandbox.SandboxSecurity.reset", nullptr); + pJNIEnv->CallVoidMethod( joSecur, jmReset); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallVoidMethod com.sun.star.lib.sandbox.SandboxSecurity.reset", nullptr); + } + } + } + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::VirtualMachine::AttachGuard::CreationException", + static_cast< cppu::OWeakObject * >(this), anyEx ); + } +} + +JavaVirtualMachine::~JavaVirtualMachine() +{ + if (m_xInetConfiguration.is()) + // We should never get here, but just in case... + try + { + m_xInetConfiguration->removeContainerListener(this); + } + catch (css::uno::Exception &) + { + OSL_FAIL("com.sun.star.uno.Exception caught"); + } + if (m_xJavaConfiguration.is()) + // We should never get here, but just in case... + try + { + m_xJavaConfiguration->removeContainerListener(this); + } + catch (css::uno::Exception &) + { + OSL_FAIL("com.sun.star.uno.Exception caught"); + } +} + +void SAL_CALL JavaVirtualMachine::disposing() +{ + css::uno::Reference< css::container::XContainer > xContainer1; + css::uno::Reference< css::container::XContainer > xContainer2; + { + osl::MutexGuard aGuard(m_aMutex); + m_bDisposed = true; + xContainer1 = m_xInetConfiguration; + m_xInetConfiguration.clear(); + xContainer2 = m_xJavaConfiguration; + m_xJavaConfiguration.clear(); + } + if (xContainer1.is()) + xContainer1->removeContainerListener(this); + if (xContainer2.is()) + xContainer2->removeContainerListener(this); +} + +/*We listen to changes in the configuration. For example, the user changes the proxy + settings in the options dialog (menu tools). Then we are notified of this change and + if the java vm is already running we change the properties (System.lang.System.setProperties) + through JNI. + To receive notifications this class implements XContainerListener. +*/ +void JavaVirtualMachine::registerConfigChangesListener() +{ + try + { + css::uno::Reference< css::lang::XMultiServiceFactory > xConfigProvider( + m_xContext->getValueByName( + "/singletons/com.sun.star.configuration.theDefaultProvider"), + css::uno::UNO_QUERY); + + if (xConfigProvider.is()) + { + // We register this instance as listener to changes in org.openoffice.Inet/Settings + // arguments for ConfigurationAccess + css::uno::Sequence<css::uno::Any> aArguments(comphelper::InitAnyPropertySequence( + { + {"nodepath", css::uno::Any(OUString("org.openoffice.Inet/Settings"))}, + {"depth", css::uno::Any(sal_Int32(-1))} + })); + m_xInetConfiguration.set( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments), + css::uno::UNO_QUERY); + + if (m_xInetConfiguration.is()) + m_xInetConfiguration->addContainerListener(this); + + // now register as listener to changes in org.openoffice.Java/VirtualMachine + css::uno::Sequence<css::uno::Any> aArguments2(comphelper::InitAnyPropertySequence( + { + {"nodepath", css::uno::Any(OUString("org.openoffice.Office.Java/VirtualMachine"))}, + {"depth", css::uno::Any(sal_Int32(-1))} // depth: -1 means unlimited + })); + m_xJavaConfiguration.set( + xConfigProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationAccess", + aArguments2), + css::uno::UNO_QUERY); + + if (m_xJavaConfiguration.is()) + m_xJavaConfiguration->addContainerListener(this); + } + }catch(const css::uno::Exception & e) + { + SAL_INFO("stoc", "could not set up listener for Configuration because of >" << e << "<"); + } +} + +// param true: all Inet setting are set as Java Properties on a live VM. +// false: the Java net properties are set to empty value. +void JavaVirtualMachine::setINetSettingsInVM(bool set_reset) +{ + osl::MutexGuard aGuard(m_aMutex); + try + { + if (m_xUnoVirtualMachine.is()) + { + jvmaccess::VirtualMachine::AttachGuard aAttachGuard( + m_xUnoVirtualMachine->getVirtualMachine()); + JNIEnv * pJNIEnv = aAttachGuard.getEnvironment(); + + // The Java Properties + OUString sFtpProxyHost("ftp.proxyHost"); + OUString sFtpProxyPort("ftp.proxyPort"); + OUString sFtpNonProxyHosts ("ftp.nonProxyHosts"); + OUString sHttpProxyHost("http.proxyHost"); + OUString sHttpProxyPort("http.proxyPort"); + OUString sHttpNonProxyHosts("http.nonProxyHosts"); + + // create Java Properties as JNI strings + jstring jsFtpProxyHost= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sFtpProxyHost.getStr()), sFtpProxyHost.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsFtpProxyPort= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sFtpProxyPort.getStr()), sFtpProxyPort.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsFtpNonProxyHosts= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sFtpNonProxyHosts.getStr()), sFtpNonProxyHosts.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsHttpProxyHost= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpProxyHost.getStr()), sHttpProxyHost.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsHttpProxyPort= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpProxyPort.getStr()), sHttpProxyPort.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + jstring jsHttpNonProxyHosts= pJNIEnv->NewString( reinterpret_cast<jchar const *>(sHttpNonProxyHosts.getStr()), sHttpNonProxyHosts.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + + // prepare java.lang.System.setProperty + jclass jcSystem= pJNIEnv->FindClass("java/lang/System"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/lang/System", nullptr); + jmethodID jmSetProps= pJNIEnv->GetStaticMethodID( jcSystem, "setProperty","(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.setProperty", nullptr); + + // call java.lang.System.getProperties + jmethodID jmGetProps= pJNIEnv->GetStaticMethodID( jcSystem, "getProperties","()Ljava/util/Properties;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetStaticMethodID java.lang.System.getProperties", nullptr); + jobject joProperties= pJNIEnv->CallStaticObjectMethod( jcSystem, jmGetProps); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.getProperties", nullptr); + // prepare java.util.Properties.remove + jclass jcProperties= pJNIEnv->FindClass("java/util/Properties"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:FindClass java/util/Properties", nullptr); + + if (set_reset) + { + // Set all network properties with the VM + JVM jvm; + getINetPropsFromConfig( &jvm, m_xContext->getServiceManager(), m_xContext); + const ::std::vector< OUString> & Props = jvm.getProperties(); + + for( auto& prop : Props) + { + sal_Int32 index= prop.indexOf( '='); + std::u16string_view propName= prop.subView( 0, index); + OUString propValue= prop.copy( index + 1); + + if( propName == sFtpProxyHost) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsFtpProxyHost, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sFtpProxyPort) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsFtpProxyPort, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sFtpNonProxyHosts) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsFtpNonProxyHosts, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sHttpProxyHost) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpProxyHost, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sHttpProxyPort) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpProxyPort, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + else if( propName == sHttpNonProxyHosts) + { + jstring jsVal= pJNIEnv->NewString( reinterpret_cast<jchar const *>(propValue.getStr()), propValue.getLength()); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:NewString", nullptr); + pJNIEnv->CallStaticObjectMethod( jcSystem, jmSetProps, jsHttpNonProxyHosts, jsVal); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:CallStaticObjectMethod java.lang.System.setProperty", nullptr); + } + } + } + else + { + // call java.util.Properties.remove + jmethodID jmRemove= pJNIEnv->GetMethodID( jcProperties, "remove", "(Ljava/lang/Object;)Ljava/lang/Object;"); + if(pJNIEnv->ExceptionOccurred()) throw css::uno::RuntimeException("JNI:GetMethodID java.util.Property.remove", nullptr); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsFtpProxyHost); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsFtpProxyPort); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsFtpNonProxyHosts); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpProxyHost); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpProxyPort); + pJNIEnv->CallObjectMethod( joProperties, jmRemove, jsHttpNonProxyHosts); + } + } + } + catch (css::uno::RuntimeException &) + { + OSL_FAIL("RuntimeException"); + } + catch (jvmaccess::VirtualMachine::AttachGuard::CreationException &) + { + OSL_FAIL("jvmaccess::VirtualMachine::AttachGuard::CreationException"); + } +} + +void JavaVirtualMachine::setUpUnoVirtualMachine(JNIEnv * environment) { + css::uno::Reference< css::util::XMacroExpander > exp = css::util::theMacroExpander::get(m_xContext); + OUString baseUrl; + try { + baseUrl = exp->expandMacros("$URE_INTERNAL_JAVA_DIR/"); + } catch (css::lang::IllegalArgumentException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "css::lang::IllegalArgumentException", + static_cast< cppu::OWeakObject * >(this), anyEx ); + } + OUString classPath; + try { + classPath = exp->expandMacros("$URE_INTERNAL_JAVA_CLASSPATH"); + } catch (css::lang::IllegalArgumentException &) {} + jclass class_URLClassLoader = environment->FindClass( + "java/net/URLClassLoader"); + if (class_URLClassLoader == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URLClassLoader = environment->GetMethodID( + class_URLClassLoader, "<init>", "([Ljava/net/URL;)V"); + if (ctor_URLClassLoader == nullptr) { + handleJniException(environment); + } + jclass class_URL = environment->FindClass("java/net/URL"); + if (class_URL == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URL_1 = environment->GetMethodID( + class_URL, "<init>", "(Ljava/lang/String;)V"); + if (ctor_URL_1 == nullptr) { + handleJniException(environment); + } + jvalue args[3]; + args[0].l = environment->NewString( + reinterpret_cast< jchar const * >(baseUrl.getStr()), + static_cast< jsize >(baseUrl.getLength())); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jobject base = environment->NewObjectA(class_URL, ctor_URL_1, args); + if (base == nullptr) { + handleJniException(environment); + } + jmethodID ctor_URL_2 = environment->GetMethodID( + class_URL, "<init>", "(Ljava/net/URL;Ljava/lang/String;)V"); + if (ctor_URL_2 == nullptr) { + handleJniException(environment); + } + jobjectArray classpath = jvmaccess::ClassPath::translateToUrls( + m_xContext, environment, classPath); + if (classpath == nullptr) { + handleJniException(environment); + } + args[0].l = base; + args[1].l = environment->NewStringUTF("unoloader.jar"); + if (args[1].l == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewObjectA(class_URL, ctor_URL_2, args); + if (args[0].l == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewObjectArray(1, class_URL, args[0].l); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jobject cl1 = environment->NewObjectA( + class_URLClassLoader, ctor_URLClassLoader, args); + if (cl1 == nullptr) { + handleJniException(environment); + } + jmethodID method_loadClass = environment->GetMethodID( + class_URLClassLoader, "loadClass", + "(Ljava/lang/String;)Ljava/lang/Class;"); + if (method_loadClass == nullptr) { + handleJniException(environment); + } + args[0].l = environment->NewStringUTF( + "com.sun.star.lib.unoloader.UnoClassLoader"); + if (args[0].l == nullptr) { + handleJniException(environment); + } + jclass class_UnoClassLoader = static_cast< jclass >( + environment->CallObjectMethodA(cl1, method_loadClass, args)); + if (class_UnoClassLoader == nullptr) { + handleJniException(environment); + } + jmethodID ctor_UnoClassLoader = environment->GetMethodID( + class_UnoClassLoader, "<init>", + "(Ljava/net/URL;[Ljava/net/URL;Ljava/lang/ClassLoader;)V"); + if (ctor_UnoClassLoader == nullptr) { + handleJniException(environment); + } + args[0].l = base; + args[1].l = classpath; + args[2].l = cl1; + jobject cl2 = environment->NewObjectA( + class_UnoClassLoader, ctor_UnoClassLoader, args); + if (cl2 == nullptr) { + handleJniException(environment); + } + try { + m_xUnoVirtualMachine = new jvmaccess::UnoVirtualMachine( + m_xVirtualMachine, cl2); + } catch (jvmaccess::UnoVirtualMachine::CreationException &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "jvmaccess::UnoVirtualMachine::CreationException", + static_cast< cppu::OWeakObject * >(this), anyEx ); + } +} + +void JavaVirtualMachine::handleJniException(JNIEnv * environment) { +#if defined DBG_UTIL + environment->ExceptionDescribe(); +#else + environment->ExceptionClear(); +#endif + throw css::uno::RuntimeException( + "JNI exception occurred", + static_cast< cppu::OWeakObject * >(this)); +} + + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_JavaVM_get_implementation( + css::uno::XComponentContext* context , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new JavaVirtualMachine(context)); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/javavm.hxx b/stoc/source/javavm/javavm.hxx new file mode 100644 index 000000000..e3551b292 --- /dev/null +++ b/stoc/source/javavm/javavm.hxx @@ -0,0 +1,147 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +#include <com/sun/star/container/XContainerListener.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/java/XJavaThreadRegister_11.hpp> +#include <com/sun/star/java/XJavaVM.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <osl/thread.hxx> +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> + +namespace com::sun::star { + namespace container { class XContainer; } + namespace uno { class XComponentContext; } +} +namespace jvmaccess { + class UnoVirtualMachine; + class VirtualMachine; +} + +namespace stoc_javavm { + +class JavaVirtualMachine: + private cppu::BaseMutex, + public cppu::WeakComponentImplHelper< + css::lang::XInitialization, css::lang::XServiceInfo, css::java::XJavaVM, + css::java::XJavaThreadRegister_11, css::container::XContainerListener> +{ +public: + explicit JavaVirtualMachine( + css::uno::Reference< + css::uno::XComponentContext > const & rContext); + + // XInitialization + virtual void SAL_CALL + initialize(css::uno::Sequence< css::uno::Any > const & + rArguments) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL + supportsService(OUString const & rServiceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + // XJavaVM + virtual css::uno::Any SAL_CALL + getJavaVM(css::uno::Sequence< sal_Int8 > const & rProcessId) override; + + virtual sal_Bool SAL_CALL isVMStarted() override; + + virtual sal_Bool SAL_CALL isVMEnabled() override; + + // XJavaThreadRegister_11 + virtual sal_Bool SAL_CALL isThreadAttached() override; + + virtual void SAL_CALL registerThread() override; + + virtual void SAL_CALL revokeThread() override; + + // XContainerListener + virtual void SAL_CALL + disposing(css::lang::EventObject const & rSource) override; + + virtual void SAL_CALL + elementInserted(css::container::ContainerEvent const & rEvent) override; + + virtual void SAL_CALL + elementRemoved(css::container::ContainerEvent const & rEvent) override; + + virtual void SAL_CALL + elementReplaced(css::container::ContainerEvent const & rEvent) override; + +private: + JavaVirtualMachine(JavaVirtualMachine const &) = delete; + void operator =(const JavaVirtualMachine&) = delete; + + virtual ~JavaVirtualMachine() override; + + virtual void SAL_CALL disposing() override; + + void registerConfigChangesListener(); + + void setINetSettingsInVM(bool set_reset); + + void setUpUnoVirtualMachine(JNIEnv * environment); + + void handleJniException(JNIEnv * environment); + + css::uno::Reference< css::uno::XComponentContext > + m_xContext; + + // the following are controlled by BaseMutex::m_aMutex: + bool m_bDisposed; + rtl::Reference< jvmaccess::VirtualMachine > m_xVirtualMachine; + rtl::Reference< jvmaccess::UnoVirtualMachine > m_xUnoVirtualMachine; + JavaVM * m_pJavaVm; + // If the first creation of Java failed and this flag is set then the + // next call to getJavaVM throws a RuntimException. This is useful when + // the second attempt to create Java might cause a crash. + css::uno::Reference< css::container::XContainer > + m_xInetConfiguration; + css::uno::Reference< css::container::XContainer > + m_xJavaConfiguration; // for Java settings + + osl::ThreadData m_aAttachGuards; +}; + +} + +#endif // INCLUDED_STOC_SOURCE_JAVAVM_JAVAVM_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/jvmargs.cxx b/stoc/source/javavm/jvmargs.cxx new file mode 100644 index 000000000..f98f22ec0 --- /dev/null +++ b/stoc/source/javavm/jvmargs.cxx @@ -0,0 +1,32 @@ +/* -*- 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 "jvmargs.hxx" +#include <rtl/ustring.hxx> + +namespace stoc_javavm +{ +JVM::JVM() noexcept //: _enabled(sal_False) +{ +} + +void JVM::pushProp(const OUString& property) { _props.push_back(property); } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/javavm/jvmargs.hxx b/stoc/source/javavm/jvmargs.hxx new file mode 100644 index 000000000..adc41ffa1 --- /dev/null +++ b/stoc/source/javavm/jvmargs.hxx @@ -0,0 +1,58 @@ +/* -*- 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 . + */ + + +#ifndef INCLUDED_STOC_SOURCE_JAVAVM_JVMARGS_HXX +#define INCLUDED_STOC_SOURCE_JAVAVM_JVMARGS_HXX + + +#include <vector> +#include <rtl/ustring.hxx> + +#if defined __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunknown-attributes" +#endif +#include <jni.h> +#if defined __clang__ +#pragma clang diagnostic pop +#endif + +extern "C" { + typedef jint JNICALL JNI_InitArgs_Type(void *); + typedef jint JNICALL JNI_CreateVM_Type(JavaVM **, JNIEnv **, void *); + +} + +namespace stoc_javavm { + + class JVM { + ::std::vector<OUString> _props; + + public: + JVM() noexcept; + + void pushProp(const OUString & uString); + const ::std::vector< OUString> & getProperties() const { return _props;} + }; +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/loader/dllcomponentloader.cxx b/stoc/source/loader/dllcomponentloader.cxx new file mode 100644 index 000000000..43f20ad0a --- /dev/null +++ b/stoc/source/loader/dllcomponentloader.cxx @@ -0,0 +1,152 @@ +/* -*- 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 <osl/diagnose.h> +#include <rtl/ustring.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/shlib.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/bootstrap.hxx> + +#include <com/sun/star/loader/XImplementationLoader.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +namespace com::sun::star::registry { class XRegistryKey; } + +using namespace com::sun::star; +using namespace css::uno; +using namespace css::loader; +using namespace css::lang; +using namespace css::registry; +using namespace cppu; +using namespace osl; + +namespace { + +class DllComponentLoader + : public WeakImplHelper< XImplementationLoader, + XInitialization, + XServiceInfo > +{ +public: + explicit DllComponentLoader( const Reference<XComponentContext> & xCtx ); + + // 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 css::uno::Sequence< css::uno::Any >& aArguments ) override; + + // XImplementationLoader + virtual Reference<XInterface> SAL_CALL activate( const OUString& implementationName, const OUString& implementationLoaderUrl, const OUString& locationUrl, const Reference<XRegistryKey>& xKey ) override; + virtual sal_Bool SAL_CALL writeRegistryInfo( const Reference<XRegistryKey>& xKey, const OUString& implementationLoaderUrl, const OUString& locationUrl ) override; + +private: + Reference<XMultiServiceFactory> m_xSMgr; +}; + + +DllComponentLoader::DllComponentLoader( const Reference<XComponentContext> & xCtx ) +{ + m_xSMgr.set( xCtx->getServiceManager(), UNO_QUERY ); +} + +OUString SAL_CALL DllComponentLoader::getImplementationName( ) +{ + return "com.sun.star.comp.stoc.DLLComponentLoader"; +} + +sal_Bool SAL_CALL DllComponentLoader::supportsService( const OUString& ServiceName ) +{ + return cppu::supportsService(this, ServiceName); +} + +Sequence<OUString> SAL_CALL DllComponentLoader::getSupportedServiceNames( ) +{ + return { "com.sun.star.loader.SharedLibrary" }; +} + + +void DllComponentLoader::initialize( const css::uno::Sequence< css::uno::Any >& ) +{ + OSL_FAIL( "dllcomponentloader::initialize should not be called !" ); +// if( aArgs.getLength() != 1 ) +// { +// throw IllegalArgumentException(); +// } + +// Reference< XMultiServiceFactory > rServiceManager; + +// if( aArgs.getConstArray()[0].getValueType().getTypeClass() == TypeClass_INTERFACE ) +// { +// aArgs.getConstArray()[0] >>= rServiceManager; +// } + +// if( !rServiceManager.is() ) +// { +// throw IllegalArgumentException(); +// } + +// m_xSMgr = rServiceManager; +} + + +Reference<XInterface> SAL_CALL DllComponentLoader::activate( + const OUString & rImplName, const OUString &, const OUString & rLibName, + const Reference< XRegistryKey > & ) +{ + return loadSharedLibComponentFactory( + cppu::bootstrap_expandUri(rLibName), OUString(), rImplName, m_xSMgr, + css::uno::Reference<css::registry::XRegistryKey>()); +} + + +sal_Bool SAL_CALL DllComponentLoader::writeRegistryInfo( + const Reference< XRegistryKey > & xKey, const OUString &, const OUString & rLibName ) +{ +#ifdef DISABLE_DYNLOADING + (void) xKey; + (void) rLibName; + OSL_FAIL( "DllComponentLoader::writeRegistryInfo() should not be called I think?" ); + return sal_False; +#else + writeSharedLibComponentInfo( + cppu::bootstrap_expandUri(rLibName), OUString(), m_xSMgr, xKey ); + return true; +#endif +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_DLLComponentLoader_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new DllComponentLoader(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/namingservice/namingservice.component b/stoc/source/namingservice/namingservice.component new file mode 100644 index 000000000..3ba192e86 --- /dev/null +++ b/stoc/source/namingservice/namingservice.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.stoc.NamingService" + constructor="stoc_NamingService_Impl_get_implementation"> + <service name="com.sun.star.uno.NamingService"/> + </implementation> +</component> diff --git a/stoc/source/namingservice/namingservice.cxx b/stoc/source/namingservice/namingservice.cxx new file mode 100644 index 000000000..acabb7370 --- /dev/null +++ b/stoc/source/namingservice/namingservice.cxx @@ -0,0 +1,123 @@ +/* -*- 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 <cppuhelper/factory.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/uno/XNamingService.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> + +#include <mutex> +#include <unordered_map> + +using namespace cppu; +using namespace osl; + +using namespace css::uno; +using namespace css::lang; +using namespace css::registry; + + +namespace stoc_namingservice +{ + +typedef std::unordered_map< OUString, Reference<XInterface > > HashMap_OWString_Interface; + +namespace { + +class NamingService_Impl + : public WeakImplHelper < XServiceInfo, XNamingService > +{ + std::mutex aMutex; + HashMap_OWString_Interface aMap; +public: + NamingService_Impl(); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString& ServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uno::XInterface > SAL_CALL getRegisteredObject( const OUString& Name ) override; + virtual void SAL_CALL registerObject( const OUString& Name, const css::uno::Reference< css::uno::XInterface >& Object ) override; + virtual void SAL_CALL revokeObject( const OUString& Name ) override; +}; + +} + + + +NamingService_Impl::NamingService_Impl() {} + +// XServiceInfo +OUString NamingService_Impl::getImplementationName() +{ + return "com.sun.star.comp.stoc.NamingService"; +} + +// XServiceInfo +sal_Bool NamingService_Impl::supportsService( const OUString & rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +// XServiceInfo +Sequence< OUString > NamingService_Impl::getSupportedServiceNames() +{ + return { "com.sun.star.uno.NamingService" }; +} + +// XServiceInfo +Reference< XInterface > NamingService_Impl::getRegisteredObject( const OUString& Name ) +{ + std::scoped_lock aGuard( aMutex ); + Reference< XInterface > xRet; + HashMap_OWString_Interface::iterator aIt = aMap.find( Name ); + if( aIt != aMap.end() ) + xRet = (*aIt).second; + return xRet; +} + +// XServiceInfo +void NamingService_Impl::registerObject( const OUString& Name, const Reference< XInterface >& Object ) +{ + std::scoped_lock aGuard( aMutex ); + aMap[ Name ] = Object; +} + +// XServiceInfo +void NamingService_Impl::revokeObject( const OUString& Name ) +{ + std::scoped_lock aGuard( aMutex ); + aMap.erase( Name ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_NamingService_Impl_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new stoc_namingservice::NamingService_Impl()); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/proxy_factory/proxyfac.component b/stoc/source/proxy_factory/proxyfac.component new file mode 100644 index 000000000..b069f88df --- /dev/null +++ b/stoc/source/proxy_factory/proxyfac.component @@ -0,0 +1,26 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + * 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 . + --> + +<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@" + xmlns="http://openoffice.org/2010/uno-components"> + <implementation name="com.sun.star.comp.reflection.ProxyFactory" + constructor="stoc_FactoryImpl_get_implementation" single-instance="true"> + <service name="com.sun.star.reflection.ProxyFactory"/> + </implementation> +</component> diff --git a/stoc/source/proxy_factory/proxyfac.cxx b/stoc/source/proxy_factory/proxyfac.cxx new file mode 100644 index 000000000..107d37160 --- /dev/null +++ b/stoc/source/proxy_factory/proxyfac.cxx @@ -0,0 +1,410 @@ +/* -*- 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 <osl/diagnose.h> +#include <osl/interlck.h> +#include <rtl/ref.hxx> +#include <uno/dispatcher.hxx> +#include <uno/data.h> +#include <uno/lbnames.h> +#include <uno/mapping.hxx> +#include <uno/environment.hxx> +#include <typelib/typedescription.hxx> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <cppuhelper/weakagg.hxx> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/reflection/XProxyFactory.hpp> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + + +using namespace ::com::sun::star; +using namespace css::uno; + + +namespace +{ + +struct FactoryImpl : public ::cppu::WeakImplHelper< lang::XServiceInfo, + reflection::XProxyFactory > +{ + Environment m_uno_env; + Environment m_cpp_env; + Mapping m_uno2cpp; + Mapping m_cpp2uno; + + UnoInterfaceReference binuno_queryInterface( + UnoInterfaceReference const & unoI, + typelib_InterfaceTypeDescription * pTypeDescr ); + + FactoryImpl(); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( const OUString & rServiceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XProxyFactory + virtual Reference< XAggregation > SAL_CALL createProxy( + Reference< XInterface > const & xTarget ) override; +}; + + +UnoInterfaceReference FactoryImpl::binuno_queryInterface( + UnoInterfaceReference const & unoI, + typelib_InterfaceTypeDescription * pTypeDescr ) +{ + // init queryInterface() td + static typelib_TypeDescription* s_pQITD = []() { + typelib_TypeDescription* pTXInterfaceDescr = nullptr; + TYPELIB_DANGER_GET(&pTXInterfaceDescr, cppu::UnoType<XInterface>::get().getTypeLibType()); + typelib_TypeDescription* pQITD = nullptr; + typelib_typedescriptionreference_getDescription( + &pQITD, reinterpret_cast<typelib_InterfaceTypeDescription*>(pTXInterfaceDescr) + ->ppAllMembers[0]); + TYPELIB_DANGER_RELEASE(pTXInterfaceDescr); + return pQITD; + }(); + + void * args[ 1 ]; + args[ 0 ] = &reinterpret_cast< typelib_TypeDescription * >( + pTypeDescr )->pWeakRef; + uno_Any ret_val, exc_space; + uno_Any * exc = &exc_space; + + unoI.dispatch( s_pQITD, &ret_val, args, &exc ); + + if (exc == nullptr) + { + UnoInterfaceReference ret; + if (ret_val.pType->eTypeClass == typelib_TypeClass_INTERFACE) + { + ret.set( *static_cast< uno_Interface ** >(ret_val.pData), + SAL_NO_ACQUIRE ); + typelib_typedescriptionreference_release( ret_val.pType ); + } + else + { + uno_any_destruct( &ret_val, nullptr ); + } + return ret; + } + else + { + // exception occurred: + OSL_ENSURE( + typelib_typedescriptionreference_isAssignableFrom( cppu::UnoType<RuntimeException>::get().getTypeLibType(), + exc->pType ), + "### RuntimeException expected!" ); + Any cpp_exc; + uno_type_copyAndConvertData( + &cpp_exc, exc, cppu::UnoType<decltype(cpp_exc)>::get().getTypeLibType(), + m_uno2cpp.get() ); + uno_any_destruct( exc, nullptr ); + ::cppu::throwException( cpp_exc ); + OSL_ASSERT( false ); // way of no return + return UnoInterfaceReference(); // for dummy + } +} + + +struct ProxyRoot : public ::cppu::OWeakAggObject +{ + // XAggregation + virtual Any SAL_CALL queryAggregation( Type const & rType ) override; + + ProxyRoot( ::rtl::Reference< FactoryImpl > const & factory, + Reference< XInterface > const & xTarget ); + + ::rtl::Reference< FactoryImpl > m_factory; + +private: + UnoInterfaceReference m_target; +}; + + +struct binuno_Proxy : public uno_Interface +{ + oslInterlockedCount m_nRefCount; + ::rtl::Reference< ProxyRoot > m_root; + UnoInterfaceReference m_target; + OUString m_oid; + TypeDescription m_typeDescr; + + binuno_Proxy( + ::rtl::Reference< ProxyRoot > const & root, + UnoInterfaceReference const & target, + OUString const & oid, TypeDescription const & typeDescr ); +}; + +extern "C" +{ + + +static void binuno_proxy_free( + uno_ExtEnvironment * pEnv, void * pProxy ) +{ + binuno_Proxy * proxy = static_cast< binuno_Proxy * >( + static_cast< uno_Interface * >( pProxy ) ); + OSL_ASSERT( proxy->m_root->m_factory->m_uno_env.get()->pExtEnv == pEnv ); + delete proxy; +} + + +static void binuno_proxy_acquire( uno_Interface * pUnoI ) +{ + binuno_Proxy * that = static_cast< binuno_Proxy * >( pUnoI ); + if (osl_atomic_increment( &that->m_nRefCount ) != 1) + return; + + // rebirth of zombie + uno_ExtEnvironment * uno_env = + that->m_root->m_factory->m_uno_env.get()->pExtEnv; + OSL_ASSERT( uno_env != nullptr ); + (*uno_env->registerProxyInterface)( + uno_env, reinterpret_cast< void ** >( &pUnoI ), binuno_proxy_free, + that->m_oid.pData, + reinterpret_cast< typelib_InterfaceTypeDescription * >( + that->m_typeDescr.get() ) ); + OSL_ASSERT( that == static_cast< binuno_Proxy * >( pUnoI ) ); +} + + +static void binuno_proxy_release( uno_Interface * pUnoI ) +{ + binuno_Proxy * that = static_cast< binuno_Proxy * >( pUnoI ); + if (osl_atomic_decrement( &that->m_nRefCount ) == 0) + { + uno_ExtEnvironment * uno_env = + that->m_root->m_factory->m_uno_env.get()->pExtEnv; + OSL_ASSERT( uno_env != nullptr ); + (*uno_env->revokeInterface)( uno_env, pUnoI ); + } +} + + +static void binuno_proxy_dispatch( + uno_Interface * pUnoI, const typelib_TypeDescription * pMemberType, + void * pReturn, void * pArgs [], uno_Any ** ppException ) +{ + binuno_Proxy * that = static_cast< binuno_Proxy * >( pUnoI ); + switch (reinterpret_cast< typelib_InterfaceMemberTypeDescription const * >( + pMemberType )->nPosition) + { + case 0: // queryInterface() + { + try + { + Type const & rType = + *static_cast< Type const * >( pArgs[ 0 ] ); + Any ret( that->m_root->queryInterface( rType ) ); + uno_type_copyAndConvertData( + pReturn, &ret, cppu::UnoType<decltype(ret)>::get().getTypeLibType(), + that->m_root->m_factory->m_cpp2uno.get() ); + *ppException = nullptr; // no exc + } + catch (RuntimeException &) + { + Any exc( ::cppu::getCaughtException() ); + uno_type_any_constructAndConvert( + *ppException, const_cast< void * >(exc.getValue()), + exc.getValueTypeRef(), + that->m_root->m_factory->m_cpp2uno.get() ); + } + break; + } + case 1: // acquire() + binuno_proxy_acquire( pUnoI ); + *ppException = nullptr; // no exc + break; + case 2: // release() + binuno_proxy_release( pUnoI ); + *ppException = nullptr; // no exc + break; + default: + that->m_target.dispatch( pMemberType, pReturn, pArgs, ppException ); + break; + } +} + +} + + +binuno_Proxy::binuno_Proxy( + ::rtl::Reference< ProxyRoot > const & root, + UnoInterfaceReference const & target, + OUString const & oid, TypeDescription const & typeDescr ) + : m_nRefCount( 1 ), + m_root( root ), + m_target( target ), + m_oid( oid ), + m_typeDescr( typeDescr ) +{ + uno_Interface::acquire = binuno_proxy_acquire; + uno_Interface::release = binuno_proxy_release; + uno_Interface::pDispatcher = binuno_proxy_dispatch; +} + +ProxyRoot::ProxyRoot( + ::rtl::Reference< FactoryImpl > const & factory, + Reference< XInterface > const & xTarget ) + : m_factory( factory ) +{ + m_factory->m_cpp2uno.mapInterface( + reinterpret_cast< void ** >( &m_target.m_pUnoI ), xTarget.get(), + cppu::UnoType<decltype(xTarget)>::get() ); + OSL_ENSURE( m_target.is(), "### mapping interface failed!" ); +} + + +Any ProxyRoot::queryAggregation( Type const & rType ) +{ + Any ret( OWeakAggObject::queryAggregation( rType ) ); + if (! ret.hasValue()) + { + typelib_TypeDescription * pTypeDescr = nullptr; + TYPELIB_DANGER_GET( &pTypeDescr, rType.getTypeLibType() ); + try + { + Reference< XInterface > xProxy; + uno_ExtEnvironment * cpp_env = m_factory->m_cpp_env.get()->pExtEnv; + OSL_ASSERT( cpp_env != nullptr ); + + // mind a new delegator, calculate current root: + Reference< XInterface > xRoot( + static_cast< OWeakObject * >(this), UNO_QUERY_THROW ); + OUString oid; + (*cpp_env->getObjectIdentifier)( cpp_env, &oid.pData, xRoot.get() ); + OSL_ASSERT( !oid.isEmpty() ); + + (*cpp_env->getRegisteredInterface)( + cpp_env, reinterpret_cast< void ** >( &xProxy ), + oid.pData, reinterpret_cast< + typelib_InterfaceTypeDescription * >(pTypeDescr) ); + if (! xProxy.is()) + { + // perform query on target: + UnoInterfaceReference proxy_target( + m_factory->binuno_queryInterface( + m_target, reinterpret_cast< + typelib_InterfaceTypeDescription * >(pTypeDescr) ) ); + if (proxy_target.is()) + { + // ensure root's object entries: + UnoInterfaceReference root; + m_factory->m_cpp2uno.mapInterface( + reinterpret_cast< void ** >( &root.m_pUnoI ), + xRoot.get(), cppu::UnoType<decltype(xRoot)>::get() ); + + UnoInterfaceReference proxy( + // ref count initially 1: + new binuno_Proxy( this, proxy_target, oid, pTypeDescr ), + SAL_NO_ACQUIRE ); + uno_ExtEnvironment * uno_env = + m_factory->m_uno_env.get()->pExtEnv; + OSL_ASSERT( uno_env != nullptr ); + (*uno_env->registerProxyInterface)( + uno_env, reinterpret_cast< void ** >( &proxy.m_pUnoI ), + binuno_proxy_free, oid.pData, + reinterpret_cast< typelib_InterfaceTypeDescription * >( + pTypeDescr ) ); + + m_factory->m_uno2cpp.mapInterface( + reinterpret_cast< void ** >( &xProxy ), + proxy.get(), pTypeDescr ); + } + } + if (xProxy.is()) + ret.setValue( &xProxy, pTypeDescr ); + } + catch (...) // finally + { + TYPELIB_DANGER_RELEASE( pTypeDescr ); + throw; + } + TYPELIB_DANGER_RELEASE( pTypeDescr ); + } + return ret; +} + + +FactoryImpl::FactoryImpl() +{ + OUString uno = UNO_LB_UNO; + OUString cpp = CPPU_CURRENT_LANGUAGE_BINDING_NAME; + + uno_getEnvironment( + reinterpret_cast< uno_Environment ** >( &m_uno_env ), uno.pData, nullptr ); + OSL_ENSURE( m_uno_env.is(), "### cannot get binary uno env!" ); + + uno_getEnvironment( + reinterpret_cast< uno_Environment ** >( &m_cpp_env ), cpp.pData, nullptr ); + OSL_ENSURE( m_cpp_env.is(), "### cannot get C++ uno env!" ); + + uno_getMapping( + reinterpret_cast< uno_Mapping ** >( &m_uno2cpp ), + m_uno_env.get(), m_cpp_env.get(), nullptr ); + OSL_ENSURE( m_uno2cpp.is(), "### cannot get bridge uno <-> C++!" ); + + uno_getMapping( + reinterpret_cast< uno_Mapping ** >( &m_cpp2uno ), + m_cpp_env.get(), m_uno_env.get(), nullptr ); + OSL_ENSURE( m_cpp2uno.is(), "### cannot get bridge C++ <-> uno!" ); +} + +// XProxyFactory + +Reference< XAggregation > FactoryImpl::createProxy( + Reference< XInterface > const & xTarget ) +{ + return new ProxyRoot( this, xTarget ); +} + +// XServiceInfo + +OUString FactoryImpl::getImplementationName() +{ + return "com.sun.star.comp.reflection.ProxyFactory"; +} + +sal_Bool FactoryImpl::supportsService( const OUString & rServiceName ) +{ + return cppu::supportsService(this, rServiceName); +} + +Sequence< OUString > FactoryImpl::getSupportedServiceNames() +{ + return { "com.sun.star.reflection.ProxyFactory" }; +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +stoc_FactoryImpl_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&) +{ + return cppu::acquire(new FactoryImpl); +} + + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/access_controller.cxx b/stoc/source/security/access_controller.cxx new file mode 100644 index 000000000..c96f1f61f --- /dev/null +++ b/stoc/source/security/access_controller.cxx @@ -0,0 +1,863 @@ +/* -*- 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 <vector> + +#include <osl/diagnose.h> +#include <osl/mutex.hxx> +#include <osl/thread.hxx> + +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> + +#include <uno/current_context.h> +#include <uno/lbnames.h> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/uno/XCurrentContext.hpp> +#include <com/sun/star/uno/DeploymentException.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/security/XAccessController.hpp> +#include <com/sun/star/security/XPolicy.hpp> + +#include "lru_cache.h" +#include "permissions.h" + +#include <memory> + +constexpr OUStringLiteral SERVICE_NAME = u"com.sun.star.security.AccessController"; +constexpr OUStringLiteral USER_CREDS = u"access-control.user-credentials.id"; + + +using namespace ::std; +using namespace ::osl; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace css::uno; +using namespace stoc_sec; + +namespace { + +// static stuff initialized when loading lib +OUString s_envType = CPPU_CURRENT_LANGUAGE_BINDING_NAME; +constexpr OUStringLiteral s_acRestriction = u"access-control.restriction"; + + +/** ac context intersects permissions of two ac contexts +*/ +class acc_Intersection + : public WeakImplHelper< security::XAccessControlContext > +{ + Reference< security::XAccessControlContext > m_x1, m_x2; + + acc_Intersection( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ); + +public: + static Reference< security::XAccessControlContext > create( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ); + + // XAccessControlContext impl + virtual void SAL_CALL checkPermission( + Any const & perm ) override; +}; + +acc_Intersection::acc_Intersection( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ) + : m_x1( x1 ) + , m_x2( x2 ) +{} + +Reference< security::XAccessControlContext > acc_Intersection::create( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ) +{ + if (! x1.is()) + return x2; + if (! x2.is()) + return x1; + return new acc_Intersection( x1, x2 ); +} + +void acc_Intersection::checkPermission( + Any const & perm ) +{ + m_x1->checkPermission( perm ); + m_x2->checkPermission( perm ); +} + +/** ac context unifies permissions of two ac contexts +*/ +class acc_Union + : public WeakImplHelper< security::XAccessControlContext > +{ + Reference< security::XAccessControlContext > m_x1, m_x2; + + acc_Union( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ); + +public: + static Reference< security::XAccessControlContext > create( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ); + + // XAccessControlContext impl + virtual void SAL_CALL checkPermission( + Any const & perm ) override; +}; + +acc_Union::acc_Union( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ) + : m_x1( x1 ) + , m_x2( x2 ) +{} + +Reference< security::XAccessControlContext > acc_Union::create( + Reference< security::XAccessControlContext > const & x1, + Reference< security::XAccessControlContext > const & x2 ) +{ + if (! x1.is()) + return Reference< security::XAccessControlContext >(); // unrestricted + if (! x2.is()) + return Reference< security::XAccessControlContext >(); // unrestricted + return new acc_Union( x1, x2 ); +} + +void acc_Union::checkPermission( + Any const & perm ) +{ + try + { + m_x1->checkPermission( perm ); + } + catch (security::AccessControlException &) + { + m_x2->checkPermission( perm ); + } +} + +/** ac context doing permission checks on static permissions +*/ +class acc_Policy + : public WeakImplHelper< security::XAccessControlContext > +{ + PermissionCollection m_permissions; + +public: + explicit acc_Policy( + PermissionCollection const & permissions ) + : m_permissions( permissions ) + {} + + // XAccessControlContext impl + virtual void SAL_CALL checkPermission( + Any const & perm ) override; +}; + +void acc_Policy::checkPermission( + Any const & perm ) +{ + m_permissions.checkPermission( perm ); +} + +/** current context overriding dynamic ac restriction +*/ +class acc_CurrentContext + : public WeakImplHelper< XCurrentContext > +{ + Reference< XCurrentContext > m_xDelegate; + Any m_restriction; + +public: + acc_CurrentContext( + Reference< XCurrentContext > const & xDelegate, + Reference< security::XAccessControlContext > const & xRestriction ); + + // XCurrentContext impl + virtual Any SAL_CALL getValueByName( OUString const & name ) override; +}; + +acc_CurrentContext::acc_CurrentContext( + Reference< XCurrentContext > const & xDelegate, + Reference< security::XAccessControlContext > const & xRestriction ) + : m_xDelegate( xDelegate ) +{ + if (xRestriction.is()) + { + m_restriction <<= xRestriction; + } + // return empty any otherwise on getValueByName(), not null interface +} + +Any acc_CurrentContext::getValueByName( OUString const & name ) +{ + if (name == s_acRestriction) + { + return m_restriction; + } + else if (m_xDelegate.is()) + { + return m_xDelegate->getValueByName( name ); + } + else + { + return Any(); + } +} + + +Reference< security::XAccessControlContext > getDynamicRestriction( + Reference< XCurrentContext > const & xContext ) +{ + if (xContext.is()) + { + Any acc(xContext->getValueByName(s_acRestriction)); + if (typelib_TypeClass_INTERFACE == acc.pType->eTypeClass) + { + // avoid ref-counting + OUString const & typeName = + OUString::unacquired( &acc.pType->pTypeName ); + if ( typeName == "com.sun.star.security.XAccessControlContext" ) + { + return Reference< security::XAccessControlContext >( + *static_cast< security::XAccessControlContext ** >( acc.pData ) ); + } + else // try to query + { + return Reference< security::XAccessControlContext >::query( + *static_cast< XInterface ** >( acc.pData ) ); + } + } + } + return Reference< security::XAccessControlContext >(); +} + +class cc_reset +{ + void * m_cc; +public: + explicit cc_reset( void * cc ) + : m_cc( cc ) {} + ~cc_reset() + { ::uno_setCurrentContext( m_cc, s_envType.pData, nullptr ); } +}; + +typedef WeakComponentImplHelper< + security::XAccessController, lang::XServiceInfo, lang::XInitialization > t_helper; + + +class AccessController + : public cppu::BaseMutex + , public t_helper +{ + Reference< XComponentContext > m_xComponentContext; + + Reference< security::XPolicy > m_xPolicy; + Reference< security::XPolicy > const & getPolicy(); + + // mode + enum class Mode { Off, On, DynamicOnly, SingleUser, SingleDefaultUser }; + Mode m_mode; + + PermissionCollection m_defaultPermissions; + // for single-user mode + PermissionCollection m_singleUserPermissions; + OUString m_singleUserId; + bool m_defaultPerm_init; + bool m_singleUser_init; + // for multi-user mode + lru_cache< OUString, PermissionCollection, OUStringHash, equal_to< OUString > > + m_user2permissions; + + ThreadData m_rec; + typedef vector< pair< OUString, Any > > t_rec_vec; + void clearPostPoned(); + void checkAndClearPostPoned(); + + PermissionCollection getEffectivePermissions( + Reference< XCurrentContext > const & xContext, + Any const & demanded_perm ); + +protected: + virtual void SAL_CALL disposing() override; + +public: + explicit AccessController( Reference< XComponentContext > const & xComponentContext ); + + // XInitialization impl + virtual void SAL_CALL initialize( + Sequence< Any > const & arguments ) override; + + // XAccessController impl + virtual void SAL_CALL checkPermission( + Any const & perm ) override; + virtual Any SAL_CALL doRestricted( + Reference< security::XAction > const & xAction, + Reference< security::XAccessControlContext > const & xRestriction ) override; + virtual Any SAL_CALL doPrivileged( + Reference< security::XAction > const & xAction, + Reference< security::XAccessControlContext > const & xRestriction ) override; + virtual Reference< security::XAccessControlContext > SAL_CALL getContext() override; + + // XServiceInfo impl + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +AccessController::AccessController( Reference< XComponentContext > const & xComponentContext ) + : t_helper( m_aMutex ) + , m_xComponentContext( xComponentContext ) + , m_mode( Mode::On ) // default + , m_defaultPerm_init( false ) + , m_singleUser_init( false ) + , m_rec( nullptr ) +{ + // The .../mode value had originally been set in + // cppu::add_access_control_entries (cppuhelper/source/servicefactory.cxx) + // to something other than "off" depending on various UNO_AC* bootstrap + // variables that are no longer supported, so this is mostly dead code now: + OUString mode; + if (m_xComponentContext->getValueByName( "/services/" + SERVICE_NAME + "/mode" ) >>= mode) + { + if ( mode == "off" ) + { + m_mode = Mode::Off; + } + else if ( mode == "on" ) + { + m_mode = Mode::On; + } + else if ( mode == "dynamic-only" ) + { + m_mode = Mode::DynamicOnly; + } + else if ( mode == "single-user" ) + { + m_xComponentContext->getValueByName( + "/services/" + SERVICE_NAME + "/single-user-id" ) >>= m_singleUserId; + if (m_singleUserId.isEmpty()) + { + throw RuntimeException( + "expected a user id in component context entry " + "\"/services/" + SERVICE_NAME + "/single-user-id\"!", + static_cast<OWeakObject *>(this) ); + } + m_mode = Mode::SingleUser; + } + else if ( mode == "single-default-user" ) + { + m_mode = Mode::SingleDefaultUser; + } + } + + // switch on caching for Mode::DynamicOnly and Mode::On (shareable multi-user process) + if (Mode::On != m_mode && Mode::DynamicOnly != m_mode) + return; + + sal_Int32 cacheSize = 0; // multi-user cache size + if (! (m_xComponentContext->getValueByName( + "/services/" + SERVICE_NAME + "/user-cache-size" ) >>= cacheSize)) + { + cacheSize = 128; // reasonable default? + } +#ifdef __CACHE_DIAGNOSE + cacheSize = 2; +#endif + m_user2permissions.setSize( cacheSize ); +} + +void AccessController::disposing() +{ + m_mode = Mode::Off; // avoid checks from now on xxx todo review/ better Mode::DynamicOnly? + m_xPolicy.clear(); + m_xComponentContext.clear(); +} + +// XInitialization impl + +void AccessController::initialize( + Sequence< Any > const & arguments ) +{ + // xxx todo: review for forking + // portal forking hack: re-initialize for another user-id + if (Mode::SingleUser != m_mode) // only if in single-user mode + { + throw RuntimeException( + "invalid call: ac must be in \"single-user\" mode!", static_cast<OWeakObject *>(this) ); + } + OUString userId; + arguments[ 0 ] >>= userId; + if ( userId.isEmpty() ) + { + throw RuntimeException( + "expected a user-id as first argument!", static_cast<OWeakObject *>(this) ); + } + // assured that no sync is necessary: no check happens at this forking time + m_singleUserId = userId; + m_singleUser_init = false; +} + + +Reference< security::XPolicy > const & AccessController::getPolicy() +{ + // get policy singleton + if (! m_xPolicy.is()) + { + Reference< security::XPolicy > xPolicy; + m_xComponentContext->getValueByName( + "/singletons/com.sun.star.security.thePolicy" ) >>= xPolicy; + if (!xPolicy.is()) + { + throw SecurityException( + "cannot get policy singleton!", static_cast<OWeakObject *>(this) ); + } + + MutexGuard guard( m_aMutex ); + if (! m_xPolicy.is()) + { + m_xPolicy = xPolicy; + } + } + return m_xPolicy; +} + +#ifdef __DIAGNOSE +static void dumpPermissions( + PermissionCollection const & collection, OUString const & userId = OUString() ) +{ + OUStringBuffer buf( 48 ); + if (!userId.isEmpty()) + { + buf.append( "> dumping permissions of user \"" ); + buf.append( userId ); + buf.append( "\":" ); + } + else + { + buf.append( "> dumping default permissions:" ); + } + SAL_INFO("stoc", buf.makeStringAndClear() ); + Sequence< OUString > permissions( collection.toStrings() ); + OUString const * p = permissions.getConstArray(); + for ( sal_Int32 nPos = 0; nPos < permissions.getLength(); ++nPos ) + { + SAL_INFO("stoc", p[ nPos ] ); + } + SAL_INFO("stoc", "> permission dump done" ); +} +#endif + + +void AccessController::clearPostPoned() +{ + delete static_cast< t_rec_vec * >( m_rec.getData() ); + m_rec.setData( nullptr ); +} + +void AccessController::checkAndClearPostPoned() +{ + // check postponed permissions + std::unique_ptr< t_rec_vec > rec( static_cast< t_rec_vec * >( m_rec.getData() ) ); + m_rec.setData( nullptr ); // takeover ownership + OSL_ASSERT(rec); + if (!rec) + return; + + t_rec_vec const& vec = *rec; + switch (m_mode) + { + case Mode::SingleUser: + { + OSL_ASSERT( m_singleUser_init ); + for (const auto & p : vec) + { + OSL_ASSERT( m_singleUserId == p.first ); + m_singleUserPermissions.checkPermission( p.second ); + } + break; + } + case Mode::SingleDefaultUser: + { + OSL_ASSERT( m_defaultPerm_init ); + for (const auto & p : vec) + { + OSL_ASSERT( p.first.isEmpty() ); // default-user + m_defaultPermissions.checkPermission( p.second ); + } + break; + } + case Mode::On: + { + for (const auto & p : vec) + { + PermissionCollection const * pPermissions; + // lookup policy for user + { + MutexGuard guard( m_aMutex ); + pPermissions = m_user2permissions.lookup( p.first ); + } + OSL_ASSERT( pPermissions ); + if (pPermissions) + { + pPermissions->checkPermission( p.second ); + } + } + break; + } + default: + OSL_FAIL( "### this should never be called in this ac mode!" ); + break; + } +} + +/** this is the only function calling the policy singleton and thus has to take care + of recurring calls! + + @param demanded_perm (if not empty) is the demanded permission of a checkPermission() call + which will be postponed for recurring calls +*/ +PermissionCollection AccessController::getEffectivePermissions( + Reference< XCurrentContext > const & xContext, + Any const & demanded_perm ) +{ + OUString userId; + + switch (m_mode) + { + case Mode::SingleUser: + { + if (m_singleUser_init) + return m_singleUserPermissions; + userId = m_singleUserId; + break; + } + case Mode::SingleDefaultUser: + { + if (m_defaultPerm_init) + return m_defaultPermissions; + break; + } + case Mode::On: + { + if (xContext.is()) + { + xContext->getValueByName( USER_CREDS ) >>= userId; + } + if ( userId.isEmpty() ) + { + throw SecurityException( + "cannot determine current user in multi-user ac!", static_cast<OWeakObject *>(this) ); + } + + // lookup policy for user + MutexGuard guard( m_aMutex ); + PermissionCollection const * pPermissions = m_user2permissions.lookup( userId ); + if (pPermissions) + return *pPermissions; + break; + } + default: + OSL_FAIL( "### this should never be called in this ac mode!" ); + return PermissionCollection(); + } + + // call on policy + // iff this is a recurring call for the default user, then grant all permissions + t_rec_vec * rec = static_cast< t_rec_vec * >( m_rec.getData() ); + if (rec) // tls entry exists => this is recursive call + { + if (demanded_perm.hasValue()) + { + // enqueue + rec->push_back( pair< OUString, Any >( userId, demanded_perm ) ); + } +#ifdef __DIAGNOSE + SAL_INFO("stoc", "> info: recurring call of user: " << userId ); +#endif + return PermissionCollection( new AllPermission() ); + } + else // no tls + { + rec = new t_rec_vec; + m_rec.setData( rec ); + } + + try // calls on API + { + // init default permissions + if (! m_defaultPerm_init) + { + PermissionCollection defaultPermissions( + getPolicy()->getDefaultPermissions() ); + // assign + MutexGuard guard( m_aMutex ); + if (! m_defaultPerm_init) + { + m_defaultPermissions = defaultPermissions; + m_defaultPerm_init = true; + } +#ifdef __DIAGNOSE + dumpPermissions( m_defaultPermissions ); +#endif + } + + PermissionCollection ret; + + // init user permissions + switch (m_mode) + { + case Mode::SingleUser: + { + ret = PermissionCollection( + getPolicy()->getPermissions( userId ), m_defaultPermissions ); + { + // assign + MutexGuard guard( m_aMutex ); + if (m_singleUser_init) + { + ret = m_singleUserPermissions; + } + else + { + m_singleUserPermissions = ret; + m_singleUser_init = true; + } + } +#ifdef __DIAGNOSE + dumpPermissions( ret, userId ); +#endif + break; + } + case Mode::SingleDefaultUser: + { + ret = m_defaultPermissions; + break; + } + case Mode::On: + { + ret = PermissionCollection( + getPolicy()->getPermissions( userId ), m_defaultPermissions ); + { + // cache + MutexGuard guard( m_aMutex ); + m_user2permissions.set( userId, ret ); + } +#ifdef __DIAGNOSE + dumpPermissions( ret, userId ); +#endif + break; + } + default: + break; + } + + // check postponed + checkAndClearPostPoned(); + return ret; + } + catch (const security::AccessControlException & exc) // wrapped into DeploymentException + { + clearPostPoned(); // safety: exception could have happened before checking postponed? + throw DeploymentException( "deployment error (AccessControlException occurred): " + exc.Message, exc.Context ); + } + catch (RuntimeException &) + { + // don't check postponed, just cleanup + clearPostPoned(); + delete static_cast< t_rec_vec * >( m_rec.getData() ); + m_rec.setData( nullptr ); + throw; + } + catch (Exception &) + { + // check postponed permissions first + // => AccessControlExceptions are errors, user exceptions not! + checkAndClearPostPoned(); + throw; + } + catch (...) + { + // don't check postponed, just cleanup + clearPostPoned(); + throw; + } +} + +// XAccessController impl + +void AccessController::checkPermission( + Any const & perm ) +{ + if (rBHelper.bDisposed) + { + throw lang::DisposedException( + "checkPermission() call on disposed AccessController!", static_cast<OWeakObject *>(this) ); + } + + if (Mode::Off == m_mode) + return; + + // first dynamic check of ac contexts + Reference< XCurrentContext > xContext; + ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext), s_envType.pData, nullptr ); + Reference< security::XAccessControlContext > xACC( getDynamicRestriction( xContext ) ); + if (xACC.is()) + { + xACC->checkPermission( perm ); + } + + if (Mode::DynamicOnly == m_mode) + return; + + // then static check + getEffectivePermissions( xContext, perm ).checkPermission( perm ); +} + +Any AccessController::doRestricted( + Reference< security::XAction > const & xAction, + Reference< security::XAccessControlContext > const & xRestriction ) +{ + if (rBHelper.bDisposed) + { + throw lang::DisposedException( + "doRestricted() call on disposed AccessController!", static_cast<OWeakObject *>(this) ); + } + + if (Mode::Off == m_mode) // optimize this way, because no dynamic check will be performed + return xAction->run(); + + if (xRestriction.is()) + { + Reference< XCurrentContext > xContext; + ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext), s_envType.pData, nullptr ); + + // override restriction + Reference< XCurrentContext > xNewContext( + new acc_CurrentContext( xContext, acc_Intersection::create( + xRestriction, getDynamicRestriction( xContext ) ) ) ); + ::uno_setCurrentContext( xNewContext.get(), s_envType.pData, nullptr ); + cc_reset reset( xContext.get() ); + return xAction->run(); + } + else + { + return xAction->run(); + } +} + +Any AccessController::doPrivileged( + Reference< security::XAction > const & xAction, + Reference< security::XAccessControlContext > const & xRestriction ) +{ + if (rBHelper.bDisposed) + { + throw lang::DisposedException( + "doPrivileged() call on disposed AccessController!", static_cast<OWeakObject *>(this) ); + } + + if (Mode::Off == m_mode) // no dynamic check will be performed + { + return xAction->run(); + } + + Reference< XCurrentContext > xContext; + ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext), s_envType.pData, nullptr ); + + Reference< security::XAccessControlContext > xOldRestr( + getDynamicRestriction( xContext ) ); + + if (xOldRestr.is()) // previous restriction + { + // override restriction + Reference< XCurrentContext > xNewContext( + new acc_CurrentContext( xContext, acc_Union::create( xRestriction, xOldRestr ) ) ); + ::uno_setCurrentContext( xNewContext.get(), s_envType.pData, nullptr ); + cc_reset reset( xContext.get() ); + return xAction->run(); + } + else // no previous restriction => never current restriction + { + return xAction->run(); + } +} + +Reference< security::XAccessControlContext > AccessController::getContext() +{ + if (rBHelper.bDisposed) + { + throw lang::DisposedException( + "getContext() call on disposed AccessController!", static_cast<OWeakObject *>(this) ); + } + + if (Mode::Off == m_mode) // optimize this way, because no dynamic check will be performed + { + return new acc_Policy( PermissionCollection( new AllPermission() ) ); + } + + Reference< XCurrentContext > xContext; + ::uno_getCurrentContext( reinterpret_cast<void **>(&xContext), s_envType.pData, nullptr ); + + return acc_Intersection::create( + getDynamicRestriction( xContext ), + new acc_Policy( getEffectivePermissions( xContext, Any() ) ) ); +} + +// XServiceInfo impl + +OUString AccessController::getImplementationName() +{ + return "com.sun.star.security.comp.stoc.AccessController"; +} + +sal_Bool AccessController::supportsService( OUString const & serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +Sequence< OUString > AccessController::getSupportedServiceNames() +{ + Sequence<OUString> aSNS { SERVICE_NAME }; + return aSNS; +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_security_comp_stoc_AccessController_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new AccessController(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/file_policy.cxx b/stoc/source/security/file_policy.cxx new file mode 100644 index 000000000..91f75092b --- /dev/null +++ b/stoc/source/security/file_policy.cxx @@ -0,0 +1,493 @@ +/* -*- 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 <osl/diagnose.h> +#include <osl/file.h> +#include <rtl/byteseq.hxx> +#include <rtl/ustrbuf.hxx> + +#include <cppuhelper/access_control.hxx> +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/security/XPolicy.hpp> +#include <com/sun/star/security/AllPermission.hpp> +#include <com/sun/star/security/RuntimePermission.hpp> +#include <com/sun/star/io/FilePermission.hpp> +#include <com/sun/star/connection/SocketPermission.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <string_view> +#include <unordered_map> + +constexpr OUStringLiteral IMPL_NAME = u"com.sun.star.security.comp.stoc.FilePolicy"; + +using namespace ::osl; +using namespace ::cppu; +using namespace ::com::sun::star; +using namespace css::uno; + +namespace { + +typedef WeakComponentImplHelper< security::XPolicy, lang::XServiceInfo > t_helper; + + +class FilePolicy + : public cppu::BaseMutex + , public t_helper +{ + Reference< XComponentContext > m_xComponentContext; + AccessControl m_ac; + + Sequence< Any > m_defaultPermissions; + typedef std::unordered_map< OUString, Sequence< Any > > t_permissions; + t_permissions m_userPermissions; + bool m_init; + +protected: + virtual void SAL_CALL disposing() override; + +public: + explicit FilePolicy( Reference< XComponentContext > const & xComponentContext ); + + // XPolicy impl + virtual Sequence< Any > SAL_CALL getPermissions( + OUString const & userId ) override; + virtual Sequence< Any > SAL_CALL getDefaultPermissions() override; + virtual void SAL_CALL refresh() override; + + // XServiceInfo impl + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService( OUString const & serviceName ) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; +}; + +FilePolicy::FilePolicy( Reference< XComponentContext > const & xComponentContext ) + : t_helper( m_aMutex ) + , m_xComponentContext( xComponentContext ) + , m_ac( xComponentContext ) + , m_init( false ) +{} + +void FilePolicy::disposing() +{ + m_userPermissions.clear(); + m_defaultPermissions = Sequence< Any >(); + m_xComponentContext.clear(); +} + + +Sequence< Any > FilePolicy::getPermissions( + OUString const & userId ) +{ + if (! m_init) + { + refresh(); + m_init = true; + } + + MutexGuard guard( m_aMutex ); + t_permissions::iterator iFind( m_userPermissions.find( userId ) ); + if (m_userPermissions.end() == iFind) + { + return Sequence< Any >(); + } + else + { + return iFind->second; + } +} + +Sequence< Any > FilePolicy::getDefaultPermissions() +{ + if (! m_init) + { + refresh(); + m_init = true; + } + + MutexGuard guard( m_aMutex ); + return m_defaultPermissions; +} + + +class PolicyReader +{ + OUString m_fileName; + oslFileHandle m_file; + + sal_Int32 m_linepos; + rtl::ByteSequence m_line; + sal_Int32 m_pos; + sal_Unicode m_back; + + sal_Unicode get(); + void back( sal_Unicode c ) + { m_back = c; } + + static bool isWhiteSpace( sal_Unicode c ) + { return (' ' == c || '\t' == c || '\n' == c || '\r' == c); } + void skipWhiteSpace(); + + static bool isCharToken( sal_Unicode c ) + { return (';' == c || ',' == c || '{' == c || '}' == c); } + +public: + PolicyReader( OUString const & file, AccessControl & ac ); + ~PolicyReader(); + + void error( std::u16string_view msg ); + + OUString getToken(); + OUString assureToken(); + OUString getQuotedToken(); + OUString assureQuotedToken(); + void assureToken( sal_Unicode token ); +}; + +void PolicyReader::assureToken( sal_Unicode token ) +{ + skipWhiteSpace(); + sal_Unicode c = get(); + if (c == token) + return; + OUString msg = "expected >" + OUStringChar(c) + "<!"; + error( msg ); +} + +OUString PolicyReader::assureQuotedToken() +{ + OUString token( getQuotedToken() ); + if (token.isEmpty()) + error( u"unexpected end of file!" ); + return token; +} + +OUString PolicyReader::getQuotedToken() +{ + skipWhiteSpace(); + OUStringBuffer buf( 32 ); + sal_Unicode c = get(); + if ('\"' != c) + error( u"expected quoting >\"< character!" ); + c = get(); + while ('\0' != c && '\"' != c) + { + buf.append( c ); + c = get(); + } + return buf.makeStringAndClear(); +} + +OUString PolicyReader::assureToken() +{ + OUString token( getToken() ); + if ( token.isEmpty()) + error( u"unexpected end of file!" ); + return token; +} + +OUString PolicyReader::getToken() +{ + skipWhiteSpace(); + sal_Unicode c = get(); + if (isCharToken( c )) + return OUString( &c, 1 ); + OUStringBuffer buf( 32 ); + while ('\0' != c && !isCharToken( c ) && !isWhiteSpace( c )) + { + buf.append( c ); + c = get(); + } + back( c ); + return buf.makeStringAndClear(); +} + +void PolicyReader::skipWhiteSpace() +{ + sal_Unicode c; + do + { + c = get(); + } + while (isWhiteSpace( c )); // seeking next non-whitespace char + + if ('/' == c) // C/C++ like comment + { + c = get(); + if ('/' == c) // C++ like comment + { + do + { + c = get(); + } + while ('\n' != c && '\0' != c); // seek eol/eof + skipWhiteSpace(); // cont skip on next line + } + else if ('*' == c) // C like comment + { + bool fini = true; + do + { + c = get(); + if ('*' == c) + { + c = get(); + fini = ('/' == c || '\0' == c); + } + else + { + fini = ('\0' == c); + } + } + while (! fini); + skipWhiteSpace(); // cont skip on next line + } + else + { + error( u"expected C/C++ like comment!" ); + } + } + else if ('#' == c) // script like comment + { + do + { + c = get(); + } + while ('\n' != c && '\0' != c); // seek eol/eof + skipWhiteSpace(); // cont skip on next line + } + + else // is token char + { + back( c ); + } +} + +sal_Unicode PolicyReader::get() +{ + if ('\0' != m_back) // one char push back possible + { + sal_Unicode c = m_back; + m_back = '\0'; + return c; + } + else if (m_pos == m_line.getLength()) // provide newline as whitespace + { + ++m_pos; + return '\n'; + } + else if (m_pos > m_line.getLength()) // read new line + { + sal_Bool eof; + oslFileError rc = ::osl_isEndOfFile( m_file, &eof ); + if (osl_File_E_None != rc) + error( u"checking eof failed!" ); + if (eof) + return '\0'; + + rc = ::osl_readLine( m_file, reinterpret_cast< sal_Sequence ** >( &m_line ) ); + if (osl_File_E_None != rc) + error( u"read line failed!" ); + ++m_linepos; + if (! m_line.getLength()) // empty line read + { + m_pos = 1; // read new line next time + return '\n'; + } + m_pos = 0; + } + return (m_line.getConstArray()[ m_pos++ ]); +} + +void PolicyReader::error( std::u16string_view msg ) +{ + throw RuntimeException( + "error processing file \"" + m_fileName + + "\" [line " + OUString::number(m_linepos) + + ", column " + OUString::number(m_pos) + + "] " + msg); +} + +PolicyReader::PolicyReader( OUString const & fileName, AccessControl & ac ) + : m_fileName( fileName ) + , m_linepos( 0 ) + , m_pos( 1 ) // force readline + , m_back( '\0' ) +{ + ac.checkFilePermission( m_fileName, "read" ); + if (osl_File_E_None != ::osl_openFile( m_fileName.pData, &m_file, osl_File_OpenFlag_Read )) + { + throw RuntimeException( "cannot open file \"" + m_fileName + "\"!" ); + } +} + +PolicyReader::~PolicyReader() +{ + if ( ::osl_closeFile( m_file ) != osl_File_E_None ) { + OSL_ASSERT( false ); + } +} + +constexpr OUStringLiteral s_grant = u"grant"; +constexpr OUStringLiteral s_user = u"user"; +constexpr OUStringLiteral s_permission = u"permission"; +constexpr OUStringLiteral s_openBrace = u"{"; +constexpr OUStringLiteral s_closingBrace = u"}"; + +constexpr OUStringLiteral s_filePermission = u"com.sun.star.io.FilePermission"; +constexpr OUStringLiteral s_socketPermission = u"com.sun.star.connection.SocketPermission"; +constexpr OUStringLiteral s_runtimePermission = u"com.sun.star.security.RuntimePermission"; +constexpr OUStringLiteral s_allPermission = u"com.sun.star.security.AllPermission"; + + +void FilePolicy::refresh() +{ + // read out file (the .../file-name value had originally been set in + // cppu::add_access_control_entries (cppuhelper/source/servicefactory.cxx) + // depending on various UNO_AC* bootstrap variables that are no longer + // supported, so this is effectively dead code): + OUString fileName; + m_xComponentContext->getValueByName( + "/implementations/" + IMPL_NAME + "/file-name" ) >>= fileName; + if ( fileName.isEmpty() ) + { + throw RuntimeException( + "name of policy file unknown!", + static_cast<OWeakObject *>(this) ); + } + + PolicyReader reader( fileName, m_ac ); + + // fill these two + Sequence< Any > defaultPermissions; + t_permissions userPermissions; + + OUString token( reader.getToken() ); + while (!token.isEmpty()) + { + if ( token != s_grant ) + reader.error( u"expected >grant< token!" ); + OUString userId; + token = reader.assureToken(); + if ( token == s_user ) // next token is user-id + { + userId = reader.assureQuotedToken(); + token = reader.assureToken(); + } + if ( token != s_openBrace ) + reader.error( u"expected opening brace >{<!" ); + token = reader.assureToken(); + // permissions list + while ( token != s_closingBrace ) + { + if ( token != s_permission ) + reader.error( u"expected >permission< or closing brace >}<!" ); + + token = reader.assureToken(); // permission type + Any perm; + if ( token == s_filePermission ) // FilePermission + { + OUString url( reader.assureQuotedToken() ); + reader.assureToken( ',' ); + OUString actions( reader.assureQuotedToken() ); + perm <<= io::FilePermission( url, actions ); + } + else if ( token == s_socketPermission ) // SocketPermission + { + OUString host( reader.assureQuotedToken() ); + reader.assureToken( ',' ); + OUString actions( reader.assureQuotedToken() ); + perm <<= connection::SocketPermission( host, actions ); + } + else if ( token == s_runtimePermission ) // RuntimePermission + { + OUString name( reader.assureQuotedToken() ); + perm <<= security::RuntimePermission( name ); + } + else if ( token == s_allPermission ) // AllPermission + { + perm <<= security::AllPermission(); + } + else + { + reader.error( u"expected permission type!" ); + } + + reader.assureToken( ';' ); + + // insert + if (!userId.isEmpty()) + { + Sequence< Any > perms( userPermissions[ userId ] ); + sal_Int32 len = perms.getLength(); + perms.realloc( len +1 ); + perms.getArray()[ len ] = perm; + userPermissions[ userId ] = perms; + } + else + { + sal_Int32 len = defaultPermissions.getLength(); + defaultPermissions.realloc( len +1 ); + defaultPermissions.getArray()[ len ] = perm; + } + + token = reader.assureToken(); // next permissions token + } + + reader.assureToken( ';' ); // semi + token = reader.getToken(); // next grant token + } + + // assign new ones + MutexGuard guard( m_aMutex ); + m_defaultPermissions = defaultPermissions; + m_userPermissions = userPermissions; +} + + +OUString FilePolicy::getImplementationName() +{ + return IMPL_NAME; +} + +sal_Bool FilePolicy::supportsService( OUString const & serviceName ) +{ + return cppu::supportsService(this, serviceName); +} + +Sequence< OUString > FilePolicy::getSupportedServiceNames() +{ + return { "com.sun.star.security.Policy" }; +} + +} // namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_security_comp_stoc_FilePolicy_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new FilePolicy(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/lru_cache.h b/stoc/source/security/lru_cache.h new file mode 100644 index 000000000..402b41d58 --- /dev/null +++ b/stoc/source/security/lru_cache.h @@ -0,0 +1,210 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_STOC_SOURCE_SECURITY_LRU_CACHE_H +#define INCLUDED_STOC_SOURCE_SECURITY_LRU_CACHE_H + +#include <memory> +#include <unordered_map> + +// __CACHE_DIAGNOSE works only for OUString keys +#ifdef __CACHE_DIAGNOSE +#include <osl/diagnose.h> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <rtl/string.hxx> +#include <sal/log.hxx> +#endif + + +namespace stoc_sec +{ + +/** Implementation of a least recently used (lru) cache. +*/ +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +class lru_cache +{ + struct Entry + { + t_key m_key; + t_val m_val; + Entry * m_pred; + Entry * m_succ; + }; + typedef std::unordered_map< t_key, Entry *, t_hashKey, t_equalKey > t_key2element; + t_key2element m_key2element; + ::std::size_t m_size; + + std::unique_ptr<Entry[]> m_block; + mutable Entry * m_head; + mutable Entry * m_tail; + inline void toFront( Entry * entry ) const; + +public: + /** Default Ctor. Does not cache. + */ + inline lru_cache(); + + /** Retrieves a pointer to value in cache. Returns 0, if none was found. + + @param key a key + @return pointer to value or 0 + */ + inline t_val const * lookup( t_key const & key ) const; + + /** Sets a value to be cached for given key. + + @param key a key + @param val a value + */ + inline void set( t_key const & key, t_val const & val ); + + /** Sets the number of elements to be cached. This will clear previous entries. + + @param cacheSize number of elements to be cached + */ + inline void setSize( ::std::size_t size ); +}; + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline void lru_cache< t_key, t_val, t_hashKey, t_equalKey >::setSize( + ::std::size_t size ) +{ + m_key2element.clear(); + m_block.reset(); + m_size = size; + + if (0 < m_size) + { + m_block.reset( new Entry[ m_size ] ); + m_head = m_block.get(); + m_tail = m_block.get() + m_size -1; + for ( ::std::size_t nPos = m_size; nPos--; ) + { + m_block[ nPos ].m_pred = m_block.get() + nPos -1; + m_block[ nPos ].m_succ = m_block.get() + nPos +1; + } + } +} + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline lru_cache< t_key, t_val, t_hashKey, t_equalKey >::lru_cache() + : m_size( 0 ) + , m_block( nullptr ) + , m_head( nullptr ) + , m_tail( nullptr ) +{ +} + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline void lru_cache< t_key, t_val, t_hashKey, t_equalKey >::toFront( + Entry * entry ) const +{ + if (entry != m_head) + { + // cut out element + if (entry == m_tail) + { + m_tail = entry->m_pred; + } + else + { + entry->m_succ->m_pred = entry->m_pred; + entry->m_pred->m_succ = entry->m_succ; + } + // push to front + m_head->m_pred = entry; + entry->m_succ = m_head; + m_head = entry; + } +} + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline t_val const * lru_cache< t_key, t_val, t_hashKey, t_equalKey >::lookup( + t_key const & key ) const +{ + if (0 < m_size) + { + typename t_key2element::const_iterator const iFind( m_key2element.find( key ) ); + if (iFind != m_key2element.end()) + { + Entry * entry = iFind->second; + toFront( entry ); +#ifdef __CACHE_DIAGNOSE + OUStringBuffer buf( 48 ); + buf.appendAscii( "> retrieved element \"" ); + buf.append( entry->m_key ); + buf.appendAscii( "\" from cache" ); + SAL_INFO("stoc", buf.makeStringAndClear() ); +#endif + return &entry->m_val; + } + } + return nullptr; +} + +template< typename t_key, typename t_val, typename t_hashKey, typename t_equalKey > +inline void lru_cache< t_key, t_val, t_hashKey, t_equalKey >::set( + t_key const & key, t_val const & val ) +{ + if (0 < m_size) + { + typename t_key2element::const_iterator const iFind( m_key2element.find( key ) ); + + Entry * entry; + if (iFind == m_key2element.end()) + { + entry = m_tail; // erase last element +#ifdef __CACHE_DIAGNOSE + if (entry->m_key.getLength()) + { + OUStringBuffer buf( 48 ); + buf.appendAscii( "> kicking element \"" ); + buf.append( entry->m_key ); + buf.appendAscii( "\" from cache" ); + SAL_INFO("stoc", buf.makeStringAndClear() ); + } +#endif + m_key2element.erase( entry->m_key ); + entry->m_key = key; + ::std::pair< typename t_key2element::iterator, bool > insertion( + m_key2element.emplace( key, entry ) ); + OSL_ENSURE( insertion.second, "### inserting new cache entry failed?!" ); + } + else + { + entry = iFind->second; +#ifdef __CACHE_DIAGNOSE + OUStringBuffer buf( 48 ); + buf.appendAscii( "> replacing element \"" ); + buf.append( entry->m_key ); + buf.appendAscii( "\" in cache" ); + SAL_INFO("stoc", buf.makeStringAndClear() ); +#endif + } + entry->m_val = val; + toFront( entry ); + } +} + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/permissions.cxx b/stoc/source/security/permissions.cxx new file mode 100644 index 000000000..1799f9c8c --- /dev/null +++ b/stoc/source/security/permissions.cxx @@ -0,0 +1,604 @@ +/* -*- 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 <vector> + +#include <osl/process.h> +#include <osl/socket.hxx> +#include <osl/mutex.hxx> + +#include <rtl/string.hxx> +#include <rtl/ustrbuf.hxx> +#include <sal/log.hxx> +#include <o3tl/string_view.hxx> + +#include <com/sun/star/security/RuntimePermission.hpp> +#include <com/sun/star/security/AllPermission.hpp> +#include <com/sun/star/io/FilePermission.hpp> +#include <com/sun/star/connection/SocketPermission.hpp> +#include <com/sun/star/security/AccessControlException.hpp> +#include <com/sun/star/uno/Sequence.hxx> + +#include "permissions.h" + +using namespace ::std; +using namespace ::osl; +using namespace ::com::sun::star; +using namespace css::uno; + +namespace stoc_sec +{ + + +static sal_Int32 makeMask( + OUString const & items, char const * const * strings ) +{ + sal_Int32 mask = 0; + + sal_Int32 n = 0; + do + { + OUString item( o3tl::trim(o3tl::getToken(items, 0, ',', n )) ); + if ( item.isEmpty()) + continue; + sal_Int32 nPos = 0; + while (strings[ nPos ]) + { + if (item.equalsAscii( strings[ nPos ] )) + { + mask |= (0x80000000 >> nPos); + break; + } + ++nPos; + } +#if OSL_DEBUG_LEVEL > 0 + if (! strings[ nPos ]) + { + SAL_WARN("stoc", "ignoring unknown socket action: " << item ); + } +#endif + } + while (n >= 0); // all items + return mask; +} + +static OUString makeStrings( + sal_Int32 mask, char const * const * strings ) +{ + OUStringBuffer buf( 48 ); + while (mask) + { + if (0x80000000 & mask) + { + buf.appendAscii( *strings ); + if ((mask << 1) != 0) // more items following + buf.append( ',' ); + } + mask = (mask << 1); + ++strings; + } + return buf.makeStringAndClear(); +} + +namespace { + +class SocketPermission : public Permission +{ + static char const * s_actions []; + sal_Int32 m_actions; + + OUString m_host; + sal_Int32 m_lowerPort; + sal_Int32 m_upperPort; + mutable OUString m_ip; + mutable bool m_resolveErr; + mutable bool m_resolvedHost; + bool m_wildCardHost; + + inline bool resolveHost() const; + +public: + SocketPermission( + connection::SocketPermission const & perm, + ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ); + virtual bool implies( Permission const & perm ) const override; + virtual OUString toString() const override; +}; + +} + +char const * SocketPermission::s_actions [] = { "accept", "connect", "listen", "resolve", nullptr }; + +SocketPermission::SocketPermission( + connection::SocketPermission const & perm, + ::rtl::Reference< Permission > const & next ) + : Permission( SOCKET, next ) + , m_actions( makeMask( perm.Actions, s_actions ) ) + , m_host( perm.Host ) + , m_lowerPort( 0 ) + , m_upperPort( 65535 ) + , m_resolveErr( false ) + , m_resolvedHost( false ) + , m_wildCardHost( !perm.Host.isEmpty() && '*' == perm.Host.pData->buffer[ 0 ] ) +{ + if (0xe0000000 & m_actions) // if any (except resolve) is given => resolve implied + m_actions |= 0x10000000; + + // separate host from portrange + sal_Int32 colon = m_host.indexOf( ':' ); + if (colon < 0) // port [range] not given + return; + + sal_Int32 minus = m_host.indexOf( '-', colon +1 ); + if (minus < 0) + { + m_lowerPort = m_upperPort = o3tl::toInt32(m_host.subView( colon +1 )); + } + else if (minus == (colon +1)) // -N + { + m_upperPort = o3tl::toInt32(m_host.subView( minus +1 )); + } + else if (minus == (m_host.getLength() -1)) // N- + { + m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, m_host.getLength() -1 -colon -1 )); + } + else // A-B + { + m_lowerPort = o3tl::toInt32(m_host.subView( colon +1, minus - colon -1 )); + m_upperPort = o3tl::toInt32(m_host.subView( minus +1 )); + } + m_host = m_host.copy( 0, colon ); +} + +inline bool SocketPermission::resolveHost() const +{ + if (m_resolveErr) + return false; + + if (! m_resolvedHost) + { + // dns lookup + SocketAddr addr; + SocketAddr::resolveHostname( m_host, addr ); + OUString ip; + m_resolveErr = (::osl_Socket_Ok != ::osl_getDottedInetAddrOfSocketAddr( + addr.getHandle(), &ip.pData )); + if (m_resolveErr) + return false; + + MutexGuard guard( Mutex::getGlobalMutex() ); + if (! m_resolvedHost) + { + m_ip = ip; + m_resolvedHost = true; + } + } + return m_resolvedHost; +} + +bool SocketPermission::implies( Permission const & perm ) const +{ + // check type + if (SOCKET != perm.m_type) + return false; + SocketPermission const & demanded = static_cast< SocketPermission const & >( perm ); + + // check actions + if ((m_actions & demanded.m_actions) != demanded.m_actions) + return false; + + // check ports + if (demanded.m_lowerPort < m_lowerPort) + return false; + if (demanded.m_upperPort > m_upperPort) + return false; + + // quick check host (DNS names: RFC 1034/1035) + if (m_host.equalsIgnoreAsciiCase( demanded.m_host )) + return true; + // check for host wildcards + if (m_wildCardHost) + { + OUString const & demanded_host = demanded.m_host; + if (demanded_host.getLength() <= m_host.getLength()) + return false; + sal_Int32 len = m_host.getLength() -1; // skip star + return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( + demanded_host.getStr() + demanded_host.getLength() - len, len, + m_host.pData->buffer + 1, len )); + } + if (demanded.m_wildCardHost) + return false; + + // compare IP addresses + if (! resolveHost()) + return false; + if (! demanded.resolveHost()) + return false; + return m_ip == demanded.m_ip; +} + +OUString SocketPermission::toString() const +{ + OUStringBuffer buf( 48 ); + // host + buf.append( "com.sun.star.connection.SocketPermission (host=\"" ); + buf.append( m_host ); + if (m_resolvedHost) + { + buf.append( '[' ); + buf.append( m_ip ); + buf.append( ']' ); + } + // port + if (0 != m_lowerPort || 65535 != m_upperPort) + { + buf.append( ':' ); + if (m_lowerPort > 0) + buf.append( m_lowerPort ); + if (m_upperPort > m_lowerPort) + { + buf.append( '-' ); + if (m_upperPort < 65535) + buf.append( m_upperPort ); + } + } + // actions + buf.append( "\", actions=\"" ); + buf.append( makeStrings( m_actions, s_actions ) ); + buf.append( "\")" ); + return buf.makeStringAndClear(); +} + +namespace { + +class FilePermission : public Permission +{ + static char const * s_actions []; + sal_Int32 m_actions; + + OUString m_url; + bool m_allFiles; + +public: + FilePermission( + io::FilePermission const & perm, + ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ); + virtual bool implies( Permission const & perm ) const override; + virtual OUString toString() const override; +}; + +} + +char const * FilePermission::s_actions [] = { "read", "write", "execute", "delete", nullptr }; + +static OUString const & getWorkingDir() +{ + static OUString s_workingDir = []() { + OUString workingDir; + ::osl_getProcessWorkingDir(&workingDir.pData); + return workingDir; + }(); + return s_workingDir; +} + +FilePermission::FilePermission( + io::FilePermission const & perm, + ::rtl::Reference< Permission > const & next ) + : Permission( FILE, next ) + , m_actions( makeMask( perm.Actions, s_actions ) ) + , m_url( perm.URL ) + , m_allFiles( perm.URL == "<<ALL FILES>>" ) +{ + if ( m_allFiles) + return; + + if ( m_url == "*" ) + { + m_url = getWorkingDir() + "/*"; + } + else if ( m_url == "-" ) + { + m_url = getWorkingDir() + "/-"; + } + else if (!m_url.startsWith("file:///")) + { + // relative path + OUString out; + oslFileError rc = ::osl_getAbsoluteFileURL( + getWorkingDir().pData, perm.URL.pData, &out.pData ); + m_url = (osl_File_E_None == rc ? out : perm.URL); // fallback + } +#ifdef _WIN32 + // correct win drive letters + if (9 < m_url.getLength() && '|' == m_url[ 9 ]) // file:///X| + { + constexpr OUStringLiteral s_colon = u":"; + // common case in API is a ':' (sal), so convert '|' to ':' + m_url = m_url.replaceAt( 9, 1, s_colon ); + } +#endif +} + +bool FilePermission::implies( Permission const & perm ) const +{ + // check type + if (FILE != perm.m_type) + return false; + FilePermission const & demanded = static_cast< FilePermission const & >( perm ); + + // check actions + if ((m_actions & demanded.m_actions) != demanded.m_actions) + return false; + + // check url + if (m_allFiles) + return true; + if (demanded.m_allFiles) + return false; + +#ifdef _WIN32 + if (m_url.equalsIgnoreAsciiCase( demanded.m_url )) + return true; +#else + if (m_url == demanded.m_url ) + return true; +#endif + if (m_url.getLength() > demanded.m_url.getLength()) + return false; + // check /- wildcard: all files and recursive in that path + if (m_url.endsWith("/-")) + { + // demanded url must start with granted path (including path trailing path sep) + sal_Int32 len = m_url.getLength() -1; +#ifdef _WIN32 + return (0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( + demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )); +#else + return (0 == ::rtl_ustr_reverseCompare_WithLength( + demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )); +#endif + } + // check /* wildcard: all files in that path (not recursive!) + if (m_url.endsWith("/*")) + { + // demanded url must start with granted path (including path trailing path sep) + sal_Int32 len = m_url.getLength() -1; +#ifdef _WIN32 + return ((0 == ::rtl_ustr_compareIgnoreAsciiCase_WithLength( + demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) && + (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths +#else + return ((0 == ::rtl_ustr_reverseCompare_WithLength( + demanded.m_url.pData->buffer, len, m_url.pData->buffer, len )) && + (0 > demanded.m_url.indexOf( '/', len ))); // in addition, no deeper paths +#endif + } + return false; +} + +OUString FilePermission::toString() const +{ + return + // url + "com.sun.star.io.FilePermission (url=\"" + m_url + // actions + + "\", actions=\"" + makeStrings( m_actions, s_actions ) + "\")"; +} + +namespace { + +class RuntimePermission : public Permission +{ + OUString m_name; + +public: + RuntimePermission( + security::RuntimePermission const & perm, + ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ) + : Permission( RUNTIME, next ) + , m_name( perm.Name ) + {} + virtual bool implies( Permission const & perm ) const override; + virtual OUString toString() const override; +}; + +} + +bool RuntimePermission::implies( Permission const & perm ) const +{ + // check type + if (RUNTIME != perm.m_type) + return false; + RuntimePermission const & demanded = static_cast< RuntimePermission const & >( perm ); + + // check name + return m_name == demanded.m_name; +} + +OUString RuntimePermission::toString() const +{ + return "com.sun.star.security.RuntimePermission (name=\"" + + m_name + "\")"; +} + + +bool AllPermission::implies( Permission const & ) const +{ + return true; +} + +OUString AllPermission::toString() const +{ + return "com.sun.star.security.AllPermission"; +} + + +PermissionCollection::PermissionCollection( + Sequence< Any > const & permissions, PermissionCollection const & addition ) + : m_head( addition.m_head ) +{ + Any const * perms = permissions.getConstArray(); + for ( sal_Int32 nPos = permissions.getLength(); nPos--; ) + { + Any const & perm = perms[ nPos ]; + Type const & perm_type = perm.getValueType(); + + // supported permission types + if (perm_type.equals( cppu::UnoType<io::FilePermission>::get())) + { + m_head = new FilePermission( + *static_cast< io::FilePermission const * >( perm.pData ), m_head ); + } + else if (perm_type.equals( cppu::UnoType<connection::SocketPermission>::get())) + { + m_head = new SocketPermission( + *static_cast< connection::SocketPermission const * >( perm.pData ), m_head ); + } + else if (perm_type.equals( cppu::UnoType<security::RuntimePermission>::get())) + { + m_head = new RuntimePermission( + *static_cast< security::RuntimePermission const * >( perm.pData ), m_head ); + } + else if (perm_type.equals( cppu::UnoType<security::AllPermission>::get())) + { + m_head = new AllPermission( m_head ); + } + else + { + throw RuntimeException( "checking for unsupported permission type: " + perm_type.getTypeName() ); + } + } +} +#ifdef __DIAGNOSE + +Sequence< OUString > PermissionCollection::toStrings() const +{ + vector< OUString > strings; + strings.reserve( 8 ); + for ( Permission * perm = m_head.get(); perm; perm = perm->m_next.get() ) + { + strings.push_back( perm->toString() ); + } + return Sequence< OUString >( strings.data(), strings.size() ); +} +#endif + +static bool implies( + ::rtl::Reference< Permission > const & head, Permission const & demanded ) +{ + for ( Permission * perm = head.get(); perm; perm = perm->m_next.get() ) + { + if (perm->implies( demanded )) + return true; + } + return false; +} + +#ifdef __DIAGNOSE + +static void demanded_diag( + Permission const & perm ) +{ + OUStringBuffer buf( 48 ); + buf.append( "demanding " ); + buf.append( perm.toString() ); + buf.append( " => ok." ); + OString str( + OUStringToOString( buf.makeStringAndClear(), RTL_TEXTENCODING_ASCII_US ) ); + SAL_INFO("stoc",( "%s", str.getStr() ); +} +#endif + +static void throwAccessControlException( + Permission const & perm, Any const & demanded_perm ) +{ + throw security::AccessControlException( + "access denied: " + perm.toString(), + Reference< XInterface >(), demanded_perm ); +} + +void PermissionCollection::checkPermission( Any const & perm ) const +{ + Type const & demanded_type = perm.getValueType(); + + // supported permission types + // stack object of SimpleReferenceObject are ok, as long as they are not + // assigned to a ::rtl::Reference<> (=> delete this) + if (demanded_type.equals( cppu::UnoType<io::FilePermission>::get())) + { + FilePermission demanded( + *static_cast< io::FilePermission const * >( perm.pData ) ); + if (implies( m_head, demanded )) + { +#ifdef __DIAGNOSE + demanded_diag( demanded ); +#endif + return; + } + throwAccessControlException( demanded, perm ); + } + else if (demanded_type.equals( cppu::UnoType<connection::SocketPermission>::get())) + { + SocketPermission demanded( + *static_cast< connection::SocketPermission const * >( perm.pData ) ); + if (implies( m_head, demanded )) + { +#ifdef __DIAGNOSE + demanded_diag( demanded ); +#endif + return; + } + throwAccessControlException( demanded, perm ); + } + else if (demanded_type.equals( cppu::UnoType<security::RuntimePermission>::get())) + { + RuntimePermission demanded( + *static_cast< security::RuntimePermission const * >( perm.pData ) ); + if (implies( m_head, demanded )) + { +#ifdef __DIAGNOSE + demanded_diag( demanded ); +#endif + return; + } + throwAccessControlException( demanded, perm ); + } + else if (demanded_type.equals( cppu::UnoType<security::AllPermission>::get())) + { + AllPermission demanded; + if (implies( m_head, demanded )) + { +#ifdef __DIAGNOSE + demanded_diag( demanded ); +#endif + return; + } + throwAccessControlException( demanded, perm ); + } + else + { + throw RuntimeException( "checking for unsupported permission type: " + demanded_type.getTypeName() ); + } +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/security/permissions.h b/stoc/source/security/permissions.h new file mode 100644 index 000000000..eb0b30778 --- /dev/null +++ b/stoc/source/security/permissions.h @@ -0,0 +1,85 @@ +/* -*- 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 . + */ +#ifndef INCLUDED_STOC_SOURCE_SECURITY_PERMISSIONS_H +#define INCLUDED_STOC_SOURCE_SECURITY_PERMISSIONS_H + +#include <rtl/ref.hxx> +#include <rtl/ustring.hxx> +#include <salhelper/simplereferenceobject.hxx> + +namespace com::sun::star::uno { class Any; } +namespace com::sun::star::uno { template <class E> class Sequence; } + +namespace stoc_sec +{ + +class Permission : public ::salhelper::SimpleReferenceObject +{ +public: + ::rtl::Reference< Permission > m_next; + // mode + enum t_type { ALL, RUNTIME, SOCKET, FILE } m_type; + + Permission( + t_type type, + ::rtl::Reference< Permission > const & next ) + : m_next( next ) + , m_type( type ) + {} + + virtual bool implies( Permission const & perm ) const = 0; + virtual OUString toString() const = 0; +}; + +class AllPermission : public Permission +{ +public: + explicit AllPermission( + ::rtl::Reference< Permission > const & next = ::rtl::Reference< Permission >() ) + : Permission( ALL, next ) + {} + + virtual bool implies( Permission const & ) const override; + virtual OUString toString() const override; +}; + + +class PermissionCollection +{ + ::rtl::Reference< Permission > m_head; +public: + PermissionCollection() + {} + explicit PermissionCollection( ::rtl::Reference< Permission > const & single ) + : m_head( single ) + {} + PermissionCollection( + css::uno::Sequence< css::uno::Any > const & permissions, + PermissionCollection const & addition = PermissionCollection() ); +#ifdef __DIAGNOSE + css::uno::Sequence< OUString > toStrings() const; +#endif + void checkPermission( css::uno::Any const & perm ) const; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/servicemanager/servicemanager.cxx b/stoc/source/servicemanager/servicemanager.cxx new file mode 100644 index 000000000..77651b0f0 --- /dev/null +++ b/stoc/source/servicemanager/servicemanager.cxx @@ -0,0 +1,1476 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/any.hxx> +#include <osl/mutex.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> + +#include <cppuhelper/basemutex.hxx> +#include <cppuhelper/factory.hxx> +#include <cppuhelper/weakref.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/compbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <comphelper/sequence.hxx> + +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XSingleComponentFactory.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/lang/XEventListener.hpp> +#include <com/sun/star/lang/DisposedException.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/container/XSet.hpp> +#include <com/sun/star/container/XElementAccess.hpp> +#include <com/sun/star/container/XEnumeration.hpp> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/uno/XComponentContext.hpp> + +#include <iterator> +#include <mutex> +#include <string_view> +#include <unordered_map> +#include <unordered_set> + +using namespace com::sun::star; +using namespace css::uno; +using namespace css::beans; +using namespace css::registry; +using namespace css::lang; +using namespace css::container; +using namespace cppu; +using namespace osl; + +namespace { + +Sequence< OUString > retrieveAsciiValueList( + const Reference< XSimpleRegistry > &xReg, const OUString &keyName ) +{ + Reference< XEnumerationAccess > xAccess( xReg, UNO_QUERY ); + Sequence< OUString > seq; + if( xAccess.is() ) + { + Reference< XEnumeration > xEnum = xAccess->createEnumeration(); + while( xEnum.is() && xEnum->hasMoreElements() ) + { + Reference< XSimpleRegistry > xTempReg; + xEnum->nextElement() >>= xTempReg; + if( xTempReg.is() ) + { + const Sequence< OUString > seq2 = retrieveAsciiValueList( xTempReg, keyName ); + + if( seq2.hasElements() ) + { + sal_Int32 n1Len = seq.getLength(); + sal_Int32 n2Len = seq2.getLength(); + + seq.realloc( n1Len + n2Len ); + std::copy(seq2.begin(), seq2.end(), std::next(seq.getArray(), n1Len)); + } + } + } + } + else if( xReg.is () ) + { + try + { + Reference< XRegistryKey > rRootKey = xReg->getRootKey(); + if( rRootKey.is() ) + { + Reference<XRegistryKey > xKey = rRootKey->openKey(keyName); + if( xKey.is() ) + { + seq = xKey->getAsciiListValue(); + } + } + } + catch( InvalidRegistryException & ) + { + } + catch (InvalidValueException &) + { + } + } + return seq; +} + +/***************************************************************************** + Enumeration by ServiceName +*****************************************************************************/ + +typedef std::unordered_set< Reference<XInterface > > HashSet_Ref; + + +class ServiceEnumeration_Impl : public WeakImplHelper< XEnumeration > +{ +public: + explicit ServiceEnumeration_Impl( const Sequence< Reference<XInterface > > & rFactories ) + : aFactories( rFactories ) + , nIt( 0 ) + {} + + // XEnumeration + sal_Bool SAL_CALL hasMoreElements() override; + Any SAL_CALL nextElement() override; +private: + std::mutex aMutex; + Sequence< Reference<XInterface > > aFactories; + sal_Int32 nIt; +}; + +// XEnumeration +sal_Bool ServiceEnumeration_Impl::hasMoreElements() +{ + std::scoped_lock aGuard( aMutex ); + return nIt != aFactories.getLength(); +} + +// XEnumeration +Any ServiceEnumeration_Impl::nextElement() +{ + std::scoped_lock aGuard( aMutex ); + if( nIt == aFactories.getLength() ) + throw NoSuchElementException("no more elements"); + + return Any( &aFactories.getConstArray()[nIt++], cppu::UnoType<XInterface>::get()); +} + + +class PropertySetInfo_Impl : public WeakImplHelper< beans::XPropertySetInfo > +{ + Sequence< beans::Property > m_properties; + +public: + explicit PropertySetInfo_Impl( Sequence< beans::Property > const & properties ) + : m_properties( properties ) + {} + + // XPropertySetInfo impl + virtual Sequence< beans::Property > SAL_CALL getProperties() override; + virtual beans::Property SAL_CALL getPropertyByName( OUString const & name ) override; + virtual sal_Bool SAL_CALL hasPropertyByName( OUString const & name ) override; +}; + +Sequence< beans::Property > PropertySetInfo_Impl::getProperties() +{ + return m_properties; +} + +beans::Property PropertySetInfo_Impl::getPropertyByName( OUString const & name ) +{ + beans::Property const * p = m_properties.getConstArray(); + for ( sal_Int32 nPos = m_properties.getLength(); nPos--; ) + { + if (p[ nPos ].Name == name) + return p[ nPos ]; + } + throw beans::UnknownPropertyException( + "unknown property: " + name ); +} + +sal_Bool PropertySetInfo_Impl::hasPropertyByName( OUString const & name ) +{ + return std::any_of(std::cbegin(m_properties), std::cend(m_properties), + [&name](const beans::Property& rProp) { return rProp.Name == name; }); +} + + +/***************************************************************************** + Enumeration by implementation +*****************************************************************************/ +class ImplementationEnumeration_Impl : public WeakImplHelper< XEnumeration > +{ +public: + explicit ImplementationEnumeration_Impl( const HashSet_Ref & rImplementationMap ) + : aImplementationMap( rImplementationMap ) + , aIt( aImplementationMap.begin() ) + {} + + // XEnumeration + virtual sal_Bool SAL_CALL hasMoreElements() override; + virtual Any SAL_CALL nextElement() override; + +private: + std::mutex aMutex; + HashSet_Ref aImplementationMap; + HashSet_Ref::iterator aIt; +}; + +// XEnumeration +sal_Bool ImplementationEnumeration_Impl::hasMoreElements() +{ + std::scoped_lock aGuard( aMutex ); + return aIt != aImplementationMap.end(); +} + +// XEnumeration +Any ImplementationEnumeration_Impl::nextElement() +{ + std::scoped_lock aGuard( aMutex ); + if( aIt == aImplementationMap.end() ) + throw NoSuchElementException("no more elements"); + + Any ret( &(*aIt), cppu::UnoType<XInterface>::get()); + ++aIt; + return ret; +} + +/***************************************************************************** + Hash tables +*****************************************************************************/ +typedef std::unordered_set +< + OUString +> HashSet_OWString; + +typedef std::unordered_multimap +< + OUString, + Reference<XInterface > +> HashMultimap_OWString_Interface; + +typedef std::unordered_map +< + OUString, + Reference<XInterface > +> HashMap_OWString_Interface; + +/***************************************************************************** + class OServiceManager_Listener +*****************************************************************************/ +class OServiceManager_Listener : public WeakImplHelper< XEventListener > +{ +private: + WeakReference<XSet > xSMgr; + +public: + explicit OServiceManager_Listener( const Reference<XSet > & rSMgr ) + : xSMgr( rSMgr ) + {} + + // XEventListener + virtual void SAL_CALL disposing(const EventObject & rEvt ) override; +}; + +void OServiceManager_Listener::disposing(const EventObject & rEvt ) +{ + Reference<XSet > x( xSMgr ); + if( !x.is() ) + return; + + try + { + x->remove( Any( &rEvt.Source, cppu::UnoType<XInterface>::get()) ); + } + catch( const IllegalArgumentException & ) + { + OSL_FAIL( "IllegalArgumentException caught" ); + } + catch( const NoSuchElementException & ) + { + OSL_FAIL( "NoSuchElementException caught" ); + } +} + + +/***************************************************************************** + class OServiceManager +*****************************************************************************/ + +typedef WeakComponentImplHelper< + lang::XMultiServiceFactory, lang::XMultiComponentFactory, lang::XServiceInfo, + lang::XInitialization, + container::XSet, container::XContentEnumerationAccess, + beans::XPropertySet > t_OServiceManager_impl; + +class OServiceManager + : public cppu::BaseMutex + , public t_OServiceManager_impl +{ +public: + explicit OServiceManager( Reference< XComponentContext > const & xContext ); + + // XInitialization + void SAL_CALL initialize( Sequence< Any > const & args ) override; + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XMultiComponentFactory + virtual Reference< XInterface > SAL_CALL createInstanceWithContext( + OUString const & rServiceSpecifier, Reference< XComponentContext > const & xContext ) override; + virtual Reference< XInterface > SAL_CALL createInstanceWithArgumentsAndContext( + OUString const & rServiceSpecifier, + Sequence< Any > const & rArguments, + Reference< XComponentContext > const & xContext ) override; +// virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() +// throw (RuntimeException); + + // XMultiServiceFactory + virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + virtual Reference<XInterface > SAL_CALL createInstance(const OUString &) override; + virtual Reference<XInterface > SAL_CALL createInstanceWithArguments(const OUString &, const Sequence<Any >& Arguments) override; + + // The same as the getAvailableServiceNames, but only unique names + Sequence< OUString > getUniqueAvailableServiceNames( + HashSet_OWString & aNameSet ); + + // XElementAccess + virtual Type SAL_CALL getElementType() override; + virtual sal_Bool SAL_CALL hasElements() override; + + // XEnumerationAccess + virtual Reference<XEnumeration > SAL_CALL createEnumeration() override; + + // XSet + virtual sal_Bool SAL_CALL has( const Any & Element ) override; + virtual void SAL_CALL insert( const Any & Element ) override; + virtual void SAL_CALL remove( const Any & Element ) override; + + // XContentEnumerationAccess + //Sequence< OUString > getAvailableServiceNames() throw( (Exception) ); + virtual Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override; + + // XComponent + virtual void SAL_CALL dispose() override; + + // XPropertySet + Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + void SAL_CALL setPropertyValue(const OUString& PropertyName, const Any& aValue) override; + Any SAL_CALL getPropertyValue(const OUString& PropertyName) override; + void SAL_CALL addPropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override; + void SAL_CALL removePropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override; + void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override; + void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override; + +protected: + bool is_disposed() const; + void check_undisposed() const; + virtual void SAL_CALL disposing() override; + + bool haveFactoryWithThisImplementation(const OUString& aImplName); + + virtual Sequence< Reference< XInterface > > queryServiceFactories( + const OUString& aServiceName, Reference< XComponentContext > const & xContext ); + + Reference< XComponentContext > m_xContext; + + Reference< beans::XPropertySetInfo > m_xPropertyInfo; + + // factories which have been loaded and not inserted( by XSet::insert) + // are remembered by this set. + HashSet_Ref m_SetLoadedFactories; +private: + + Reference<XEventListener > getFactoryListener(); + + + HashMultimap_OWString_Interface m_ServiceMap; + HashSet_Ref m_ImplementationMap; + HashMap_OWString_Interface m_ImplementationNameMap; + Reference<XEventListener > xFactoryListener; + bool m_bInDisposing; +}; + + +bool OServiceManager::is_disposed() const +{ + // ought to be guarded by m_mutex: + return (m_bInDisposing || rBHelper.bDisposed); +} + + +void OServiceManager::check_undisposed() const +{ + if (is_disposed()) + { + throw lang::DisposedException( + "service manager instance has already been disposed!", + static_cast<OWeakObject *>(const_cast<OServiceManager *>(this)) ); + } +} + + +typedef WeakComponentImplHelper< + lang::XMultiServiceFactory, lang::XMultiComponentFactory, lang::XServiceInfo, + container::XSet, container::XContentEnumerationAccess, + beans::XPropertySet > t_OServiceManagerWrapper_impl; + +class OServiceManagerWrapper : public cppu::BaseMutex, public t_OServiceManagerWrapper_impl +{ + Reference< XComponentContext > m_xContext; + Reference< XMultiComponentFactory > m_root; + Reference< XMultiComponentFactory > const & getRoot() const + { + if (! m_root.is()) + { + throw lang::DisposedException( + "service manager instance has already been disposed!" ); + } + return m_root; + } + +protected: + virtual void SAL_CALL disposing() override; + +public: + explicit OServiceManagerWrapper( + Reference< XComponentContext > const & xContext ); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override + { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->getImplementationName(); } + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override + { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->supportsService( ServiceName ); } + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override + { return Reference< XServiceInfo >(getRoot(), UNO_QUERY_THROW)->getSupportedServiceNames(); } + + // XMultiComponentFactory + virtual Reference< XInterface > SAL_CALL createInstanceWithContext( + OUString const & rServiceSpecifier, Reference< XComponentContext > const & xContext ) override + { return getRoot()->createInstanceWithContext( rServiceSpecifier, xContext ); } + virtual Reference< XInterface > SAL_CALL createInstanceWithArgumentsAndContext( + OUString const & rServiceSpecifier, + Sequence< Any > const & rArguments, + Reference< XComponentContext > const & xContext ) override + { return getRoot()->createInstanceWithArgumentsAndContext( rServiceSpecifier, rArguments, xContext ); } +// virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() +// throw (RuntimeException); + + // XMultiServiceFactory + virtual Sequence< OUString > SAL_CALL getAvailableServiceNames() override + { return getRoot()->getAvailableServiceNames(); } + virtual Reference<XInterface > SAL_CALL createInstance(const OUString & name) override + { return getRoot()->createInstanceWithContext( name, m_xContext ); } + virtual Reference<XInterface > SAL_CALL createInstanceWithArguments(const OUString & name, const Sequence<Any >& Arguments) override + { return getRoot()->createInstanceWithArgumentsAndContext( name, Arguments, m_xContext ); } + + // XElementAccess + virtual Type SAL_CALL getElementType() override + { return Reference< XElementAccess >(getRoot(), UNO_QUERY_THROW)->getElementType(); } + virtual sal_Bool SAL_CALL hasElements() override + { return Reference< XElementAccess >(getRoot(), UNO_QUERY_THROW)->hasElements(); } + + // XEnumerationAccess + virtual Reference<XEnumeration > SAL_CALL createEnumeration() override + { return Reference< XEnumerationAccess >(getRoot(), UNO_QUERY_THROW)->createEnumeration(); } + + // XSet + virtual sal_Bool SAL_CALL has( const Any & Element ) override + { return Reference< XSet >(getRoot(), UNO_QUERY_THROW)->has( Element ); } + virtual void SAL_CALL insert( const Any & Element ) override + { Reference< XSet >(getRoot(), UNO_QUERY_THROW)->insert( Element ); } + virtual void SAL_CALL remove( const Any & Element ) override + { Reference< XSet >(getRoot(), UNO_QUERY_THROW)->remove( Element ); } + + // XContentEnumerationAccess + //Sequence< OUString > getAvailableServiceNames() throw( (Exception) ); + virtual Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override + { return Reference< XContentEnumerationAccess >(getRoot(), UNO_QUERY_THROW)->createContentEnumeration( aServiceName ); } + + // XPropertySet + Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override + { return Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->getPropertySetInfo(); } + + void SAL_CALL setPropertyValue(const OUString& PropertyName, const Any& aValue) override; + Any SAL_CALL getPropertyValue(const OUString& PropertyName) override; + + void SAL_CALL addPropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override + { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->addPropertyChangeListener( PropertyName, aListener ); } + void SAL_CALL removePropertyChangeListener(const OUString& PropertyName, const Reference<XPropertyChangeListener >& aListener) override + { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->removePropertyChangeListener( PropertyName, aListener ); } + void SAL_CALL addVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override + { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->addVetoableChangeListener( PropertyName, aListener ); } + void SAL_CALL removeVetoableChangeListener(const OUString& PropertyName, const Reference<XVetoableChangeListener >& aListener) override + { Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->removeVetoableChangeListener( PropertyName, aListener ); } +}; + +void SAL_CALL OServiceManagerWrapper::setPropertyValue( + const OUString& PropertyName, const Any& aValue ) +{ + if ( PropertyName == "DefaultContext" ) + { + Reference< XComponentContext > xContext; + if (!(aValue >>= xContext)) + { + throw IllegalArgumentException( + "no XComponentContext given!", + static_cast<OWeakObject *>(this), 1 ); + } + + MutexGuard aGuard( m_aMutex ); + m_xContext = xContext; + + } + else + { + Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->setPropertyValue( PropertyName, aValue ); + } +} + +Any SAL_CALL OServiceManagerWrapper::getPropertyValue( + const OUString& PropertyName ) +{ + if ( PropertyName == "DefaultContext" ) + { + MutexGuard aGuard( m_aMutex ); + if( m_xContext.is() ) + return Any( m_xContext ); + else + return Any(); + } + else + { + return Reference< XPropertySet >(getRoot(), UNO_QUERY_THROW)->getPropertyValue( PropertyName ); + } +} + +void OServiceManagerWrapper::disposing() +{ + m_xContext.clear(); + +// no m_root->dispose(), because every context disposes its service manager... + m_root.clear(); +} + +OServiceManagerWrapper::OServiceManagerWrapper( + Reference< XComponentContext > const & xContext ) + : t_OServiceManagerWrapper_impl( m_aMutex ) + , m_xContext( xContext ) + , m_root( xContext->getServiceManager() ) +{ + if (! m_root.is()) + { + throw RuntimeException( + "no service manager to wrap" ); + } +} + + +/** + * Create a ServiceManager + */ +OServiceManager::OServiceManager( Reference< XComponentContext > const & xContext ) + : t_OServiceManager_impl( m_aMutex ) + , m_xContext( xContext ) + , m_bInDisposing( false ) +{} + +// XComponent +void OServiceManager::dispose() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + return; + t_OServiceManager_impl::dispose(); +} + +void OServiceManager::disposing() +{ + // dispose all factories + HashSet_Ref aImpls; + { + MutexGuard aGuard( m_aMutex ); + m_bInDisposing = true; + aImpls = m_ImplementationMap; + } + for( const auto& rxImpl : aImpls ) + { + try + { + Reference<XComponent > xComp( Reference<XComponent >::query( rxImpl ) ); + if( xComp.is() ) + xComp->dispose(); + } + catch (const RuntimeException & exc) + { + SAL_INFO("stoc", "RuntimeException occurred upon disposing factory: " << exc); + } + } + + // dispose + HashSet_Ref aImplMap; + { + MutexGuard aGuard( m_aMutex ); + // erase all members + m_ServiceMap = HashMultimap_OWString_Interface(); + aImplMap = m_ImplementationMap; + m_ImplementationMap = HashSet_Ref(); + m_ImplementationNameMap = HashMap_OWString_Interface(); + m_SetLoadedFactories= HashSet_Ref(); + } + + m_xContext.clear(); + + // not only the Event should hold the object + OSL_ASSERT( m_refCount != 1 ); +} + +// XPropertySet +Reference<XPropertySetInfo > OServiceManager::getPropertySetInfo() +{ + check_undisposed(); + if (! m_xPropertyInfo.is()) + { + Sequence< beans::Property > seq{ beans::Property( + "DefaultContext", -1, cppu::UnoType<decltype(m_xContext)>::get(), 0 ) }; + Reference< beans::XPropertySetInfo > xInfo( new PropertySetInfo_Impl( seq ) ); + + MutexGuard aGuard( m_aMutex ); + if (! m_xPropertyInfo.is()) + { + m_xPropertyInfo = xInfo; + } + } + return m_xPropertyInfo; +} + +void OServiceManager::setPropertyValue( + const OUString& PropertyName, const Any& aValue ) +{ + check_undisposed(); + if ( PropertyName != "DefaultContext" ) + { + throw UnknownPropertyException( + "unknown property " + PropertyName, + static_cast<OWeakObject *>(this) ); + } + + Reference< XComponentContext > xContext; + if (!(aValue >>= xContext)) + { + throw IllegalArgumentException( + "no XComponentContext given!", + static_cast<OWeakObject *>(this), 1 ); + } + + MutexGuard aGuard( m_aMutex ); + m_xContext = xContext; +} + +Any OServiceManager::getPropertyValue(const OUString& PropertyName) +{ + check_undisposed(); + if ( PropertyName == "DefaultContext" ) + { + MutexGuard aGuard( m_aMutex ); + if( m_xContext.is() ) + return Any( m_xContext ); + else + return Any(); + } + else + { + UnknownPropertyException except; + except.Message = "ServiceManager : unknown property " + PropertyName; + throw except; + } +} + +void OServiceManager::addPropertyChangeListener( + const OUString&, const Reference<XPropertyChangeListener >&) +{ + check_undisposed(); + throw UnknownPropertyException("unsupported"); +} + +void OServiceManager::removePropertyChangeListener( + const OUString&, const Reference<XPropertyChangeListener >&) +{ + check_undisposed(); + throw UnknownPropertyException("unsupported"); +} + +void OServiceManager::addVetoableChangeListener( + const OUString&, const Reference<XVetoableChangeListener >&) +{ + check_undisposed(); + throw UnknownPropertyException("unsupported"); +} + +void OServiceManager::removeVetoableChangeListener( + const OUString&, const Reference<XVetoableChangeListener >&) +{ + check_undisposed(); + throw UnknownPropertyException("unsupported"); +} + +// OServiceManager +Reference<XEventListener > OServiceManager::getFactoryListener() +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + if( !xFactoryListener.is() ) + xFactoryListener = new OServiceManager_Listener( this ); + return xFactoryListener; +} + +// XMultiServiceFactory, XContentEnumeration +Sequence< OUString > OServiceManager::getUniqueAvailableServiceNames( + HashSet_OWString & aNameSet ) +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + for( const auto& rEntry : m_ServiceMap ) + aNameSet.insert( rEntry.first ); + + /* do not return the implementation names + HashMap_OWString_Interface m_ImplementationNameMap; + HashMap_OWString_Interface::iterator aIt = m_ImplementationNameMap.begin(); + while( aIt != m_ImplementationNameMap.end() ) + aNameSet.insert( (*aIt++).first ); + */ + + return comphelper::containerToSequence(aNameSet); +} + +// XMultiComponentFactory +Reference< XInterface > OServiceManager::createInstanceWithContext( + OUString const & rServiceSpecifier, + Reference< XComponentContext > const & xContext ) +{ + check_undisposed(); +#if OSL_DEBUG_LEVEL > 0 + Reference< beans::XPropertySet > xProps( xContext->getServiceManager(), UNO_QUERY ); + OSL_ASSERT( xProps.is() ); + if (xProps.is()) + { + Reference< XComponentContext > xDefContext; + xProps->getPropertyValue( "DefaultContext" ) >>= xDefContext; + OSL_ENSURE( + xContext == xDefContext, + "### default context of service manager singleton differs from context holding it!" ); + } +#endif + + const Sequence< Reference< XInterface > > factories( + queryServiceFactories( rServiceSpecifier, xContext ) ); + for ( Reference< XInterface > const & xFactory : factories ) + { + try + { + if (xFactory.is()) + { + Reference< XSingleComponentFactory > xFac( xFactory, UNO_QUERY ); + if (xFac.is()) + { + return xFac->createInstanceWithContext( xContext ); + } + else + { + Reference< XSingleServiceFactory > xFac2( xFactory, UNO_QUERY ); + if (xFac2.is()) + { + SAL_INFO("stoc", "ignoring given context raising service " << rServiceSpecifier << "!!!"); + return xFac2->createInstance(); + } + } + } + } + catch (const lang::DisposedException & exc) + { + SAL_INFO("stoc", "DisposedException occurred: " << exc); + } + } + + return Reference< XInterface >(); +} +// XMultiComponentFactory +Reference< XInterface > OServiceManager::createInstanceWithArgumentsAndContext( + OUString const & rServiceSpecifier, + Sequence< Any > const & rArguments, + Reference< XComponentContext > const & xContext ) +{ + check_undisposed(); +#if OSL_DEBUG_LEVEL > 0 + Reference< beans::XPropertySet > xProps( xContext->getServiceManager(), UNO_QUERY ); + OSL_ASSERT( xProps.is() ); + if (xProps.is()) + { + Reference< XComponentContext > xDefContext; + xProps->getPropertyValue( "DefaultContext" ) >>= xDefContext; + OSL_ENSURE( + xContext == xDefContext, + "### default context of service manager singleton differs from context holding it!" ); + } +#endif + + const Sequence< Reference< XInterface > > factories( + queryServiceFactories( rServiceSpecifier, xContext ) ); + for ( Reference< XInterface > const & xFactory : factories ) + { + try + { + if (xFactory.is()) + { + Reference< XSingleComponentFactory > xFac( xFactory, UNO_QUERY ); + if (xFac.is()) + { + return xFac->createInstanceWithArgumentsAndContext( rArguments, xContext ); + } + else + { + Reference< XSingleServiceFactory > xFac2( xFactory, UNO_QUERY ); + if (xFac2.is()) + { + SAL_INFO("stoc", "ignoring given context raising service " << rServiceSpecifier << "!!!"); + return xFac2->createInstanceWithArguments( rArguments ); + } + } + } + } + catch (const lang::DisposedException & exc) + { + SAL_INFO("stoc", "DisposedException occurred: " << exc); + } + } + + return Reference< XInterface >(); +} + +// XMultiServiceFactory, XMultiComponentFactory, XContentEnumeration +Sequence< OUString > OServiceManager::getAvailableServiceNames() +{ + check_undisposed(); + // all names + HashSet_OWString aNameSet; + return getUniqueAvailableServiceNames( aNameSet ); +} + +// XMultipleServiceFactory +Reference<XInterface > OServiceManager::createInstance( + const OUString& rServiceSpecifier ) +{ + return createInstanceWithContext( + rServiceSpecifier, m_xContext ); +} + +// XMultipleServiceFactory +Reference<XInterface > OServiceManager::createInstanceWithArguments( + const OUString& rServiceSpecifier, + const Sequence<Any >& rArguments ) +{ + return createInstanceWithArgumentsAndContext( + rServiceSpecifier, rArguments, m_xContext ); +} + +// XInitialization +void OServiceManager::initialize( Sequence< Any > const & ) +{ + check_undisposed(); + OSL_FAIL( "not impl!" ); +} + +// XServiceInfo +OUString OServiceManager::getImplementationName() +{ + return "com.sun.star.comp.stoc.OServiceManager"; +} + +// XServiceInfo +sal_Bool OServiceManager::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > OServiceManager::getSupportedServiceNames() +{ + return { "com.sun.star.lang.MultiServiceFactory", "com.sun.star.lang.ServiceManager" }; +} + + +Sequence< Reference< XInterface > > OServiceManager::queryServiceFactories( + const OUString& aServiceName, Reference< XComponentContext > const & ) +{ + Sequence< Reference< XInterface > > ret; + + MutexGuard aGuard( m_aMutex ); + ::std::pair< + HashMultimap_OWString_Interface::iterator, + HashMultimap_OWString_Interface::iterator> p( + m_ServiceMap.equal_range( aServiceName ) ); + + if (p.first == p.second) // no factories + { + // no service found, look for an implementation + HashMap_OWString_Interface::iterator aIt = m_ImplementationNameMap.find( aServiceName ); + if( aIt != m_ImplementationNameMap.end() ) + { + Reference< XInterface > const & x = aIt->second; + // an implementation found + ret = Sequence< Reference< XInterface > >( &x, 1 ); + } + } + else + { + ::std::vector< Reference< XInterface > > vec; + vec.reserve( 4 ); + while (p.first != p.second) + { + vec.push_back( p.first->second ); + ++p.first; + } + ret = Sequence< Reference< XInterface > >( vec.data(), vec.size() ); + } + + return ret; +} + +// XContentEnumerationAccess +Reference<XEnumeration > OServiceManager::createContentEnumeration( + const OUString& aServiceName ) +{ + check_undisposed(); + Sequence< Reference< XInterface > > factories( + OServiceManager::queryServiceFactories( aServiceName, m_xContext ) ); + if (factories.hasElements()) + return new ServiceEnumeration_Impl( factories ); + else + return Reference< XEnumeration >(); +} + +// XEnumeration +Reference<XEnumeration > OServiceManager::createEnumeration() +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + return new ImplementationEnumeration_Impl( m_ImplementationMap ); +} + +// XElementAccess +Type OServiceManager::getElementType() +{ + check_undisposed(); + return cppu::UnoType<XInterface>::get(); +} + +// XElementAccess +sal_Bool OServiceManager::hasElements() +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + return !m_ImplementationMap.empty(); +} + +// XSet +sal_Bool OServiceManager::has( const Any & Element ) +{ + check_undisposed(); + if( Element.getValueTypeClass() == TypeClass_INTERFACE ) + { + Reference<XInterface > xEle( Element, UNO_QUERY_THROW ); + MutexGuard aGuard( m_aMutex ); + return m_ImplementationMap.find( xEle ) != + m_ImplementationMap.end(); + } + else if (auto implName = o3tl::tryAccess<OUString>(Element)) + { + MutexGuard aGuard( m_aMutex ); + return m_ImplementationNameMap.find( *implName ) != + m_ImplementationNameMap.end(); + } + return false; +} + +// XSet +void OServiceManager::insert( const Any & Element ) +{ + check_undisposed(); + if( Element.getValueTypeClass() != TypeClass_INTERFACE ) + { + throw IllegalArgumentException( + "exception interface, got " + Element.getValueType().getTypeName(), + Reference< XInterface >(), 0 ); + } + Reference<XInterface > xEle( Element, UNO_QUERY_THROW ); + + { + MutexGuard aGuard( m_aMutex ); + HashSet_Ref::iterator aIt = m_ImplementationMap.find( xEle ); + if( aIt != m_ImplementationMap.end() ) + { + throw ElementExistException( "element already exists!" ); + } + + // put into the implementation hashmap + m_ImplementationMap.insert( xEle ); + + // put into the implementation name hashmap + Reference<XServiceInfo > xInfo( Reference<XServiceInfo >::query( xEle ) ); + if( xInfo.is() ) + { + OUString aImplName = xInfo->getImplementationName(); + if( !aImplName.isEmpty() ) + m_ImplementationNameMap[ aImplName ] = xEle; + + //put into the service map + const Sequence< OUString > aServiceNames = xInfo->getSupportedServiceNames(); + for( const OUString& rServiceName : aServiceNames ) + { + m_ServiceMap.emplace( + rServiceName, *o3tl::doAccess<Reference<XInterface>>(Element) ); + } + } + } + // add the disposing listener to the factory + Reference<XComponent > xComp( Reference<XComponent >::query( xEle ) ); + if( xComp.is() ) + xComp->addEventListener( getFactoryListener() ); +} + +// helper function +bool OServiceManager::haveFactoryWithThisImplementation(const OUString& aImplName) +{ + return ( m_ImplementationNameMap.find(aImplName) != m_ImplementationNameMap.end()); +} + +// XSet +void OServiceManager::remove( const Any & Element ) +{ + if (is_disposed()) + return; + + Reference<XInterface > xEle; + if (Element.getValueTypeClass() == TypeClass_INTERFACE) + { + xEle.set( Element, UNO_QUERY_THROW ); + } + else if (auto implName = o3tl::tryAccess<OUString>(Element)) + { + MutexGuard aGuard( m_aMutex ); + HashMap_OWString_Interface::const_iterator const iFind( + m_ImplementationNameMap.find( *implName ) ); + if (iFind == m_ImplementationNameMap.end()) + { + throw NoSuchElementException( + "element is not in: " + *implName, + static_cast< OWeakObject * >(this) ); + } + xEle = iFind->second; + } + else + { + throw IllegalArgumentException( + "expected interface or string, got " + Element.getValueType().getTypeName(), + Reference< XInterface >(), 0 ); + } + + // remove the disposing listener from the factory + Reference<XComponent > xComp( Reference<XComponent >::query( xEle ) ); + if( xComp.is() ) + xComp->removeEventListener( getFactoryListener() ); + + MutexGuard aGuard( m_aMutex ); + HashSet_Ref::iterator aIt = m_ImplementationMap.find( xEle ); + if( aIt == m_ImplementationMap.end() ) + { + throw NoSuchElementException( + "element not found", + static_cast< OWeakObject * >(this) ); + } + //First remove all factories which have been loaded by ORegistryServiceManager. + m_SetLoadedFactories.erase( *aIt); + //Remove from the implementation map. It contains all factories of m_SetLoadedFactories + //which have been added directly through XSet, that is not via ORegistryServiceManager + m_ImplementationMap.erase( aIt ); + + // remove from the implementation name hashmap + Reference<XServiceInfo > xInfo( Reference<XServiceInfo >::query( xEle ) ); + if( xInfo.is() ) + { + OUString aImplName = xInfo->getImplementationName(); + if( !aImplName.isEmpty() ) + m_ImplementationNameMap.erase( aImplName ); + } + + //remove from the service map + Reference<XServiceInfo > xSF( Reference<XServiceInfo >::query( xEle ) ); + if( !xSF.is() ) + return; + + const Sequence< OUString > aServiceNames = xSF->getSupportedServiceNames(); + for( const OUString& rServiceName : aServiceNames ) + { + std::pair<HashMultimap_OWString_Interface::iterator, HashMultimap_OWString_Interface::iterator> p = + m_ServiceMap.equal_range( rServiceName ); + + while( p.first != p.second ) + { + if( xEle == (*p.first).second ) + { + m_ServiceMap.erase( p.first ); + break; + } + ++p.first; + } + } +} + +/***************************************************************************** + class ORegistryServiceManager +*****************************************************************************/ +class ORegistryServiceManager : public OServiceManager +{ +public: + explicit ORegistryServiceManager( Reference< XComponentContext > const & xContext ); + + // XInitialization + void SAL_CALL initialize(const Sequence< Any >& Arguments) override; + + // XServiceInfo + OUString SAL_CALL getImplementationName() override + { return "com.sun.star.comp.stoc.ORegistryServiceManager"; } + + Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XMultiServiceFactory + Sequence< OUString > SAL_CALL getAvailableServiceNames() override; + + // XContentEnumerationAccess + //Sequence< OUString > getAvailableServiceNames() throw( (Exception) ); + Reference<XEnumeration > SAL_CALL createContentEnumeration(const OUString& aServiceName) override; + + // XComponent + void SAL_CALL dispose() override; + + // OServiceManager + Reference<XPropertySetInfo > SAL_CALL getPropertySetInfo() override; + Any SAL_CALL getPropertyValue(const OUString& PropertyName) override; + +protected: + //OServiceManager + Sequence< Reference< XInterface > > queryServiceFactories( + const OUString& aServiceName, Reference< XComponentContext > const & xContext ) override; +private: + Reference<XRegistryKey > getRootKey(); + Reference<XInterface > loadWithImplementationName( + const OUString & rImplName, Reference< XComponentContext > const & xContext ); + Sequence<OUString> getFromServiceName(std::u16string_view serviceName) const; + Reference<XInterface > loadWithServiceName( + std::u16string_view rImplName, Reference< XComponentContext > const & xContext ); + void fillAllNamesFromRegistry( HashSet_OWString & ); + + bool m_searchedRegistry; + Reference<XSimpleRegistry > m_xRegistry; // readonly property Registry + Reference<XRegistryKey > m_xRootKey; + +#if OSL_DEBUG_LEVEL > 0 + bool m_init; +#endif +}; + +/** + * Create a ServiceManager + */ +ORegistryServiceManager::ORegistryServiceManager( Reference< XComponentContext > const & xContext ) + : OServiceManager( xContext ) + , m_searchedRegistry(false) +#if OSL_DEBUG_LEVEL > 0 + , m_init( false ) +#endif +{ +} + +// XComponent +void ORegistryServiceManager::dispose() +{ + if (rBHelper.bDisposed || rBHelper.bInDispose) + return; + OServiceManager::dispose(); + // dispose + MutexGuard aGuard( m_aMutex ); + // erase all members + m_xRegistry.clear(); + m_xRootKey.clear(); +} + +/** + * Return the root key of the registry. The Default registry service is ordered + * if no registry is set. + */ +//Reference<XServiceProvider > create_DefaultRegistry_ServiceProvider(); + +Reference<XRegistryKey > ORegistryServiceManager::getRootKey() +{ + if( !m_xRootKey.is() ) + { + MutexGuard aGuard( m_aMutex ); + // DefaultRegistry suchen !!!! + if( !m_xRegistry.is() && !m_searchedRegistry ) + { + // NB. we only search this once + m_searchedRegistry = true; + + m_xRegistry.set( + createInstanceWithContext( + "com.sun.star.registry.DefaultRegistry", + m_xContext ), + UNO_QUERY ); + } + if( m_xRegistry.is() && !m_xRootKey.is() ) + m_xRootKey = m_xRegistry->getRootKey(); + } + + return m_xRootKey; +} + +/** + * Create a service provider from the registry with an implementation name + */ +Reference<XInterface > ORegistryServiceManager::loadWithImplementationName( + const OUString& name, Reference< XComponentContext > const & xContext ) +{ + Reference<XInterface > ret; + + Reference<XRegistryKey > xRootKey = getRootKey(); + if( !xRootKey.is() ) + return ret; + + try + { + OUString implementationName = "/IMPLEMENTATIONS/" + name; + Reference<XRegistryKey > xImpKey = m_xRootKey->openKey(implementationName); + + if( xImpKey.is() ) + { + Reference< lang::XMultiServiceFactory > xMgr; + if (xContext.is()) + xMgr.set( xContext->getServiceManager(), UNO_QUERY_THROW ); + else + xMgr.set( this ); + ret = createSingleRegistryFactory( xMgr, name, xImpKey ); + insert( Any( ret ) ); + // Remember this factory as loaded in contrast to inserted ( XSet::insert) + // factories. Those loaded factories in this set are candidates for being + // released on an unloading notification. + m_SetLoadedFactories.insert( ret); + } + } + catch (InvalidRegistryException &) + { + } + + return ret; +} + +/** + * Return all implementation out of the registry. + */ +Sequence<OUString> ORegistryServiceManager::getFromServiceName( + std::u16string_view serviceName ) const +{ + OUString buf = OUString::Concat("/SERVICES/") + serviceName; + return retrieveAsciiValueList( m_xRegistry, buf ); +} + +/** + * Create a service provider from the registry + */ +Reference<XInterface > ORegistryServiceManager::loadWithServiceName( + std::u16string_view serviceName, Reference< XComponentContext > const & xContext ) +{ + const Sequence<OUString> implEntries = getFromServiceName( serviceName ); + for (const auto& rEntry : implEntries) + { + Reference< XInterface > x( loadWithImplementationName( rEntry, xContext ) ); + if (x.is()) + return x; + } + + return Reference<XInterface >(); +} + +/** + * Return a sequence of all service names from the registry. + */ +void ORegistryServiceManager::fillAllNamesFromRegistry( HashSet_OWString & rSet ) +{ + Reference<XRegistryKey > xRootKey = getRootKey(); + if( !xRootKey.is() ) + return; + + try + { + Reference<XRegistryKey > xServicesKey = xRootKey->openKey( "SERVICES" ); + // root + /Services + / + if( xServicesKey.is() ) + { + sal_Int32 nPrefix = xServicesKey->getKeyName().getLength() +1; + const Sequence<Reference<XRegistryKey > > aKeys = xServicesKey->openKeys(); + std::transform(aKeys.begin(), aKeys.end(), std::inserter(rSet, rSet.end()), + [nPrefix](const Reference<XRegistryKey>& rKey) -> OUString { + return rKey->getKeyName().copy( nPrefix ); }); + } + } + catch (InvalidRegistryException &) + { + } +} + +// XInitialization +void ORegistryServiceManager::initialize(const Sequence< Any >& Arguments) +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + if (Arguments.hasElements()) + { + m_xRootKey.clear(); + Arguments[ 0 ] >>= m_xRegistry; + } +#if OSL_DEBUG_LEVEL > 0 + // to find all bootstrapping processes to be fixed... + OSL_ENSURE( !m_init, "### second init of service manager instance!" ); + m_init = true; +#endif +} + +// XMultiServiceFactory, XContentEnumeration +Sequence< OUString > ORegistryServiceManager::getAvailableServiceNames() +{ + check_undisposed(); + MutexGuard aGuard( m_aMutex ); + // all names + HashSet_OWString aNameSet; + + // all names from the registry + fillAllNamesFromRegistry( aNameSet ); + + return OServiceManager::getUniqueAvailableServiceNames( aNameSet ); +} + +// XServiceInfo +Sequence< OUString > ORegistryServiceManager::getSupportedServiceNames() +{ + return { "com.sun.star.lang.MultiServiceFactory", "com.sun.star.lang.RegistryServiceManager" }; +} + + +// OServiceManager +Sequence< Reference< XInterface > > ORegistryServiceManager::queryServiceFactories( + const OUString& aServiceName, Reference< XComponentContext > const & xContext ) +{ + Sequence< Reference< XInterface > > ret( + OServiceManager::queryServiceFactories( aServiceName, xContext ) ); + if (ret.hasElements()) + { + return ret; + } + else + { + MutexGuard aGuard( m_aMutex ); + Reference< XInterface > x( loadWithServiceName( aServiceName, xContext ) ); + if (! x.is()) + x = loadWithImplementationName( aServiceName, xContext ); + return Sequence< Reference< XInterface > >( &x, 1 ); + } +} + +// XContentEnumerationAccess +Reference<XEnumeration > ORegistryServiceManager::createContentEnumeration( + const OUString& aServiceName ) +{ + check_undisposed(); + MutexGuard aGuard(m_aMutex); + // get all implementation names registered under this service name from the registry + const Sequence<OUString> aImpls = getFromServiceName( aServiceName ); + // load and insert all factories specified by the registry + for( const OUString& aImplName : aImpls ) + { + if ( !haveFactoryWithThisImplementation(aImplName) ) + { + loadWithImplementationName( aImplName, m_xContext ); + } + } + // call the superclass to enumerate all contents + return OServiceManager::createContentEnumeration( aServiceName ); +} + +// OServiceManager +Reference<XPropertySetInfo > ORegistryServiceManager::getPropertySetInfo() +{ + check_undisposed(); + if (! m_xPropertyInfo.is()) + { + Sequence< beans::Property > seq{ + beans::Property("DefaultContext", -1, cppu::UnoType<decltype(m_xContext)>::get(), 0), + beans::Property("Registry", -1, cppu::UnoType<decltype(m_xRegistry)>::get(), + beans::PropertyAttribute::READONLY) + }; + Reference< beans::XPropertySetInfo > xInfo( new PropertySetInfo_Impl( seq ) ); + + MutexGuard aGuard( m_aMutex ); + if (! m_xPropertyInfo.is()) + { + m_xPropertyInfo = xInfo; + } + } + return m_xPropertyInfo; +} + +Any ORegistryServiceManager::getPropertyValue(const OUString& PropertyName) +{ + check_undisposed(); + if ( PropertyName == "Registry" ) + { + MutexGuard aGuard( m_aMutex ); + if( m_xRegistry.is() ) + return Any( m_xRegistry ); + else + return Any(); + } + return OServiceManager::getPropertyValue( PropertyName ); +} + +} // namespace + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_OServiceManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OServiceManager(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_ORegistryServiceManager_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new ORegistryServiceManager(context)); +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_OServiceManagerWrapper_get_implementation( + css::uno::XComponentContext *context, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new OServiceManagerWrapper(context)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/simpleregistry/simpleregistry.cxx b/stoc/source/simpleregistry/simpleregistry.cxx new file mode 100644 index 000000000..6291889a3 --- /dev/null +++ b/stoc/source/simpleregistry/simpleregistry.cxx @@ -0,0 +1,941 @@ +/* -*- 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 <sal/config.h> + +#include <cstdlib> +#include <mutex> +#include <vector> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/registry/InvalidRegistryException.hpp> +#include <com/sun/star/registry/InvalidValueException.hpp> +#include <com/sun/star/registry/MergeConflictException.hpp> +#include <com/sun/star/registry/RegistryKeyType.hpp> +#include <com/sun/star/registry/XRegistryKey.hpp> +#include <com/sun/star/registry/XSimpleRegistry.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <comphelper/sequence.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <registry/registry.hxx> +#include <registry/regtype.h> +#include <rtl/ref.hxx> +#include <rtl/string.h> +#include <rtl/string.hxx> +#include <rtl/textcvt.h> +#include <rtl/textenc.h> +#include <rtl/ustring.h> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace com::sun::star::uno { class XComponentContext; } + +namespace { + +class SimpleRegistry: + public cppu::WeakImplHelper< + css::registry::XSimpleRegistry, css::lang::XServiceInfo > +{ +public: + SimpleRegistry() {} + + std::mutex mutex_; + +private: + virtual OUString SAL_CALL getURL() override; + + virtual void SAL_CALL open( + OUString const & rURL, sal_Bool bReadOnly, sal_Bool bCreate) override; + + virtual sal_Bool SAL_CALL isValid() override; + + virtual void SAL_CALL close() override; + + virtual void SAL_CALL destroy() override; + + virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL + getRootKey() override; + + virtual sal_Bool SAL_CALL isReadOnly() override; + + virtual void SAL_CALL mergeKey( + OUString const & aKeyName, OUString const & aUrl) override; + + virtual OUString SAL_CALL getImplementationName() override + { return "com.sun.star.comp.stoc.SimpleRegistry"; } + + virtual sal_Bool SAL_CALL supportsService(OUString const & ServiceName) override + { return cppu::supportsService(this, ServiceName); } + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override + { + css::uno::Sequence< OUString > names { "com.sun.star.registry.SimpleRegistry" }; + return names; + } + + Registry registry_; +}; + +class Key: public cppu::WeakImplHelper< css::registry::XRegistryKey > { +public: + Key( + rtl::Reference< SimpleRegistry > const & registry, + RegistryKey const & key): + registry_(registry), key_(key) {} + +private: + virtual OUString SAL_CALL getKeyName() override; + + virtual sal_Bool SAL_CALL isReadOnly() override; + + virtual sal_Bool SAL_CALL isValid() override; + + virtual css::registry::RegistryKeyType SAL_CALL getKeyType( + OUString const & rKeyName) override; + + virtual css::registry::RegistryValueType SAL_CALL getValueType() override; + + virtual sal_Int32 SAL_CALL getLongValue() override; + + virtual void SAL_CALL setLongValue(sal_Int32 value) override; + + virtual css::uno::Sequence< sal_Int32 > SAL_CALL getLongListValue() override; + + virtual void SAL_CALL setLongListValue( + css::uno::Sequence< sal_Int32 > const & seqValue) override; + + virtual OUString SAL_CALL getAsciiValue() override; + + virtual void SAL_CALL setAsciiValue(OUString const & value) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getAsciiListValue() override; + + virtual void SAL_CALL setAsciiListValue( + css::uno::Sequence< OUString > const & seqValue) override; + + virtual OUString SAL_CALL getStringValue() override; + + virtual void SAL_CALL setStringValue(OUString const & value) override; + + virtual css::uno::Sequence< OUString > SAL_CALL getStringListValue() override; + + virtual void SAL_CALL setStringListValue( + css::uno::Sequence< OUString > const & seqValue) override; + + virtual css::uno::Sequence< sal_Int8 > SAL_CALL getBinaryValue() override; + + virtual void SAL_CALL setBinaryValue( + css::uno::Sequence< sal_Int8 > const & value) override; + + virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL openKey( + OUString const & aKeyName) override; + + virtual css::uno::Reference< css::registry::XRegistryKey > SAL_CALL + createKey(OUString const & aKeyName) override; + + virtual void SAL_CALL closeKey() override; + + virtual void SAL_CALL deleteKey(OUString const & rKeyName) override; + + virtual + css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > > + SAL_CALL openKeys() override; + + virtual css::uno::Sequence< OUString > SAL_CALL getKeyNames() override; + + virtual sal_Bool SAL_CALL createLink( + OUString const & aLinkName, OUString const & aLinkTarget) override; + + virtual void SAL_CALL deleteLink(OUString const & rLinkName) override; + + virtual OUString SAL_CALL getLinkTarget(OUString const & rLinkName) override; + + virtual OUString SAL_CALL getResolvedName(OUString const & aKeyName) override; + + rtl::Reference< SimpleRegistry > registry_; + RegistryKey key_; +}; + +OUString Key::getKeyName() { + std::scoped_lock guard(registry_->mutex_); + return key_.getName(); +} + +sal_Bool Key::isReadOnly() +{ + std::scoped_lock guard(registry_->mutex_); + return key_.isReadOnly(); +} + +sal_Bool Key::isValid() { + std::scoped_lock guard(registry_->mutex_); + return key_.isValid(); +} + +css::registry::RegistryKeyType Key::getKeyType(OUString const & ) +{ + return css::registry::RegistryKeyType_KEY; +} + +css::registry::RegistryValueType Key::getValueType() +{ + std::scoped_lock guard(registry_->mutex_); + RegValueType type; + sal_uInt32 size; + RegError err = key_.getValueInfo(OUString(), &type, &size); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::INVALID_VALUE: + type = RegValueType::NOT_DEFINED; + break; + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getValueType:" + " underlying RegistryKey::getValueInfo() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + switch (type) { + default: + std::abort(); // this cannot happen + // pseudo-fall-through to avoid warnings on MSC + case RegValueType::NOT_DEFINED: + return css::registry::RegistryValueType_NOT_DEFINED; + case RegValueType::LONG: + return css::registry::RegistryValueType_LONG; + case RegValueType::STRING: + return css::registry::RegistryValueType_ASCII; + case RegValueType::UNICODE: + return css::registry::RegistryValueType_STRING; + case RegValueType::BINARY: + return css::registry::RegistryValueType_BINARY; + case RegValueType::LONGLIST: + return css::registry::RegistryValueType_LONGLIST; + case RegValueType::STRINGLIST: + return css::registry::RegistryValueType_ASCIILIST; + case RegValueType::UNICODELIST: + return css::registry::RegistryValueType_STRINGLIST; + } +} + +sal_Int32 Key::getLongValue() +{ + std::scoped_lock guard(registry_->mutex_); + sal_Int32 value; + RegError err = key_.getValue(OUString(), &value); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::INVALID_VALUE: + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getLongValue:" + " underlying RegistryKey::getValue() = RegError::INVALID_VALUE", + static_cast< OWeakObject * >(this)); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getLongValue:" + " underlying RegistryKey::getValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + return value; +} + +void Key::setLongValue(sal_Int32 value) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_.setValue( + OUString(), RegValueType::LONG, &value, sizeof (sal_Int32)); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setLongValue:" + " underlying RegistryKey::setValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +css::uno::Sequence< sal_Int32 > Key::getLongListValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryValueList< sal_Int32 > list; + RegError err = key_.getLongListValue(OUString(), list); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::VALUE_NOT_EXISTS: + return css::uno::Sequence< sal_Int32 >(); + case RegError::INVALID_VALUE: + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getLongListValue:" + " underlying RegistryKey::getLongListValue() =" + " RegError::INVALID_VALUE", + static_cast< OWeakObject * >(this)); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getLongListValue:" + " underlying RegistryKey::getLongListValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getLongListValue:" + " underlying RegistryKey::getLongListValue() too large", + static_cast< OWeakObject * >(this)); + } + css::uno::Sequence< sal_Int32 > value(static_cast< sal_Int32 >(n)); + auto aValueRange = asNonConstRange(value); + for (sal_uInt32 i = 0; i < n; ++i) { + aValueRange[static_cast< sal_Int32 >(i)] = list.getElement(i); + } + return value; +} + +void Key::setLongListValue(css::uno::Sequence< sal_Int32 > const & seqValue) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_.setLongListValue( + OUString(), seqValue.getConstArray(), static_cast< sal_uInt32 >(seqValue.getLength())); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setLongListValue:" + " underlying RegistryKey::setLongListValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +OUString Key::getAsciiValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegValueType type; + sal_uInt32 size; + RegError err = key_.getValueInfo(OUString(), &type, &size); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey::getValueInfo() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + if (type != RegValueType::STRING) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey type = " + OUString::number(static_cast<int>(type)), + static_cast< OWeakObject * >(this)); + } + // size contains terminating null (error in underlying registry.cxx): + if (size == 0) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey size 0 cannot happen due to" + " design error", + static_cast< OWeakObject * >(this)); + } + if (size > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey size too large", + static_cast< OWeakObject * >(this)); + } + std::vector< char > list(size); + err = key_.getValue(OUString(), list.data()); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey::getValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + if (list[size - 1] != '\0') { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey value must be null-terminated due" + " to design error", + static_cast< OWeakObject * >(this)); + } + OUString value; + if (!rtl_convertStringToUString( + &value.pData, list.data(), + static_cast< sal_Int32 >(size - 1), RTL_TEXTENCODING_UTF8, + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) + { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getAsciiValue:" + " underlying RegistryKey not UTF-8", + static_cast< OWeakObject * >(this)); + } + return value; +} + +void Key::setAsciiValue(OUString const & value) +{ + std::scoped_lock guard(registry_->mutex_); + OString utf8; + if (!value.convertToString( + &utf8, RTL_TEXTENCODING_UTF8, + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) + { + throw css::uno::RuntimeException( + "com.sun.star.registry.SimpleRegistry key setAsciiValue:" + " value not UTF-16", + static_cast< OWeakObject * >(this)); + } + RegError err = key_.setValue( + OUString(), RegValueType::STRING, + const_cast< char * >(utf8.getStr()), utf8.getLength() + 1); + // +1 for terminating null (error in underlying registry.cxx) + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setAsciiValue:" + " underlying RegistryKey::setValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +css::uno::Sequence< OUString > Key::getAsciiListValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryValueList< char * > list; + RegError err = key_.getStringListValue(OUString(), list); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::VALUE_NOT_EXISTS: + return css::uno::Sequence< OUString >(); + case RegError::INVALID_VALUE: + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getAsciiListValue: underlying" + " RegistryKey::getStringListValue() = RegError::INVALID_VALUE", + static_cast< OWeakObject * >(this)); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key" + " getAsciiListValue: underlying" + " RegistryKey::getStringListValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getAsciiListValue: underlying" + " RegistryKey::getStringListValue() too large", + static_cast< OWeakObject * >(this)); + } + css::uno::Sequence< OUString > value(static_cast< sal_Int32 >(n)); + auto aValueRange = asNonConstRange(value); + for (sal_uInt32 i = 0; i < n; ++i) { + char * el = list.getElement(i); + sal_Int32 size = rtl_str_getLength(el); + if (!rtl_convertStringToUString( + &aValueRange[static_cast< sal_Int32 >(i)].pData, el, size, + RTL_TEXTENCODING_UTF8, + (RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR | + RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR))) + { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getAsciiListValue: underlying RegistryKey not" + " UTF-8", + static_cast< OWeakObject * >(this)); + } + } + return value; +} + +void Key::setAsciiListValue( + css::uno::Sequence< OUString > const & seqValue) +{ + std::scoped_lock guard(registry_->mutex_); + std::vector< OString > list; + for (const auto& rValue : seqValue) { + OString utf8; + if (!rValue.convertToString( + &utf8, RTL_TEXTENCODING_UTF8, + (RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR))) + { + throw css::uno::RuntimeException( + "com.sun.star.registry.SimpleRegistry key" + " setAsciiListValue: value not UTF-16", + static_cast< OWeakObject * >(this)); + } + list.push_back(utf8); + } + std::vector< char * > list2; + for (const auto& rItem : list) + { + list2.push_back(const_cast< char * >(rItem.getStr())); + } + RegError err = key_.setStringListValue( + OUString(), list2.data(), static_cast< sal_uInt32 >(list2.size())); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key" + " setAsciiListValue: underlying" + " RegistryKey::setStringListValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +OUString Key::getStringValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegValueType type; + sal_uInt32 size; + RegError err = key_.getValueInfo(OUString(), &type, &size); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey::getValueInfo() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + if (type != RegValueType::UNICODE) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey type = " + OUString::number(static_cast<int>(type)), + static_cast< OWeakObject * >(this)); + } + // size contains terminating null and is *2 (error in underlying + // registry.cxx): + if (size == 0 || (size & 1) == 1) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey size 0 or odd cannot happen due to" + " design error", + static_cast< OWeakObject * >(this)); + } + if (size > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey size too large", + static_cast< OWeakObject * >(this)); + } + std::vector< sal_Unicode > list(size); + err = key_.getValue(OUString(), list.data()); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey::getValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + if (list[size/2 - 1] != 0) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getStringValue:" + " underlying RegistryKey value must be null-terminated due" + " to design error", + static_cast< OWeakObject * >(this)); + } + return OUString(list.data(), static_cast< sal_Int32 >(size/2 - 1)); +} + +void Key::setStringValue(OUString const & value) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_.setValue( + OUString(), RegValueType::UNICODE, + const_cast< sal_Unicode * >(value.getStr()), + (value.getLength() + 1) * sizeof (sal_Unicode)); + // +1 for terminating null (error in underlying registry.cxx) + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setStringValue:" + " underlying RegistryKey::setValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +css::uno::Sequence< OUString > Key::getStringListValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryValueList< sal_Unicode * > list; + RegError err = key_.getUnicodeListValue(OUString(), list); + switch (err) { + case RegError::NO_ERROR: + break; + case RegError::VALUE_NOT_EXISTS: + return css::uno::Sequence< OUString >(); + case RegError::INVALID_VALUE: + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getStringListValue: underlying" + " RegistryKey::getUnicodeListValue() = RegError::INVALID_VALUE", + static_cast< OWeakObject * >(this)); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key" + " getStringListValue: underlying" + " RegistryKey::getUnicodeListValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key" + " getStringListValue: underlying" + " RegistryKey::getUnicodeListValue() too large", + static_cast< OWeakObject * >(this)); + } + css::uno::Sequence< OUString > value(static_cast< sal_Int32 >(n)); + auto aValueRange = asNonConstRange(value); + for (sal_uInt32 i = 0; i < n; ++i) { + aValueRange[static_cast< sal_Int32 >(i)] = list.getElement(i); + } + return value; +} + +void Key::setStringListValue( + css::uno::Sequence< OUString > const & seqValue) +{ + std::scoped_lock guard(registry_->mutex_); + std::vector< sal_Unicode * > list; + list.reserve(seqValue.getLength()); + std::transform(seqValue.begin(), seqValue.end(), std::back_inserter(list), + [](const OUString& rValue) -> sal_Unicode* { return const_cast<sal_Unicode*>(rValue.getStr()); }); + RegError err = key_.setUnicodeListValue( + OUString(), list.data(), static_cast< sal_uInt32 >(list.size())); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key" + " setStringListValue: underlying" + " RegistryKey::setUnicodeListValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +css::uno::Sequence< sal_Int8 > Key::getBinaryValue() +{ + std::scoped_lock guard(registry_->mutex_); + RegValueType type; + sal_uInt32 size; + RegError err = key_.getValueInfo(OUString(), &type, &size); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getBinaryValue:" + " underlying RegistryKey::getValueInfo() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + if (type != RegValueType::BINARY) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getBinaryValue:" + " underlying RegistryKey type = " + OUString::number(static_cast<int>(type)), + static_cast< OWeakObject * >(this)); + } + if (size > SAL_MAX_INT32) { + throw css::registry::InvalidValueException( + "com.sun.star.registry.SimpleRegistry key getBinaryValue:" + " underlying RegistryKey size too large", + static_cast< OWeakObject * >(this)); + } + css::uno::Sequence< sal_Int8 > value(static_cast< sal_Int32 >(size)); + err = key_.getValue(OUString(), value.getArray()); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getBinaryValue:" + " underlying RegistryKey::getValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + return value; +} + +void Key::setBinaryValue(css::uno::Sequence< sal_Int8 > const & value) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_.setValue( + OUString(), RegValueType::BINARY, + const_cast< sal_Int8 * >(value.getConstArray()), + static_cast< sal_uInt32 >(value.getLength())); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key setBinaryValue:" + " underlying RegistryKey::setValue() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +css::uno::Reference< css::registry::XRegistryKey > Key::openKey( + OUString const & aKeyName) +{ + std::scoped_lock guard(registry_->mutex_); + RegistryKey key; + RegError err = key_.openKey(aKeyName, key); + switch (err) { + case RegError::NO_ERROR: + return new Key(registry_, key); + case RegError::KEY_NOT_EXISTS: + return css::uno::Reference< css::registry::XRegistryKey >(); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key openKey:" + " underlying RegistryKey::openKey() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +css::uno::Reference< css::registry::XRegistryKey > Key::createKey( + OUString const & aKeyName) +{ + std::scoped_lock guard(registry_->mutex_); + RegistryKey key; + RegError err = key_.createKey(aKeyName, key); + switch (err) { + case RegError::NO_ERROR: + return new Key(registry_, key); + case RegError::INVALID_KEYNAME: + return css::uno::Reference< css::registry::XRegistryKey >(); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key createKey:" + " underlying RegistryKey::createKey() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +void Key::closeKey() +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_.closeKey(); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key closeKey:" + " underlying RegistryKey::closeKey() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +void Key::deleteKey(OUString const & rKeyName) +{ + std::scoped_lock guard(registry_->mutex_); + RegError err = key_.deleteKey(rKeyName); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key deleteKey:" + " underlying RegistryKey::deleteKey() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > > +Key::openKeys() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryKeyArray list; + RegError err = key_.openSubKeys(OUString(), list); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key openKeys:" + " underlying RegistryKey::openSubKeys() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getKeyNames:" + " underlying RegistryKey::getKeyNames() too large", + static_cast< OWeakObject * >(this)); + } + css::uno::Sequence< css::uno::Reference< css::registry::XRegistryKey > > + keys(static_cast< sal_Int32 >(n)); + auto aKeysRange = asNonConstRange(keys); + for (sal_uInt32 i = 0; i < n; ++i) { + aKeysRange[static_cast< sal_Int32 >(i)] = new Key( + registry_, list.getElement(i)); + } + return keys; +} + +css::uno::Sequence< OUString > Key::getKeyNames() +{ + std::scoped_lock guard(registry_->mutex_); + RegistryKeyNames list; + RegError err = key_.getKeyNames(OUString(), list); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getKeyNames:" + " underlying RegistryKey::getKeyNames() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + sal_uInt32 n = list.getLength(); + if (n > SAL_MAX_INT32) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getKeyNames:" + " underlying RegistryKey::getKeyNames() too large", + static_cast< OWeakObject * >(this)); + } + css::uno::Sequence< OUString > names(static_cast< sal_Int32 >(n)); + auto aNamesRange = asNonConstRange(names); + for (sal_uInt32 i = 0; i < n; ++i) { + aNamesRange[static_cast< sal_Int32 >(i)] = list.getElement(i); + } + return names; +} + +sal_Bool Key::createLink( + OUString const & /*aLinkName*/, OUString const & /*aLinkTarget*/) +{ + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key createLink: links are no longer supported", + static_cast< OWeakObject * >(this)); +} + +void Key::deleteLink(OUString const & /*rLinkName*/) +{ + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key deleteLink: links are no longer supported", + static_cast< OWeakObject * >(this)); +} + +OUString Key::getLinkTarget(OUString const & /*rLinkName*/) +{ + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getLinkTarget: links are no longer supported", + static_cast< OWeakObject * >(this)); +} + +OUString Key::getResolvedName(OUString const & aKeyName) +{ + std::scoped_lock guard(registry_->mutex_); + OUString resolved; + RegError err = key_.getResolvedKeyName(aKeyName, resolved); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry key getResolvedName:" + " underlying RegistryKey::getResolvedName() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + return resolved; +} + +OUString SimpleRegistry::getURL() { + std::scoped_lock guard(mutex_); + return registry_.getName(); +} + +void SimpleRegistry::open( + OUString const & rURL, sal_Bool bReadOnly, sal_Bool bCreate) +{ + std::scoped_lock guard(mutex_); + RegError err = (rURL.isEmpty() && bCreate) + ? RegError::REGISTRY_NOT_EXISTS + : registry_.open(rURL, bReadOnly ? RegAccessMode::READONLY : RegAccessMode::READWRITE); + if (err == RegError::REGISTRY_NOT_EXISTS && bCreate) { + err = registry_.create(rURL); + } + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.open(" + rURL + + "): underlying Registry::open/create() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +sal_Bool SimpleRegistry::isValid() { + std::scoped_lock guard(mutex_); + return registry_.isValid(); +} + +void SimpleRegistry::close() +{ + std::scoped_lock guard(mutex_); + RegError err = registry_.close(); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.close:" + " underlying Registry::close() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +void SimpleRegistry::destroy() +{ + std::scoped_lock guard(mutex_); + RegError err = registry_.destroy(OUString()); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.destroy:" + " underlying Registry::destroy() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +css::uno::Reference< css::registry::XRegistryKey > SimpleRegistry::getRootKey() +{ + std::scoped_lock guard(mutex_); + RegistryKey root; + RegError err = registry_.openRootKey(root); + if (err != RegError::NO_ERROR) { + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.getRootKey:" + " underlying Registry::getRootKey() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } + return new Key(this, root); +} + +sal_Bool SimpleRegistry::isReadOnly() +{ + std::scoped_lock guard(mutex_); + return registry_.isReadOnly(); +} + +void SimpleRegistry::mergeKey( + OUString const & aKeyName, OUString const & aUrl) +{ + std::scoped_lock guard(mutex_); + RegistryKey root; + RegError err = registry_.openRootKey(root); + if (err == RegError::NO_ERROR) { + err = registry_.mergeKey(root, aKeyName, aUrl, false); + } + switch (err) { + case RegError::NO_ERROR: + case RegError::MERGE_CONFLICT: + break; + case RegError::MERGE_ERROR: + throw css::registry::MergeConflictException( + "com.sun.star.registry.SimpleRegistry.mergeKey:" + " underlying Registry::mergeKey() = RegError::MERGE_ERROR", + static_cast< cppu::OWeakObject * >(this)); + default: + throw css::registry::InvalidRegistryException( + "com.sun.star.registry.SimpleRegistry.mergeKey:" + " underlying Registry::getRootKey/mergeKey() = " + OUString::number(static_cast<int>(err)), + static_cast< OWeakObject * >(this)); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface * +com_sun_star_comp_stoc_SimpleRegistry_get_implementation( + SAL_UNUSED_PARAMETER css::uno::XComponentContext *, + css::uno::Sequence<css::uno::Any> const &) +{ + return cppu::acquire(new SimpleRegistry); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/typeconv/convert.cxx b/stoc/source/typeconv/convert.cxx new file mode 100644 index 000000000..1b50c9449 --- /dev/null +++ b/stoc/source/typeconv/convert.cxx @@ -0,0 +1,870 @@ +/* -*- 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 <sal/config.h> + +#include <o3tl/any.hxx> +#include <o3tl/safeint.hxx> +#include <osl/diagnose.h> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> + +#include <typelib/typedescription.hxx> +#include <uno/data.h> + +#ifdef _WIN32 +#include <cmath> +#else +#include <math.h> +#endif +#include <float.h> + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/script/CannotConvertException.hpp> +#include <com/sun/star/script/XTypeConverter.hpp> +#include <com/sun/star/script/FailReason.hpp> + +namespace com::sun::star::uno { class XComponentContext; } + +using namespace css::uno; +using namespace css::lang; +using namespace css::script; +using namespace cppu; +using namespace osl; + +namespace stoc_tcv +{ + +static double round( double aVal ) +{ + bool bPos = (aVal >= 0.0); + aVal = ::fabs( aVal ); + double aUpper = ::ceil( aVal ); + + aVal = ((aUpper-aVal) <= 0.5) ? aUpper : (aUpper - 1.0); + return (bPos ? aVal : -aVal); +} + + +static bool getNumericValue( double & rfVal, const OUString & rStr ) +{ + double fRet = rStr.toDouble(); + if (fRet == 0.0) + { + sal_Int32 nLen = rStr.getLength(); + if (!nLen || (nLen == 1 && rStr[0] == '0')) // common case + { + rfVal = 0.0; + return true; + } + + OUString trim( rStr.trim() ); + + // try hex + sal_Int32 nX = trim.indexOf( 'x' ); + if (nX < 0) + nX = trim.indexOf( 'X' ); + + if (nX > 0 && trim[nX-1] == '0') // 0x + { + bool bNeg = false; + switch (nX) + { + case 2: // (+|-)0x... + if (trim[0] == '-') + bNeg = true; + else if (trim[0] != '+') + return false; + break; + case 1: // 0x... + break; + default: + return false; + } + + OUString aHexRest( trim.copy( nX+1 ) ); + sal_uInt64 nRet = aHexRest.toUInt64( 16 ); + + if (nRet == 0) + { + for ( sal_Int32 nPos = aHexRest.getLength(); nPos--; ) + { + if (aHexRest[nPos] != '0') + return false; + } + } + + rfVal = (bNeg ? -static_cast<double>(nRet) : static_cast<double>(nRet)); + return true; + } + + nLen = trim.getLength(); + sal_Int32 nPos = 0; + + // skip +/- + if (nLen && (trim[0] == '-' || trim[0] == '+')) + ++nPos; + + while (nPos < nLen) // skip leading zeros + { + if (trim[nPos] != '0') + { + if (trim[nPos] != '.') + return false; + ++nPos; + while (nPos < nLen) // skip trailing zeros + { + if (trim[nPos] != '0') + return false; + ++nPos; + } + break; + } + ++nPos; + } + } + rfVal = fRet; + return true; +} + + +static bool getHyperValue( sal_Int64 & rnVal, const OUString & rStr ) +{ + sal_Int32 nLen = rStr.getLength(); + if (!nLen || (nLen == 1 && rStr[0] == '0')) // common case + { + rnVal = 0; + return true; + } + + OUString trim( rStr.trim() ); + + // try hex + sal_Int32 nX = trim.indexOf( 'x' ); + if (nX < 0) + nX = trim.indexOf( 'X' ); + + if (nX >= 0) + { + if (nX > 0 && trim[nX-1] == '0') // 0x + { + bool bNeg = false; + switch (nX) + { + case 2: // (+|-)0x... + if (trim[0] == '-') + bNeg = true; + else if (trim[0] != '+') + return false; + break; + case 1: // 0x... + break; + default: + return false; + } + + OUString aHexRest( trim.copy( nX+1 ) ); + sal_uInt64 nRet = aHexRest.toUInt64( 16 ); + + if (nRet == 0) + { + for ( sal_Int32 nPos = aHexRest.getLength(); nPos--; ) + { + if (aHexRest[nPos] != '0') + return false; + } + } + + rnVal = (bNeg ? -static_cast<sal_Int64>(nRet) : nRet); + return true; + } + return false; + } + + double fVal; + if (getNumericValue( fVal, rStr ) && + fVal >= double(SAL_MIN_INT64) && + fVal <= double(SAL_MAX_UINT64)) + { + rnVal = static_cast<sal_Int64>(round( fVal )); + return true; + } + return false; +} + +namespace { + +class TypeConverter_Impl : public WeakImplHelper< XTypeConverter, XServiceInfo > +{ + // ...misc helpers... + /// @throws CannotConvertException + static sal_Int64 toHyper( + const Any& rAny, sal_Int64 min, sal_uInt64 max = SAL_MAX_UINT64 ); + /// @throws CannotConvertException + static double toDouble( const Any& rAny, double min = -DBL_MAX, double max = DBL_MAX ); + +public: + TypeConverter_Impl(); + + // XServiceInfo + virtual OUString SAL_CALL getImplementationName() override; + virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; + virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override; + + // XTypeConverter + virtual Any SAL_CALL convertTo( const Any& aFrom, const Type& DestinationType ) override; + virtual Any SAL_CALL convertToSimpleType( const Any& aFrom, TypeClass aDestinationType ) override; +}; + +} + +TypeConverter_Impl::TypeConverter_Impl() {} + +// XServiceInfo +OUString TypeConverter_Impl::getImplementationName() +{ + return "com.sun.star.comp.stoc.TypeConverter"; +} + +// XServiceInfo +sal_Bool TypeConverter_Impl::supportsService(const OUString& ServiceName) +{ + return cppu::supportsService(this, ServiceName); +} + +// XServiceInfo +Sequence< OUString > TypeConverter_Impl::getSupportedServiceNames() +{ + return { "com.sun.star.script.Converter" }; +} + + +sal_Int64 TypeConverter_Impl::toHyper( const Any& rAny, sal_Int64 min, sal_uInt64 max ) +{ + sal_Int64 nRet; + TypeClass aDestinationClass = rAny.getValueTypeClass(); + + switch (aDestinationClass) + { + // ENUM + case TypeClass_ENUM: + nRet = *static_cast<sal_Int32 const *>(rAny.getValue()); + break; + // BOOL + case TypeClass_BOOLEAN: + nRet = *o3tl::forceAccess<bool>(rAny) ? 1 : 0; + break; + // CHAR, BYTE + case TypeClass_CHAR: + nRet = *o3tl::forceAccess<sal_Unicode>(rAny); + break; + case TypeClass_BYTE: + nRet = *o3tl::forceAccess<sal_Int8>(rAny); + break; + // SHORT + case TypeClass_SHORT: + nRet = *o3tl::forceAccess<sal_Int16>(rAny); + break; + // UNSIGNED SHORT + case TypeClass_UNSIGNED_SHORT: + nRet = *o3tl::forceAccess<sal_uInt16>(rAny); + break; + // LONG + case TypeClass_LONG: + nRet = *o3tl::forceAccess<sal_Int32>(rAny); + break; + // UNSIGNED LONG + case TypeClass_UNSIGNED_LONG: + nRet = *o3tl::forceAccess<sal_uInt32>(rAny); + break; + // HYPER + case TypeClass_HYPER: + nRet = *o3tl::forceAccess<sal_Int64>(rAny); + break; + // UNSIGNED HYPER + case TypeClass_UNSIGNED_HYPER: + { + auto const n = *o3tl::forceAccess<sal_uInt64>(rAny); + if ((min < 0 || n >= o3tl::make_unsigned(min)) && // lower bound + n <= max) // upper bound + { + return static_cast<sal_Int64>(n); + } + throw CannotConvertException( + "UNSIGNED HYPER out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); + } + + // FLOAT, DOUBLE + case TypeClass_FLOAT: + { + double fVal = round( *o3tl::forceAccess<float>(rAny) ); + if (fVal >= min && fVal <= max) + { + nRet = (fVal >= 0.0 ? static_cast<sal_Int64>(static_cast<sal_uInt64>(fVal)) : static_cast<sal_Int64>(fVal)); + return nRet; + } + throw CannotConvertException( + "FLOAT out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); + } + case TypeClass_DOUBLE: + { + double fVal = round( *o3tl::forceAccess<double>(rAny) ); + if (fVal >= min && fVal <= max) + { + nRet = (fVal >= 0.0 ? static_cast<sal_Int64>(static_cast<sal_uInt64>(fVal)) : static_cast<sal_Int64>(fVal)); + return nRet; + } + throw CannotConvertException( + "DOUBLE out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); + } + + // STRING + case TypeClass_STRING: + { + sal_Int64 nVal = SAL_CONST_INT64(0); + if (! getHyperValue( nVal, *o3tl::forceAccess<OUString>(rAny) )) + { + throw CannotConvertException( + "invalid STRING value!", + Reference<XInterface>(), aDestinationClass, FailReason::IS_NOT_NUMBER, 0 ); + } + nRet = nVal; + if (nVal >= min && (nVal < 0 || o3tl::make_unsigned(nVal) <= max)) + return nRet; + throw CannotConvertException( + "STRING value out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); + } + + default: + throw CannotConvertException( + "TYPE is not supported!", + Reference<XInterface>(), aDestinationClass, FailReason::TYPE_NOT_SUPPORTED, 0 ); + } + + if (nRet >= min && (nRet < 0 || o3tl::make_unsigned(nRet) <= max)) + return nRet; + throw CannotConvertException( + "VALUE is out of range!", + Reference<XInterface>(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); +} + + +double TypeConverter_Impl::toDouble( const Any& rAny, double min, double max ) +{ + double fRet; + TypeClass aDestinationClass = rAny.getValueTypeClass(); + + switch (aDestinationClass) + { + // ENUM + case TypeClass_ENUM: + fRet = *static_cast<sal_Int32 const *>(rAny.getValue()); + break; + // BOOL + case TypeClass_BOOLEAN: + fRet = *o3tl::forceAccess<bool>(rAny) ? 1.0 : 0.0; + break; + // CHAR, BYTE + case TypeClass_CHAR: + fRet = *o3tl::forceAccess<sal_Unicode>(rAny); + break; + case TypeClass_BYTE: + fRet = *o3tl::forceAccess<sal_Int8>(rAny); + break; + // SHORT + case TypeClass_SHORT: + fRet = *o3tl::forceAccess<sal_Int16>(rAny); + break; + // UNSIGNED SHORT + case TypeClass_UNSIGNED_SHORT: + fRet = *o3tl::forceAccess<sal_uInt16>(rAny); + break; + // LONG + case TypeClass_LONG: + fRet = *o3tl::forceAccess<sal_Int32>(rAny); + break; + // UNSIGNED LONG + case TypeClass_UNSIGNED_LONG: + fRet = *o3tl::forceAccess<sal_uInt32>(rAny); + break; + // HYPER + case TypeClass_HYPER: + fRet = static_cast<double>(*o3tl::forceAccess<sal_Int64>(rAny)); + break; + // UNSIGNED HYPER + case TypeClass_UNSIGNED_HYPER: + fRet = static_cast<double>(*o3tl::forceAccess<sal_uInt64>(rAny)); + break; + // FLOAT, DOUBLE + case TypeClass_FLOAT: + fRet = *o3tl::forceAccess<float>(rAny); + break; + case TypeClass_DOUBLE: + fRet = *o3tl::forceAccess<double>(rAny); + break; + + // STRING + case TypeClass_STRING: + { + if (! getNumericValue( fRet, *o3tl::forceAccess<OUString>(rAny) )) + { + throw CannotConvertException( + "invalid STRING value!", + Reference<XInterface>(), aDestinationClass, FailReason::IS_NOT_NUMBER, 0 ); + } + break; + } + + default: + throw CannotConvertException( + "TYPE is not supported!", + Reference< XInterface >(), aDestinationClass, FailReason::TYPE_NOT_SUPPORTED, 0 ); + } + + if (fRet >= min && fRet <= max) + return fRet; + throw CannotConvertException( + "VALUE is out of range!", + Reference< XInterface >(), aDestinationClass, FailReason::OUT_OF_RANGE, 0 ); +} + + +Any SAL_CALL TypeConverter_Impl::convertTo( const Any& rVal, const Type& aDestType ) +{ + const Type& aSourceType = rVal.getValueType(); + if (aSourceType == aDestType) + return rVal; + + TypeClass aSourceClass = aSourceType.getTypeClass(); + TypeClass aDestinationClass = aDestType.getTypeClass(); + + Any aRet; + + // convert to... + switch (aDestinationClass) + { + // --- to VOID ------------------------------------------------------------------------------ + case TypeClass_VOID: + return Any(); + // --- to ANY ------------------------------------------------------------------------------- + case TypeClass_ANY: + return rVal; + + // --- to STRUCT, EXCEPTION ---------------------------------------------------------- + case TypeClass_STRUCT: + case TypeClass_EXCEPTION: + { + // same types or destination type is derived source type? + TypeDescription aSourceTD( aSourceType ); + TypeDescription aDestTD( aDestType ); + if (!typelib_typedescription_isAssignableFrom( aDestTD.get(), aSourceTD.get() )) + { + throw CannotConvertException( + "value is not of same or derived type!", + Reference< XInterface >(), aDestinationClass, + FailReason::SOURCE_IS_NO_DERIVED_TYPE, 0 ); + } + aRet.setValue( rVal.getValue(), aDestTD.get() ); // evtl. .uP.cAsT. + break; + } + // --- to INTERFACE ------------------------------------------------------------------------- + case TypeClass_INTERFACE: + { + if (! rVal.hasValue()) + { + // void -> interface (null) + void * null_ref = nullptr; + // coverity[var_deref_model : FALSE] - null_ref will not be derefed in this case + aRet.setValue( &null_ref, aDestType ); + break; + } + + auto ifc = o3tl::tryAccess<css::uno::Reference<css::uno::XInterface>>( + rVal); + if (!ifc || !ifc->is()) + { + throw CannotConvertException( + "value is not interface", + Reference< XInterface >(), aDestinationClass, FailReason::NO_SUCH_INTERFACE, 0 ); + } + aRet = (*ifc)->queryInterface(aDestType ); + if (! aRet.hasValue()) + { + throw CannotConvertException( + "value does not implement " + aDestType.getTypeName(), + Reference< XInterface >(), aDestinationClass, FailReason::NO_SUCH_INTERFACE, 0 ); + } + break; + } + // --- to SEQUENCE -------------------------------------------------------------------------- + case TypeClass_SEQUENCE: + { + if (aSourceClass==TypeClass_SEQUENCE) + { + if( aSourceType == aDestType ) + return rVal; + + TypeDescription aSourceTD( aSourceType ); + TypeDescription aDestTD( aDestType ); + // For a sequence type notation "[]...", SequenceTypeDescription in + // cppuhelper/source/typemanager.cxx resolves the "..." component type notation part + // only lazily, so it could happen here that bad user input (e.g., "[]" or "[]foo" from + // a Basic script CreateUnoValue call) leads to a bad but as-of-yet undetected + // aDestType, so check it here; this is less likely an issue for the non-sequence type + // classes, whose notation is not resolved lazily based on their syntax: + if (!aDestTD.is()) { + throw css::lang::IllegalArgumentException( + "Bad XTypeConverter::convertTo destination " + aDestType.getTypeName(), + static_cast<cppu::OWeakObject *>(this), 1); + } + typelib_TypeDescription * pSourceElementTD = nullptr; + TYPELIB_DANGER_GET( + &pSourceElementTD, + reinterpret_cast<typelib_IndirectTypeDescription *>(aSourceTD.get())->pType ); + typelib_TypeDescription * pDestElementTD = nullptr; + TYPELIB_DANGER_GET( + &pDestElementTD, + reinterpret_cast<typelib_IndirectTypeDescription *>(aDestTD.get())->pType ); + + sal_uInt32 nPos = (*static_cast<const uno_Sequence * const *>(rVal.getValue()))->nElements; + uno_Sequence * pRet = nullptr; + uno_sequence_construct( + &pRet, aDestTD.get(), nullptr, nPos, + reinterpret_cast< uno_AcquireFunc >(cpp_acquire) ); + aRet.setValue( &pRet, aDestTD.get() ); + uno_destructData( + &pRet, aDestTD.get(), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) ); + // decr ref count + + char * pDestElements = (*static_cast<uno_Sequence * const *>(aRet.getValue()))->elements; + const char * pSourceElements = + (*static_cast<const uno_Sequence * const *>(rVal.getValue()))->elements; + + while (nPos--) + { + char * pDestPos = pDestElements + (nPos * pDestElementTD->nSize); + const char * pSourcePos = pSourceElements + (nPos * pSourceElementTD->nSize); + + Any aElement( + convertTo( Any( pSourcePos, pSourceElementTD ), pDestElementTD->pWeakRef ) ); + + if (!uno_assignData( + pDestPos, pDestElementTD, + (pDestElementTD->eTypeClass == typelib_TypeClass_ANY + ? &aElement + : const_cast< void * >( aElement.getValue() )), + pDestElementTD, + reinterpret_cast< uno_QueryInterfaceFunc >( + cpp_queryInterface), + reinterpret_cast< uno_AcquireFunc >(cpp_acquire), + reinterpret_cast< uno_ReleaseFunc >(cpp_release) )) + { + OSL_ASSERT( false ); + } + } + TYPELIB_DANGER_RELEASE( pDestElementTD ); + TYPELIB_DANGER_RELEASE( pSourceElementTD ); + } + break; + } + // --- to ENUM ------------------------------------------------------------------------------ + case TypeClass_ENUM: + { + TypeDescription aEnumTD( aDestType ); + aEnumTD.makeComplete(); + sal_Int32 nPos = -1; + + if (aSourceClass==TypeClass_STRING) + { + for ( nPos = reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->nEnumValues; nPos--; ) + { + if (o3tl::forceAccess<OUString>(rVal)->equalsIgnoreAsciiCase( + OUString::unacquired(&reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->ppEnumNames[nPos]) )) + break; + } + } + else if (aSourceClass!=TypeClass_ENUM && // exclude some unwanted types for toHyper() + aSourceClass!=TypeClass_BOOLEAN && + aSourceClass!=TypeClass_CHAR) + { + sal_Int32 nEnumValue = static_cast<sal_Int32>(toHyper( rVal, -sal_Int64(0x80000000), 0x7fffffff )); + for ( nPos = reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->nEnumValues; nPos--; ) + { + if (nEnumValue == reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->pEnumValues[nPos]) + break; + } + } + + if (nPos < 0) + { + throw CannotConvertException( + "value cannot be converted to demanded ENUM!", + Reference< XInterface >(), aDestinationClass, FailReason::IS_NOT_ENUM, 0 ); + } + + aRet.setValue( + &reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->pEnumValues[nPos], + aEnumTD.get() ); + + break; + } + + default: + // else simple type conversion possible? + try + { + aRet = convertToSimpleType( rVal, aDestinationClass ); + } + catch (IllegalArgumentException &) + { + // ...FailReason::INVALID is thrown + } + } + + if (aRet.hasValue()) + return aRet; + + throw CannotConvertException( + "conversion not possible!", + Reference< XInterface >(), aDestinationClass, FailReason::INVALID, 0 ); +} + + +Any TypeConverter_Impl::convertToSimpleType( const Any& rVal, TypeClass aDestinationClass ) +{ + switch (aDestinationClass) + { + // only simple Conversion of _simple_ types + case TypeClass_VOID: + case TypeClass_BOOLEAN: + case TypeClass_BYTE: + case TypeClass_SHORT: + case TypeClass_UNSIGNED_SHORT: + case TypeClass_LONG: + case TypeClass_UNSIGNED_LONG: + case TypeClass_HYPER: + case TypeClass_UNSIGNED_HYPER: + case TypeClass_FLOAT: + case TypeClass_DOUBLE: + case TypeClass_CHAR: + case TypeClass_STRING: + case TypeClass_ANY: + break; + + default: + throw IllegalArgumentException( + "destination type is not simple!", + Reference< XInterface >(), sal_Int16(1) ); + } + + const Type& aSourceType = rVal.getValueType(); + TypeClass aSourceClass = aSourceType.getTypeClass(); + if (aDestinationClass == aSourceClass) + return rVal; + + Any aRet; + + // Convert to... + switch (aDestinationClass) + { + // --- to VOID ------------------------------------------------------------------------------ + case TypeClass_VOID: + return Any(); + + // --- to ANY ------------------------------------------------------------------------------- + case TypeClass_ANY: + return rVal; + + // --- to BOOL ------------------------------------------------------------------------------ + case TypeClass_BOOLEAN: + switch (aSourceClass) + { + default: + aRet <<= (toDouble( rVal ) != 0.0); + break; + case TypeClass_ENUM: // exclude enums + break; + + case TypeClass_STRING: + { + const OUString & aStr = *o3tl::forceAccess<OUString>(rVal); + if ( aStr == "0" || aStr.equalsIgnoreAsciiCase( "false" )) + { + aRet <<= false; + } + else if ( aStr == "1" || aStr.equalsIgnoreAsciiCase( "true" )) + { + aRet <<= true; + } + else + { + throw CannotConvertException( + "STRING has no boolean value, " + aStr, + Reference< XInterface >(), aDestinationClass, FailReason::IS_NOT_BOOL, 0 ); + } + } + } + break; + + // --- to CHAR, BYTE ------------------------------------------------------------------------ + case TypeClass_CHAR: + { + if (aSourceClass==TypeClass_STRING) + { + auto const s = o3tl::forceAccess<OUString>(rVal); + if (s->getLength() == 1) // single char + aRet <<= (*s)[0]; + } + else if (aSourceClass!=TypeClass_ENUM && // exclude enums, chars + aSourceClass!=TypeClass_CHAR) + { + aRet <<= sal_Unicode(toHyper( rVal, 0, 0xffff )); // range + } + break; + } + case TypeClass_BYTE: + aRet <<= static_cast<sal_Int8>( toHyper( rVal, -sal_Int64(0x80), 0x7f ) ); + break; + + // --- to SHORT, UNSIGNED SHORT ------------------------------------------------------------- + case TypeClass_SHORT: + aRet <<= static_cast<sal_Int16>( toHyper( rVal, -sal_Int64(0x8000), 0x7fff ) ); + break; + case TypeClass_UNSIGNED_SHORT: + aRet <<= static_cast<sal_uInt16>( toHyper( rVal, 0, 0xffff ) ); + break; + + // --- to LONG, UNSIGNED LONG --------------------------------------------------------------- + case TypeClass_LONG: + aRet <<= static_cast<sal_Int32>( toHyper( rVal, -sal_Int64(0x80000000), 0x7fffffff ) ); + break; + case TypeClass_UNSIGNED_LONG: + aRet <<= static_cast<sal_uInt32>( toHyper( rVal, 0, 0xffffffff ) ); + break; + + // --- to HYPER, UNSIGNED HYPER-------------------------------------------- + case TypeClass_HYPER: + aRet <<= toHyper( rVal, SAL_MIN_INT64, SAL_MAX_INT64 ); + break; + case TypeClass_UNSIGNED_HYPER: + aRet <<= static_cast<sal_uInt64>( toHyper( rVal, 0 ) ); + break; + + // --- to FLOAT, DOUBLE --------------------------------------------------------------------- + case TypeClass_FLOAT: + aRet <<= static_cast<float>( toDouble( rVal, -FLT_MAX, FLT_MAX ) ); + break; + case TypeClass_DOUBLE: + aRet <<= toDouble( rVal, -DBL_MAX, DBL_MAX ); + break; + + // --- to STRING ---------------------------------------------------------------------------- + case TypeClass_STRING: + switch (aSourceClass) + { + case TypeClass_ENUM: + { + TypeDescription aEnumTD( aSourceType ); + aEnumTD.makeComplete(); + sal_Int32 nPos; + sal_Int32 nEnumValue = *static_cast<sal_Int32 const *>(rVal.getValue()); + for ( nPos = reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->nEnumValues; nPos--; ) + { + if (nEnumValue == reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->pEnumValues[nPos]) + break; + } + if (nPos < 0) + { + throw CannotConvertException( + "value is not ENUM!", + Reference< XInterface >(), aDestinationClass, FailReason::IS_NOT_ENUM, 0 ); + } + + aRet <<= OUString::unacquired( + &reinterpret_cast<typelib_EnumTypeDescription *>(aEnumTD.get())->ppEnumNames[nPos]); + + break; + } + + case TypeClass_BOOLEAN: + aRet <<= *o3tl::forceAccess<bool>(rVal) ? + OUString("true") : + OUString("false"); + break; + case TypeClass_CHAR: + aRet <<= OUString(*o3tl::forceAccess<sal_Unicode>(rVal)); + break; + + case TypeClass_BYTE: + aRet <<= OUString::number( *o3tl::forceAccess<sal_Int8>(rVal) ); + break; + case TypeClass_SHORT: + aRet <<= OUString::number( *o3tl::forceAccess<sal_Int16>(rVal) ); + break; + case TypeClass_UNSIGNED_SHORT: + aRet <<= OUString::number( *o3tl::forceAccess<sal_uInt16>(rVal) ); + break; + case TypeClass_LONG: + aRet <<= OUString::number( *o3tl::forceAccess<sal_Int32>(rVal) ); + break; + case TypeClass_UNSIGNED_LONG: + aRet <<= OUString::number( *o3tl::forceAccess<sal_uInt32>(rVal) ); + break; + case TypeClass_HYPER: + aRet <<= OUString::number( *o3tl::forceAccess<sal_Int64>(rVal) ); + break; +// case TypeClass_UNSIGNED_HYPER: +// aRet <<= OUString::valueOf( (sal_Int64)*(sal_uInt64 const *)rVal.getValue() ); +// break; + // handle unsigned hyper like double + + default: + aRet <<= OUString::number( toDouble( rVal ) ); + } + break; + + default: + OSL_ASSERT(false); + break; + } + + if (aRet.hasValue()) + return aRet; + + throw CannotConvertException( + "conversion not possible!", + Reference< XInterface >(), aDestinationClass, FailReason::INVALID, 0 ); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_stoc_TypeConverter_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + return ::cppu::acquire(new stoc_tcv::TypeConverter_Impl()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/ExternalUriReferenceTranslator.cxx b/stoc/source/uriproc/ExternalUriReferenceTranslator.cxx new file mode 100644 index 000000000..b9e2ca4e9 --- /dev/null +++ b/stoc/source/uriproc/ExternalUriReferenceTranslator.cxx @@ -0,0 +1,187 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uri/XExternalUriReferenceTranslator.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <osl/thread.h> +#include <rtl/string.h> +#include <rtl/textenc.h> +#include <rtl/uri.h> +#include <rtl/uri.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace com::sun::star::uno { class XInterface; } +namespace com::sun::star::uno { class XComponentContext; } + +namespace { + +class Translator: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XExternalUriReferenceTranslator> +{ +public: + Translator() {} + + Translator(const Translator&) = delete; + Translator& operator=(const Translator&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual OUString SAL_CALL + translateToInternal(OUString const & externalUriReference) override; + + virtual OUString SAL_CALL + translateToExternal(OUString const & internalUriReference) override; + +private: + virtual ~Translator() override {} +}; + +OUString Translator::getImplementationName() +{ + return "com.sun.star.comp.uri.ExternalUriReferenceTranslator"; +} + +sal_Bool Translator::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Translator::getSupportedServiceNames() +{ + css::uno::Sequence< OUString > s { "com.sun.star.uri.ExternalUriReferenceTranslator" }; + return s; +} + +OUString Translator::translateToInternal( + OUString const & externalUriReference) +{ + if (!externalUriReference.matchIgnoreAsciiCase("file:/")) + { + return externalUriReference; + } + sal_Int32 i = RTL_CONSTASCII_LENGTH("file:"); + OUStringBuffer buf(128); + buf.append(externalUriReference.subView(0, i)); + // Some environments (e.g., Java) produce illegal file URLs without an + // authority part; treat them as having an empty authority part: + if (!externalUriReference.match("//", i)) + { + buf.append("//"); + } + rtl_TextEncoding encoding = osl_getThreadTextEncoding(); + for (bool path = true;;) { + sal_Int32 j = i; + while (j != externalUriReference.getLength() + && externalUriReference[j] != '#' + && (!path || externalUriReference[j] != '/')) + { + ++j; + } + if (j != i) { + OUString seg( + rtl::Uri::encode( + rtl::Uri::decode( + externalUriReference.copy(i, j - i), + rtl_UriDecodeStrict, encoding), + rtl_UriCharClassPchar, rtl_UriEncodeStrict, + RTL_TEXTENCODING_UTF8)); + if (seg.isEmpty()) { + return OUString(); + } + buf.append(seg); + } + if (j == externalUriReference.getLength()) { + break; + } + buf.append(externalUriReference[j]); + path = externalUriReference[j] == '/'; + i = j + 1; + } + return buf.makeStringAndClear(); +} + +OUString Translator::translateToExternal( + OUString const & internalUriReference) +{ + if (!internalUriReference.matchIgnoreAsciiCase("file://")) + { + return internalUriReference; + } + sal_Int32 i = RTL_CONSTASCII_LENGTH("file://"); + OUStringBuffer buf(128); + buf.append(internalUriReference.subView(0, i)); + rtl_TextEncoding encoding = osl_getThreadTextEncoding(); + for (bool path = true;;) { + sal_Int32 j = i; + while (j != internalUriReference.getLength() + && internalUriReference[j] != '#' + && (!path || internalUriReference[j] != '/')) + { + ++j; + } + if (j != i) { + // Use rtl_UriDecodeToIuri -> rtl_UriEncodeStrictKeepEscapes instead + // of rtl_UriDecodeStrict -> rtl_UriEncodeStrict, so that spurious + // non--UTF-8 octets like "%FE" are copied verbatim: + OUString seg( + rtl::Uri::encode( + rtl::Uri::decode( + internalUriReference.copy(i, j - i), + rtl_UriDecodeToIuri, RTL_TEXTENCODING_UTF8), + rtl_UriCharClassPchar, rtl_UriEncodeStrictKeepEscapes, + encoding)); + if (seg.isEmpty()) { + return OUString(); + } + buf.append(seg); + } + if (j == internalUriReference.getLength()) { + break; + } + buf.append(internalUriReference[j]); + path = internalUriReference[j] == '/'; + i = j + 1; + } + return buf.makeStringAndClear(); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_ExternalUriReferenceTranslator_get_implementation(css::uno::XComponentContext* , + css::uno::Sequence<css::uno::Any> const &) +{ + return ::cppu::acquire(new Translator); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriReference.cxx b/stoc/source/uriproc/UriReference.cxx new file mode 100644 index 000000000..e8f7db0bf --- /dev/null +++ b/stoc/source/uriproc/UriReference.cxx @@ -0,0 +1,183 @@ +/* -*- 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 <sal/config.h> + +#include <cassert> + +#include "UriReference.hxx" + +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +using stoc::uriproc::UriReference; + +UriReference::UriReference( + OUString const & scheme, bool bHasAuthority, + OUString const & authority, OUString const & path, + bool bHasQuery, OUString const & query): + m_path(path), + m_scheme(scheme), + m_authority(authority), + m_query(query), + m_hasAuthority(bHasAuthority), + m_hasQuery(bHasQuery), + m_hasFragment(false) +{ + assert(authority.isEmpty() || bHasAuthority); + assert(query.isEmpty() || bHasQuery); +} + +UriReference::~UriReference() {} + +OUString UriReference::getUriReference() +{ + std::lock_guard g(m_mutex); + OUStringBuffer buf(128); + if (!m_scheme.isEmpty()) { + buf.append(m_scheme); + buf.append(':'); + } + appendSchemeSpecificPart(buf); + if (m_hasFragment) { + buf.append('#'); + buf.append(m_fragment); + } + return buf.makeStringAndClear(); +} + +bool UriReference::isAbsolute() const { + return !m_scheme.isEmpty(); +} + + +OUString UriReference::getSchemeSpecificPart() +{ + std::lock_guard g(m_mutex); + OUStringBuffer buf; + appendSchemeSpecificPart(buf); + return buf.makeStringAndClear(); +} + +bool UriReference::isHierarchical() { + std::lock_guard g(m_mutex); + return m_scheme.isEmpty() || m_hasAuthority || m_path.startsWith("/"); +} + +bool UriReference::hasAuthority() const { + return m_hasAuthority; +} + +const OUString& UriReference::getAuthority() const { + return m_authority; +} + +OUString UriReference::getPath() { + std::lock_guard g(m_mutex); + return m_path; +} + +bool UriReference::hasRelativePath() { + std::lock_guard g(m_mutex); + return !m_hasAuthority + && (m_path.isEmpty() || m_path[0] != '/'); +} + +sal_Int32 UriReference::getPathSegmentCount() +{ + std::lock_guard g(m_mutex); + if (m_path.isEmpty()) { + return 0; + } else { + sal_Int32 n = m_path[0] == '/' ? 0 : 1; + for (sal_Int32 i = 0;; ++i) { + i = m_path.indexOf('/', i); + if (i < 0) { + break; + } + ++n; + } + return n; + } +} + +OUString UriReference::getPathSegment(sal_Int32 index) +{ + std::lock_guard g(m_mutex); + if (!m_path.isEmpty() && index >= 0) { + for (sal_Int32 i = m_path[0] == '/' ? 1 : 0;; ++i) { + if (index-- == 0) { + sal_Int32 j = m_path.indexOf('/', i); + return j < 0 ? m_path.copy(i) : m_path.copy(i, j - i); + } + i = m_path.indexOf('/', i); + if (i < 0) { + break; + } + } + } + return OUString(); +} + +bool UriReference::hasQuery() const { + return m_hasQuery; +} + +const OUString& UriReference::getQuery() const { + return m_query; +} + +bool UriReference::hasFragment() { + std::lock_guard g(m_mutex); + return m_hasFragment; +} + +OUString UriReference::getFragment() { + std::lock_guard g(m_mutex); + return m_fragment; +} + +void UriReference::setFragment(OUString const & fragment) +{ + std::lock_guard g(m_mutex); + m_hasFragment = true; + m_fragment = fragment; +} + +void UriReference::clearFragment() { + std::lock_guard g(m_mutex); + m_hasFragment = false; + m_fragment.clear(); +} + +void UriReference::appendSchemeSpecificPart(OUStringBuffer & buffer) const +{ + if (m_hasAuthority) { + buffer.append("//"); + buffer.append(m_authority); + } + buffer.append(m_path); + if (m_hasQuery) { + buffer.append('?'); + buffer.append(m_query); + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriReference.hxx b/stoc/source/uriproc/UriReference.hxx new file mode 100644 index 000000000..e16453720 --- /dev/null +++ b/stoc/source/uriproc/UriReference.hxx @@ -0,0 +1,112 @@ +/* -*- 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 . + */ + +#ifndef INCLUDED_STOC_SOURCE_URIPROC_URIREFERENCE_HXX +#define INCLUDED_STOC_SOURCE_URIPROC_URIREFERENCE_HXX + +#include <mutex> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <rtl/ustrbuf.hxx> + +namespace stoc::uriproc { + +class UriReference { +public: + UriReference( + OUString const & scheme, bool hasAuthority, + OUString const & authority, OUString const & path, + bool hasQuery, OUString const & query); + + ~UriReference(); + + /// @throws css::uno::RuntimeException + OUString getUriReference(); + + /// @throws css::uno::RuntimeException + bool isAbsolute() const; + + /// @throws css::uno::RuntimeException + const OUString& getScheme() const { return m_scheme;} + + /// @throws css::uno::RuntimeException + OUString getSchemeSpecificPart(); + + /// @throws css::uno::RuntimeException + bool isHierarchical(); + + /// @throws css::uno::RuntimeException + bool hasAuthority() const; + + /// @throws css::uno::RuntimeException + const OUString& getAuthority() const; + + /// @throws css::uno::RuntimeException + OUString getPath(); + + /// @throws css::uno::RuntimeException + bool hasRelativePath(); + + /// @throws css::uno::RuntimeException + sal_Int32 getPathSegmentCount(); + + /// @throws css::uno::RuntimeException + OUString getPathSegment(sal_Int32 index); + + /// @throws css::uno::RuntimeException + bool hasQuery() const; + + /// @throws css::uno::RuntimeException + const OUString& getQuery() const; + + /// @throws css::uno::RuntimeException + bool hasFragment(); + + /// @throws css::uno::RuntimeException + OUString getFragment(); + + /// @throws css::uno::RuntimeException + void setFragment(OUString const & fragment); + + /// @throws css::uno::RuntimeException + void clearFragment(); + + std::mutex m_mutex; + OUString m_path; + +private: + UriReference(UriReference const &) = delete; + void operator =(UriReference const &) = delete; + + void appendSchemeSpecificPart(OUStringBuffer & buffer) const; + + OUString m_scheme; + OUString m_authority; + OUString m_query; + OUString m_fragment; + bool m_hasAuthority; + bool m_hasQuery; + bool m_hasFragment; +}; + +} + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriReferenceFactory.cxx b/stoc/source/uriproc/UriReferenceFactory.cxx new file mode 100644 index 000000000..254097c17 --- /dev/null +++ b/stoc/source/uriproc/UriReferenceFactory.cxx @@ -0,0 +1,719 @@ +/* -*- 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 <sal/config.h> + +#include <algorithm> +#include <cassert> +#include <cstddef> +#include <string_view> +#include <utility> +#include <vector> + +#include <com/sun/star/lang/WrappedTargetRuntimeException.hpp> +#include <com/sun/star/lang/XMultiComponentFactory.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Any.hxx> +#include <com/sun/star/uno/Exception.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uno/XComponentContext.hpp> +#include <com/sun/star/uno/XInterface.hpp> +#include <com/sun/star/uri/RelativeUriExcessParentSegments.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XUriReferenceFactory.hpp> +#include <com/sun/star/uri/XUriSchemeParser.hpp> +#include <cppuhelper/exc_hlp.hxx> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/character.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +#include "UriReference.hxx" + +namespace { + +bool equalIgnoreEscapeCase(std::u16string_view s1, std::u16string_view s2) { + if (s1.size() == s2.size()) { + for (size_t i = 0; i < s1.size();) { + if (s1[i] == '%' && s2[i] == '%' && s1.size() - i > 2 + && rtl::isAsciiHexDigit(s1[i + 1]) + && rtl::isAsciiHexDigit(s1[i + 2]) + && rtl::isAsciiHexDigit(s2[i + 1]) + && rtl::isAsciiHexDigit(s2[i + 2]) + && rtl::compareIgnoreAsciiCase(s1[i + 1], s2[i + 1]) == 0 + && rtl::compareIgnoreAsciiCase(s1[i + 2], s2[i + 2]) == 0) + { + i += 3; + } else if (s1[i] != s2[i]) { + return false; + } else { + ++i; + } + } + return true; + } else { + return false; + } +} + +sal_Int32 parseScheme(std::u16string_view uriReference) { + if (uriReference.size() >= 2 && rtl::isAsciiAlpha(uriReference[0])) { + for (size_t i = 0; i < uriReference.size(); ++i) { + sal_Unicode c = uriReference[i]; + if (c == ':') { + return i; + } else if (!rtl::isAsciiAlpha(c) && !rtl::isAsciiDigit(c) + && c != '+' && c != '-' && c != '.') + { + break; + } + } + } + return -1; +} + +class UriReference: + public cppu::WeakImplHelper<css::uri::XUriReference> +{ +public: + UriReference( + OUString const & scheme, bool bHasAuthority, + OUString const & authority, OUString const & path, + bool bHasQuery, OUString const & query): + m_base( + scheme, bHasAuthority, authority, path, bHasQuery, + query) + {} + + UriReference(const UriReference&) = delete; + UriReference& operator=(const UriReference&) = delete; + + virtual OUString SAL_CALL getUriReference() override + { return m_base.getUriReference(); } + + virtual sal_Bool SAL_CALL isAbsolute() override + { return m_base.isAbsolute(); } + + virtual OUString SAL_CALL getScheme() override + { return m_base.getScheme(); } + + virtual OUString SAL_CALL getSchemeSpecificPart() override + { return m_base.getSchemeSpecificPart(); } + + virtual sal_Bool SAL_CALL isHierarchical() override + { return m_base.isHierarchical(); } + + virtual sal_Bool SAL_CALL hasAuthority() override + { return m_base.hasAuthority(); } + + virtual OUString SAL_CALL getAuthority() override + { return m_base.getAuthority(); } + + virtual OUString SAL_CALL getPath() override + { return m_base.getPath(); } + + virtual sal_Bool SAL_CALL hasRelativePath() override + { return m_base.hasRelativePath(); } + + virtual sal_Int32 SAL_CALL getPathSegmentCount() override + { return m_base.getPathSegmentCount(); } + + virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override + { return m_base.getPathSegment(index); } + + virtual sal_Bool SAL_CALL hasQuery() override + { return m_base.hasQuery(); } + + virtual OUString SAL_CALL getQuery() override + { return m_base.getQuery(); } + + virtual sal_Bool SAL_CALL hasFragment() override + { return m_base.hasFragment(); } + + virtual OUString SAL_CALL getFragment() override + { return m_base.getFragment(); } + + virtual void SAL_CALL setFragment(OUString const & fragment) override + { m_base.setFragment(fragment); } + + virtual void SAL_CALL clearFragment() override + { m_base.clearFragment(); } + +private: + virtual ~UriReference() override {} + + stoc::uriproc::UriReference m_base; +}; + +css::uno::Reference< css::uri::XUriReference > parseGeneric( + OUString const & scheme, OUString const & schemeSpecificPart) +{ + sal_Int32 len = schemeSpecificPart.getLength(); + sal_Int32 i = 0; + bool hasAuthority = false; + OUString authority; + if (len - i >= 2 && schemeSpecificPart[i] == '/' + && schemeSpecificPart[i + 1] == '/') + { + i += 2; + sal_Int32 n = i; + while (i < len && schemeSpecificPart[i] != '/' + && schemeSpecificPart[i] != '?') { + ++i; + } + hasAuthority = true; + authority = schemeSpecificPart.copy(n, i - n); + } + sal_Int32 n = i; + i = schemeSpecificPart.indexOf('?', i); + if (i == -1) { + i = len; + } + OUString path = schemeSpecificPart.copy(n, i - n); + bool hasQuery = false; + OUString query; + if (i != len) { + hasQuery = true; + query = schemeSpecificPart.copy(i + 1); + } + return new UriReference( + scheme, hasAuthority, authority, path, hasQuery, query); +} + +struct Segment { + bool leadingSlash; + bool excessParent; + std::u16string_view segment; + + Segment(bool theLeadingSlash, bool theExcessParent, std::u16string_view theSegment): + leadingSlash(theLeadingSlash), excessParent(theExcessParent), segment(theSegment) {} +}; + +std::pair<std::vector<Segment>, bool> processSegments( + std::u16string_view first, std::u16string_view second, bool processSpecialSegments) +{ + std::vector<Segment> segments; + bool processed = false; + std::u16string_view const * half = &first; + // later checks for `half == &first` and `half == &second` rely on the fact that `first` and + // `second` are passed by value, in case a caller passes the same object for both arguments + std::size_t index = 0; + bool slash = false; + if (index == half->length()) { + half = &second; + index = 0; + } + if (index != half->length()) { + if ((*half)[index] == u'/') { + slash = true; + ++index; + } + for (;;) { + if (index == half->length() && half == &first) { + half = &second; + index = 0; + } + if (index == half->length()) { + if (slash) { + segments.emplace_back(true, false, std::u16string_view()); + } + break; + } + auto const n = std::min(half->find(u'/', index), half->length()); + auto const leadingSlash = slash; + auto const segment = half->substr(index, n - index); + auto const process = processSpecialSegments || half == &second; + index = n; + slash = false; + if (index == half->length() && half == &first) { + half = &second; + index = 0; + } + if (index != half->length() && (*half)[index] == u'/') { + slash = true; + ++index; + } + if (process) { + if (segment == u".") { + slash = leadingSlash; + processed = true; + continue; + } else if (segment == u"..") { + if (segments.empty() || segments.back().excessParent) { + segments.emplace_back(leadingSlash, true, segment); + } else { + if (leadingSlash) { + segments.pop_back(); + } + slash = leadingSlash; + } + processed = true; + continue; + } + } + segments.emplace_back(leadingSlash, false, segment); + } + } + return {segments, processed}; +} + +class Factory: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XUriReferenceFactory> +{ +public: + explicit Factory( + css::uno::Reference< css::uno::XComponentContext > const & context): + m_context(context) {} + + Factory(const Factory&) = delete; + Factory& operator=(const Factory&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + parse(OUString const & uriReference) override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + makeAbsolute( + css::uno::Reference< css::uri::XUriReference > const & baseUriReference, + css::uno::Reference< css::uri::XUriReference > const & uriReference, + sal_Bool processAdditionalSpecialSegments, + css::uri::RelativeUriExcessParentSegments excessParentSegments) override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + makeRelative( + css::uno::Reference< css::uri::XUriReference > const & baseUriReference, + css::uno::Reference< css::uri::XUriReference > const & uriReference, + sal_Bool preferAuthorityOverRelativePath, + sal_Bool preferAbsoluteOverRelativePath, + sal_Bool encodeRetainedSpecialSegments) override; + +private: + virtual ~Factory() override {} + + css::uno::Reference< css::uri::XUriReference > clone( + css::uno::Reference< css::uri::XUriReference > const & uriReference) + { return parse(uriReference->getUriReference()); } + + css::uno::Reference< css::uno::XComponentContext > m_context; +}; + +OUString Factory::getImplementationName() +{ + return "com.sun.star.comp.uri.UriReferenceFactory"; +} + +sal_Bool Factory::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Factory::getSupportedServiceNames() +{ + css::uno::Sequence< OUString > s { "com.sun.star.uri.UriReferenceFactory" }; + return s; +} + +css::uno::Reference< css::uri::XUriReference > Factory::parse( + OUString const & uriReference) +{ + sal_Int32 fragment = uriReference.indexOf('#'); + if (fragment == -1) { + fragment = uriReference.getLength(); + } + OUString scheme; + OUString schemeSpecificPart; + OUString serviceName; + sal_Int32 n = parseScheme(uriReference); + assert(n < fragment); + if (n >= 0) { + scheme = uriReference.copy(0, n); + schemeSpecificPart = uriReference.copy(n + 1, fragment - (n + 1)); + OUStringBuffer buf(128); + buf.append("com.sun.star.uri.UriSchemeParser_"); + for (sal_Int32 i = 0; i < scheme.getLength(); ++i) { + sal_Unicode c = scheme[i]; + if (rtl::isAsciiUpperCase(c)) { + buf.append(static_cast<sal_Unicode>(rtl::toAsciiLowerCase(c))); + } else if (c == '+') { + buf.append("PLUS"); + } else if (c == '-') { + buf.append("HYPHEN"); + } else if (c == '.') { + buf.append("DOT"); + } else { + assert(rtl::isAsciiLowerCase(c) || rtl::isAsciiDigit(c)); + buf.append(c); + } + } + serviceName = buf.makeStringAndClear(); + } else { + schemeSpecificPart = uriReference.copy(0, fragment); + } + css::uno::Reference< css::uri::XUriSchemeParser > parser; + if (!serviceName.isEmpty()) { + css::uno::Reference< css::lang::XMultiComponentFactory > factory( + m_context->getServiceManager()); + if (factory.is()) { + css::uno::Reference< css::uno::XInterface > service; + try { + service = factory->createInstanceWithContext( + serviceName, m_context); + } catch (css::uno::RuntimeException &) { + throw; + } catch (const css::uno::Exception &) { + css::uno::Any anyEx = cppu::getCaughtException(); + throw css::lang::WrappedTargetRuntimeException( + "creating service " + serviceName, + static_cast< cppu::OWeakObject * >(this), + anyEx); + } + if (service.is()) { + parser.set( service, css::uno::UNO_QUERY_THROW); + } + } + } + css::uno::Reference< css::uri::XUriReference > uriRef( + parser.is() + ? parser->parse(scheme, schemeSpecificPart) + : parseGeneric(scheme, schemeSpecificPart)); + if (uriRef.is() && fragment != uriReference.getLength()) { + uriRef->setFragment(uriReference.copy(fragment + 1)); + } + return uriRef; +} + +css::uno::Reference< css::uri::XUriReference > Factory::makeAbsolute( + css::uno::Reference< css::uri::XUriReference > const & baseUriReference, + css::uno::Reference< css::uri::XUriReference > const & uriReference, + sal_Bool processAdditionalSpecialSegments, + css::uri::RelativeUriExcessParentSegments excessParentSegments) +{ + if (!baseUriReference.is() || !baseUriReference->isAbsolute() + || !uriReference.is()) { + return nullptr; + } else if (uriReference->isAbsolute()) { + if (processAdditionalSpecialSegments) { + auto const path = uriReference->getPath(); + auto [segments, proc] = processSegments(path, {}, true); + if (proc) { + OUStringBuffer abs(uriReference->getScheme()); + abs.append(':'); + if (uriReference->hasAuthority()) { + abs.append("//"); + abs.append(uriReference->getAuthority()); + } + for (auto const & i : segments) + { + if (i.excessParent) { + switch (excessParentSegments) { + case css::uri::RelativeUriExcessParentSegments_ERROR: + return nullptr; + + case css::uri::RelativeUriExcessParentSegments_RETAIN: + assert(i.segment == u".."); + break; + + case css::uri::RelativeUriExcessParentSegments_REMOVE: + continue; + + default: + assert(false); + break; + } + } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); + } + if (uriReference->hasQuery()) { + abs.append('?'); + abs.append(uriReference->getQuery()); + } + if (uriReference->hasFragment()) { + abs.append('#'); + abs.append(uriReference->getFragment()); + } + return parse(abs.makeStringAndClear()); + } + } + return clone(uriReference); + } else if (!uriReference->hasAuthority() + && uriReference->getPath().isEmpty()) { + OUStringBuffer abs(baseUriReference->getScheme()); + abs.append(':'); + if (baseUriReference->hasAuthority()) { + abs.append("//"); + abs.append(baseUriReference->getAuthority()); + } + abs.append(baseUriReference->getPath()); + if (uriReference->hasQuery()) { + abs.append('?'); + abs.append(uriReference->getQuery()); + } else if (baseUriReference->hasQuery()) { + abs.append('?'); + abs.append(baseUriReference->getQuery()); + } + if (uriReference->hasFragment()) { + abs.append('#'); + abs.append(uriReference->getFragment()); + } + return parse(abs.makeStringAndClear()); + } else { + OUStringBuffer abs(128); + abs.append(baseUriReference->getScheme()); + abs.append(':'); + if (uriReference->hasAuthority()) { + abs.append("//"); + abs.append(uriReference->getAuthority()); + } else if (baseUriReference->hasAuthority()) { + abs.append("//"); + abs.append(baseUriReference->getAuthority()); + } + if (uriReference->hasRelativePath()) { + auto path1 = baseUriReference->getPath(); + if (path1.isEmpty()) { + if (baseUriReference->hasAuthority()) { + path1 = "/"; + } + } else { + path1 = path1.copy(0, path1.lastIndexOf('/') + 1); + } + auto const path2 = uriReference->getPath(); + auto [segments, _] = processSegments(path1, path2, processAdditionalSpecialSegments); + (void)_; + for (auto const & i : segments) + { + if (i.excessParent) { + switch (excessParentSegments) { + case css::uri::RelativeUriExcessParentSegments_ERROR: + return nullptr; + + case css::uri::RelativeUriExcessParentSegments_RETAIN: + assert(i.segment == u".."); + break; + + case css::uri::RelativeUriExcessParentSegments_REMOVE: + continue; + + default: + assert(false); + break; + } + } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); + } + } else { + bool processed = false; + if (processAdditionalSpecialSegments) { + auto const path = uriReference->getPath(); + auto [segments, proc] = processSegments(path, {}, true); + if (proc) { + for (auto const & i : segments) + { + if (i.excessParent) { + switch (excessParentSegments) { + case css::uri::RelativeUriExcessParentSegments_ERROR: + return nullptr; + + case css::uri::RelativeUriExcessParentSegments_RETAIN: + assert(i.segment == u".."); + break; + + case css::uri::RelativeUriExcessParentSegments_REMOVE: + continue; + + default: + assert(false); + break; + } + } + if (i.leadingSlash) { + abs.append('/'); + } + abs.append(i.segment); + } + processed = true; + } + } + if (!processed) { + abs.append(uriReference->getPath()); + } + } + if (uriReference->hasQuery()) { + abs.append('?'); + abs.append(uriReference->getQuery()); + } + if (uriReference->hasFragment()) { + abs.append('#'); + abs.append(uriReference->getFragment()); + } + return parse(abs.makeStringAndClear()); + } +} + +css::uno::Reference< css::uri::XUriReference > Factory::makeRelative( + css::uno::Reference< css::uri::XUriReference > const & baseUriReference, + css::uno::Reference< css::uri::XUriReference > const & uriReference, + sal_Bool preferAuthorityOverRelativePath, + sal_Bool preferAbsoluteOverRelativePath, + sal_Bool encodeRetainedSpecialSegments) +{ + if (!baseUriReference.is() || !baseUriReference->isAbsolute() + || !uriReference.is()) { + return nullptr; + } else if (!uriReference->isAbsolute() || uriReference->hasRelativePath() + || !baseUriReference->getScheme().equalsIgnoreAsciiCase( + uriReference->getScheme())) { + return clone(uriReference); + } else { + OUStringBuffer rel(128); + bool omitQuery = false; + if ((baseUriReference->hasAuthority() != uriReference->hasAuthority()) + || !equalIgnoreEscapeCase( + baseUriReference->getAuthority(), + uriReference->getAuthority())) + { + if (uriReference->hasAuthority()) { + rel.append("//"); + rel.append(uriReference->getAuthority()); + } + rel.append(uriReference->getPath()); + } else if ((equalIgnoreEscapeCase( + baseUriReference->getPath(), uriReference->getPath()) + || (baseUriReference->getPath() == "/" + && uriReference->getPath().isEmpty())) + && baseUriReference->hasQuery() == uriReference->hasQuery() + && equalIgnoreEscapeCase( + baseUriReference->getQuery(), uriReference->getQuery())) + { + omitQuery = true; + } else { + sal_Int32 count1 = std::max< sal_Int32 >( + baseUriReference->getPathSegmentCount(), 1); + sal_Int32 count2 = std::max< sal_Int32 >( + uriReference->getPathSegmentCount(), 1); + sal_Int32 i = 0; + for (; i < std::min(count1, count2) - 1; ++i) { + if (!equalIgnoreEscapeCase( + baseUriReference->getPathSegment(i), + uriReference->getPathSegment(i))) + { + break; + } + } + if (i == 0 + && (preferAbsoluteOverRelativePath || uriReference->hasQuery()) + && (preferAuthorityOverRelativePath + || !uriReference->getPath().startsWith("//"))) + { + if (uriReference->getPath().isEmpty()) { + if (!baseUriReference->getPath().isEmpty() + && baseUriReference->getPath() != "/") + { + rel.append('/'); + } + } else if (uriReference->getPath() == "/") { + if (baseUriReference->getPath().isEmpty() + || baseUriReference->getPath() != "/") + { + rel.append('/'); + } + } else { + if (uriReference->getPath().startsWith("//")) { + assert(uriReference->hasAuthority()); + rel.append("//"); + rel.append(uriReference->getAuthority()); + } + rel.append(uriReference->getPath()); + } + } else { + bool segments = false; + for (sal_Int32 j = i; j < count1 - 1; ++j) { + if (segments) { + rel.append('/'); + } + rel.append(".."); + segments = true; + } + if (i < count2 - 1 + || (!uriReference->getPathSegment(count2 - 1).isEmpty())) + { + if (!segments + && (uriReference->getPathSegment(i).isEmpty() + || (parseScheme(uriReference->getPathSegment(i)) + >= 0))) + { + rel.append('.'); + segments = true; + } + for (; i < count2; ++i) { + if (segments) { + rel.append('/'); + } + OUString s(uriReference->getPathSegment(i)); + if (encodeRetainedSpecialSegments && s == ".") { + rel.append("%2E"); + } else if (encodeRetainedSpecialSegments && s == "..") { + rel.append("%2E%2E"); + } else { + rel.append(s); + } + segments = true; + } + } + } + } + if (!omitQuery && uriReference->hasQuery()) { + rel.append('?'); + rel.append(uriReference->getQuery()); + } + if (uriReference->hasFragment()) { + rel.append('#'); + rel.append(uriReference->getFragment()); + } + return parse(rel.makeStringAndClear()); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_UriReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext, + css::uno::Sequence<css::uno::Any> const &) +{ + return ::cppu::acquire(new Factory(rxContext)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTexpand.cxx b/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTexpand.cxx new file mode 100644 index 000000000..dde6fcb06 --- /dev/null +++ b/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTexpand.cxx @@ -0,0 +1,198 @@ +/* -*- 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 <sal/config.h> + +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uri/XUriSchemeParser.hpp> +#include <com/sun/star/uri/XVndSunStarExpandUrlReference.hpp> +#include <com/sun/star/util/XMacroExpander.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/textenc.h> +#include <rtl/uri.h> +#include <rtl/uri.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +#include "UriReference.hxx" + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::uno { class XInterface; } +namespace com::sun::star::uri { class XUriReference; } + +namespace { + +bool parseSchemeSpecificPart(OUString const & part) { + // Liberally accepts both an empty opaque_part and an opaque_part that + // starts with a non-escaped "/": + return part.isEmpty() + || (!::rtl::Uri::decode(part, ::rtl_UriDecodeStrict, RTL_TEXTENCODING_UTF8).isEmpty()); +} + +class UrlReference: + public ::cppu::WeakImplHelper<css::uri::XVndSunStarExpandUrlReference> +{ +public: + UrlReference(OUString const & scheme, OUString const & path): + base_( + scheme, false, OUString(), path, false, + OUString()) + {} + + UrlReference(const UrlReference&) = delete; + UrlReference& operator=(const UrlReference&) = delete; + + virtual OUString SAL_CALL getUriReference() override + { return base_.getUriReference(); } + + virtual sal_Bool SAL_CALL isAbsolute() override + { return base_.isAbsolute(); } + + virtual OUString SAL_CALL getScheme() override + { return base_.getScheme(); } + + virtual OUString SAL_CALL getSchemeSpecificPart() override + { return base_.getSchemeSpecificPart(); } + + virtual sal_Bool SAL_CALL isHierarchical() override + { return base_.isHierarchical(); } + + virtual sal_Bool SAL_CALL hasAuthority() override + { return base_.hasAuthority(); } + + virtual OUString SAL_CALL getAuthority() override + { return base_.getAuthority(); } + + virtual OUString SAL_CALL getPath() override + { return base_.getPath(); } + + virtual sal_Bool SAL_CALL hasRelativePath() override + { return base_.hasRelativePath(); } + + virtual ::sal_Int32 SAL_CALL getPathSegmentCount() override + { return base_.getPathSegmentCount(); } + + virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override + { return base_.getPathSegment(index); } + + virtual sal_Bool SAL_CALL hasQuery() override + { return base_.hasQuery(); } + + virtual OUString SAL_CALL getQuery() override + { return base_.getQuery(); } + + virtual sal_Bool SAL_CALL hasFragment() override + { return base_.hasFragment(); } + + virtual OUString SAL_CALL getFragment() override + { return base_.getFragment(); } + + virtual void SAL_CALL setFragment(OUString const & fragment) override + { base_.setFragment(fragment); } + + virtual void SAL_CALL clearFragment() override + { base_.clearFragment(); } + + virtual OUString SAL_CALL expand( + css::uno::Reference< css::util::XMacroExpander > const & expander) override; + +private: + virtual ~UrlReference() override {} + + stoc::uriproc::UriReference base_; +}; + +OUString UrlReference::expand( + css::uno::Reference< css::util::XMacroExpander > const & expander) +{ + if (!expander.is()) { + throw css::uno::RuntimeException("null expander passed to XVndSunStarExpandUrl.expand"); + } + return expander->expandMacros( + ::rtl::Uri::decode( + getPath(), ::rtl_UriDecodeWithCharset, RTL_TEXTENCODING_UTF8)); +} + +class Parser: + public ::cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XUriSchemeParser> +{ +public: + Parser() {} + + Parser(const Parser&) = delete; + Parser& operator=(const Parser&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService( + OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + parse( + OUString const & scheme, + OUString const & schemeSpecificPart) override; + +private: + virtual ~Parser() override {} +}; + +OUString Parser::getImplementationName() +{ + return "com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTexpand"; +} + +sal_Bool Parser::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Parser::getSupportedServiceNames() +{ + return { "com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTexpand" }; +} + +css::uno::Reference< css::uri::XUriReference > Parser::parse( + OUString const & scheme, OUString const & schemeSpecificPart) +{ + if (!parseSchemeSpecificPart(schemeSpecificPart)) { + return css::uno::Reference< css::uri::XUriReference >(); + } + return new UrlReference(scheme, schemeSpecificPart); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_UriSchemeParser_vndDOTsunDOTstarDOTexpand_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + //TODO: single instance + return ::cppu::acquire(new Parser()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript.cxx b/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript.cxx new file mode 100644 index 000000000..a8203a811 --- /dev/null +++ b/stoc/source/uriproc/UriSchemeParser_vndDOTsunDOTstarDOTscript.cxx @@ -0,0 +1,386 @@ +/* -*- 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 "UriReference.hxx" + +#include <com/sun/star/lang/IllegalArgumentException.hpp> +#include <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uri/XUriSchemeParser.hpp> +#include <com/sun/star/uri/XVndSunStarScriptUrlReference.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/character.hxx> +#include <rtl/uri.hxx> +#include <rtl/ustrbuf.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> +#include <o3tl/safeint.hxx> + +#include <string_view> + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::uno { class XInterface; } +namespace com::sun::star::uri { class XUriReference; } + +namespace { + +int getHexWeight(sal_Unicode c) { + return c >= '0' && c <= '9' ? static_cast< int >(c - '0') + : c >= 'A' && c <= 'F' ? static_cast< int >(c - 'A' + 10) + : c >= 'a' && c <= 'f' ? static_cast< int >(c - 'a' + 10) + : -1; +} + +int parseEscaped(std::u16string_view part, sal_Int32 * index) { + if (part.size() - *index < 3 || part[*index] != '%') { + return -1; + } + int n1 = getHexWeight(part[*index + 1]); + int n2 = getHexWeight(part[*index + 2]); + if (n1 < 0 || n2 < 0) { + return -1; + } + *index += 3; + return (n1 << 4) | n2; +} + +OUString parsePart( + std::u16string_view part, bool namePart, sal_Int32 * index) +{ + OUStringBuffer buf(64); + while (o3tl::make_unsigned(*index) < part.size()) { + sal_Unicode c = part[*index]; + if (namePart ? c == '?' : c == '&' || c == '=') { + break; + } else if (c == '%') { + sal_Int32 i = *index; + int n = parseEscaped(part, &i); + if (n >= 0 && n <= 0x7F) { + buf.append(static_cast< sal_Unicode >(n)); + } else if (n >= 0xC0 && n <= 0xFC) { + sal_Int32 encoded; + int shift; + sal_Int32 min; + if (n <= 0xDF) { + encoded = (n & 0x1F) << 6; + shift = 0; + min = 0x80; + } else if (n <= 0xEF) { + encoded = (n & 0x0F) << 12; + shift = 6; + min = 0x800; + } else if (n <= 0xF7) { + encoded = (n & 0x07) << 18; + shift = 12; + min = 0x10000; + } else if (n <= 0xFB) { + encoded = (n & 0x03) << 24; + shift = 18; + min = 0x200000; + } else { + encoded = 0; + shift = 24; + min = 0x4000000; + } + bool utf8 = true; + for (; shift >= 0; shift -= 6) { + n = parseEscaped(part, &i); + if (n < 0x80 || n > 0xBF) { + utf8 = false; + break; + } + encoded |= (n & 0x3F) << shift; + } + if (!utf8 || !rtl::isUnicodeScalarValue(encoded) + || encoded < min) + { + break; + } + buf.appendUtf32(encoded); + } else { + break; + } + *index = i; + } else { + buf.append(c); + ++*index; + } + } + return buf.makeStringAndClear(); +} + +OUString encodeNameOrParamFragment(OUString const & fragment) { + static constexpr auto nameOrParamFragment = rtl::createUriCharClass( + u8"!$'()*+,-.0123456789:;@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]_abcdefghijklmnopqrstuvwxyz~"); + return rtl::Uri::encode( + fragment, nameOrParamFragment.data(), rtl_UriEncodeIgnoreEscapes, + RTL_TEXTENCODING_UTF8); +} + +bool parseSchemeSpecificPart(std::u16string_view part) { + size_t len = part.size(); + sal_Int32 i = 0; + if (parsePart(part, true, &i).isEmpty() || part[0] == '/') { + return false; + } + if (o3tl::make_unsigned(i) == len) { + return true; + } + for (;;) { + ++i; // skip '?' or '&' + if (parsePart(part, false, &i).isEmpty() || o3tl::make_unsigned(i) == len + || part[i] != '=') + { + return false; + } + ++i; + parsePart(part, false, &i); + if (o3tl::make_unsigned(i) == len) { + return true; + } + if (part[i] != '&') { + return false; + } + } +} + +class UrlReference: + public cppu::WeakImplHelper<css::uri::XVndSunStarScriptUrlReference> +{ +public: + UrlReference(OUString const & scheme, OUString const & path): + m_base( + scheme, false, OUString(), path, false, OUString()) + {} + + UrlReference(const UrlReference&) = delete; + UrlReference& operator=(const UrlReference&) = delete; + + virtual OUString SAL_CALL getUriReference() override + { return m_base.getUriReference(); } + + virtual sal_Bool SAL_CALL isAbsolute() override + { return m_base.isAbsolute(); } + + virtual OUString SAL_CALL getScheme() override + { return m_base.getScheme(); } + + virtual OUString SAL_CALL getSchemeSpecificPart() override + { return m_base.getSchemeSpecificPart(); } + + virtual sal_Bool SAL_CALL isHierarchical() override + { return m_base.isHierarchical(); } + + virtual sal_Bool SAL_CALL hasAuthority() override + { return m_base.hasAuthority(); } + + virtual OUString SAL_CALL getAuthority() override + { return m_base.getAuthority(); } + + virtual OUString SAL_CALL getPath() override + { return m_base.getPath(); } + + virtual sal_Bool SAL_CALL hasRelativePath() override + { return m_base.hasRelativePath(); } + + virtual sal_Int32 SAL_CALL getPathSegmentCount() override + { return m_base.getPathSegmentCount(); } + + virtual OUString SAL_CALL getPathSegment(sal_Int32 index) override + { return m_base.getPathSegment(index); } + + virtual sal_Bool SAL_CALL hasQuery() override + { return m_base.hasQuery(); } + + virtual OUString SAL_CALL getQuery() override + { return m_base.getQuery(); } + + virtual sal_Bool SAL_CALL hasFragment() override + { return m_base.hasFragment(); } + + virtual OUString SAL_CALL getFragment() override + { return m_base.getFragment(); } + + virtual void SAL_CALL setFragment(OUString const & fragment) override + { m_base.setFragment(fragment); } + + virtual void SAL_CALL clearFragment() override + { m_base.clearFragment(); } + + virtual OUString SAL_CALL getName() override; + + virtual void SAL_CALL setName(OUString const & name) override; + + virtual sal_Bool SAL_CALL hasParameter(OUString const & key) override; + + virtual OUString SAL_CALL getParameter(OUString const & key) override; + + virtual void SAL_CALL setParameter(OUString const & key, OUString const & value) override; + +private: + virtual ~UrlReference() override {} + + sal_Int32 findParameter(std::u16string_view key) const; + + stoc::uriproc::UriReference m_base; +}; + +OUString UrlReference::getName() { + std::lock_guard g(m_base.m_mutex); + sal_Int32 i = 0; + return parsePart(m_base.m_path, true, &i); +} + +void SAL_CALL UrlReference::setName(OUString const & name) +{ + if (name.isEmpty()) + throw css::lang::IllegalArgumentException( + OUString(), *this, 1); + + std::lock_guard g(m_base.m_mutex); + sal_Int32 i = 0; + parsePart(m_base.m_path, true, &i); + + m_base.m_path = encodeNameOrParamFragment(name) + m_base.m_path.subView(i); +} + +sal_Bool UrlReference::hasParameter(OUString const & key) +{ + std::lock_guard g(m_base.m_mutex); + return findParameter(key) >= 0; +} + +OUString UrlReference::getParameter(OUString const & key) +{ + std::lock_guard g(m_base.m_mutex); + sal_Int32 i = findParameter(key); + return i >= 0 ? parsePart(m_base.m_path, false, &i) : OUString(); +} + +void UrlReference::setParameter(OUString const & key, OUString const & value) +{ + if (key.isEmpty()) + throw css::lang::IllegalArgumentException( + OUString(), *this, 1); + + std::lock_guard g(m_base.m_mutex); + sal_Int32 i = findParameter(key); + bool bExistent = ( i>=0 ); + if (!bExistent) { + i = m_base.m_path.getLength(); + } + + OUStringBuffer newPath(128); + newPath.append(m_base.m_path.subView(0, i)); + if (!bExistent) { + newPath.append( m_base.m_path.indexOf('?') < 0 ? '?' : '&' ); + newPath.append(encodeNameOrParamFragment(key)); + newPath.append('='); + } + newPath.append(encodeNameOrParamFragment(value)); + if (bExistent) { + /*oldValue = */ + parsePart(m_base.m_path, false, &i); // skip key + newPath.append(m_base.m_path.subView(i)); + } + + m_base.m_path = newPath.makeStringAndClear(); +} + +sal_Int32 UrlReference::findParameter(std::u16string_view key) const { + sal_Int32 i = 0; + parsePart(m_base.m_path, true, &i); // skip name + for (;;) { + if (i == m_base.m_path.getLength()) { + return -1; + } + ++i; // skip '?' or '&' + OUString k = parsePart(m_base.m_path, false, &i); + ++i; // skip '=' + if (k == key) { + return i; + } + parsePart(m_base.m_path, false, &i); // skip value + } +} + +class Parser: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XUriSchemeParser> +{ +public: + Parser() {} + + Parser(const Parser&) = delete; + Parser& operator=(const Parser&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + parse( + OUString const & scheme, OUString const & schemeSpecificPart) override; + +private: + virtual ~Parser() override {} +}; + +OUString Parser::getImplementationName() +{ + return "com.sun.star.comp.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript"; +} + +sal_Bool Parser::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Parser::getSupportedServiceNames() +{ + return { "com.sun.star.uri.UriSchemeParser_vndDOTsunDOTstarDOTscript" }; +} + +css::uno::Reference< css::uri::XUriReference > +Parser::parse( + OUString const & scheme, OUString const & schemeSpecificPart) +{ + if (!parseSchemeSpecificPart(schemeSpecificPart)) { + return nullptr; + } + return new UrlReference(scheme, schemeSpecificPart); +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_UriSchemeParser_vndDOTsunDOTstarDOTscript_get_implementation(css::uno::XComponentContext*, + css::uno::Sequence<css::uno::Any> const &) +{ + //TODO: single instance + return ::cppu::acquire(new Parser()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/stoc/source/uriproc/VndSunStarPkgUrlReferenceFactory.cxx b/stoc/source/uriproc/VndSunStarPkgUrlReferenceFactory.cxx new file mode 100644 index 000000000..2a3211a08 --- /dev/null +++ b/stoc/source/uriproc/VndSunStarPkgUrlReferenceFactory.cxx @@ -0,0 +1,119 @@ +/* -*- 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 <com/sun/star/lang/XServiceInfo.hpp> +#include <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/uno/RuntimeException.hpp> +#include <com/sun/star/uno/Sequence.hxx> +#include <com/sun/star/uri/UriReferenceFactory.hpp> +#include <com/sun/star/uri/XUriReference.hpp> +#include <com/sun/star/uri/XVndSunStarPkgUrlReferenceFactory.hpp> +#include <cppuhelper/implbase.hxx> +#include <cppuhelper/supportsservice.hxx> +#include <cppuhelper/weak.hxx> +#include <rtl/textenc.h> +#include <rtl/uri.h> +#include <rtl/uri.hxx> +#include <rtl/ustring.hxx> +#include <sal/types.h> + +namespace com::sun::star::uno { class XComponentContext; } +namespace com::sun::star::uno { class XInterface; } + +namespace { + +class Factory: + public cppu::WeakImplHelper< + css::lang::XServiceInfo, css::uri::XVndSunStarPkgUrlReferenceFactory> +{ +public: + explicit Factory( + css::uno::Reference< css::uno::XComponentContext > const & context): + m_context(context) {} + + Factory(const Factory&) = delete; + Factory& operator=(const Factory&) = delete; + + virtual OUString SAL_CALL getImplementationName() override; + + virtual sal_Bool SAL_CALL supportsService(OUString const & serviceName) override; + + virtual css::uno::Sequence< OUString > SAL_CALL + getSupportedServiceNames() override; + + virtual css::uno::Reference< css::uri::XUriReference > SAL_CALL + createVndSunStarPkgUrlReference( + css::uno::Reference< css::uri::XUriReference > const & authority) override; + +private: + virtual ~Factory() override {} + + css::uno::Reference< css::uno::XComponentContext > m_context; +}; + +OUString Factory::getImplementationName() +{ + return "com.sun.star.comp.uri.VndSunStarPkgUrlReferenceFactory"; +} + +sal_Bool Factory::supportsService(OUString const & serviceName) +{ + return cppu::supportsService(this, serviceName); +} + +css::uno::Sequence< OUString > Factory::getSupportedServiceNames() +{ + css::uno::Sequence< OUString > s { "com.sun.star.uri.VndSunStarPkgUrlReferenceFactory" }; + return s; +} + +css::uno::Reference< css::uri::XUriReference > +Factory::createVndSunStarPkgUrlReference( + css::uno::Reference< css::uri::XUriReference > const & authority) +{ + if (!authority.is()) { + throw css::uno::RuntimeException( + "null authority passed to" + " XVndSunStarPkgUrlReferenceFactory.createVndSunStarPkgUrlReference"); + } + if (authority->isAbsolute() && !authority->hasFragment()) { + OUString buf = + "vnd.sun.star.pkg://" + + rtl::Uri::encode( + authority->getUriReference(), rtl_UriCharClassRegName, + rtl_UriEncodeIgnoreEscapes, RTL_TEXTENCODING_UTF8); + css::uno::Reference< css::uri::XUriReference > uriRef( + css::uri::UriReferenceFactory::create(m_context)->parse( + buf)); + return uriRef; + } else { + return css::uno::Reference< css::uri::XUriReference >(); + } +} + +} + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +com_sun_star_comp_uri_VndSunStarPkgUrlReferenceFactory_get_implementation(css::uno::XComponentContext* rxContext, + css::uno::Sequence<css::uno::Any> const &) +{ + return ::cppu::acquire(new Factory(rxContext)); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |