diff options
Diffstat (limited to 'stoc/source/inspect')
-rw-r--r-- | stoc/source/inspect/introspection.component | 28 | ||||
-rw-r--r-- | stoc/source/inspect/introspection.cxx | 2420 |
2 files changed, 2448 insertions, 0 deletions
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: */ |