diff options
Diffstat (limited to 'vbahelper/source/vbahelper/collectionbase.cxx')
-rwxr-xr-x | vbahelper/source/vbahelper/collectionbase.cxx | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/vbahelper/source/vbahelper/collectionbase.cxx b/vbahelper/source/vbahelper/collectionbase.cxx new file mode 100755 index 000000000000..16fc673f54d5 --- /dev/null +++ b/vbahelper/source/vbahelper/collectionbase.cxx @@ -0,0 +1,332 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2000, 2011 Oracle and/or its affiliates. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * This file is part of OpenOffice.org. + * + * OpenOffice.org is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License version 3 + * only, as published by the Free Software Foundation. + * + * OpenOffice.org is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License version 3 for more details + * (a copy is included in the LICENSE file that accompanied this code). + * + * You should have received a copy of the GNU Lesser General Public License + * version 3 along with OpenOffice.org. If not, see + * <http://www.openoffice.org/license.html> + * for a copy of the LGPLv3 License. + * + ************************************************************************/ + +#include <vbahelper/collectionbase.hxx> + +#include <map> +#include <com/sun/star/container/XIndexAccess.hpp> +#include <com/sun/star/container/XNameAccess.hpp> +#include <cppuhelper/implbase2.hxx> + +namespace vbahelper { + +using namespace ::com::sun::star; +using namespace ::ooo::vba; + +// ============================================================================ + +namespace { + +// ---------------------------------------------------------------------------- + +class CollectionEnumeration : public ::cppu::WeakImplHelper1< container::XEnumeration > +{ +public: + explicit CollectionEnumeration( const ::rtl::Reference< CollectionBase >& rxCollection ); + virtual sal_Bool SAL_CALL hasMoreElements() throw (uno::RuntimeException); + virtual uno::Any SAL_CALL nextElement() throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException); + +private: + ::rtl::Reference< CollectionBase > mxCollection; + sal_Int32 mnCurrIndex; +}; + +CollectionEnumeration::CollectionEnumeration( const ::rtl::Reference< CollectionBase >& rxCollection ) : + mxCollection( rxCollection ), + mnCurrIndex( 1 ) // collection expects one-based indexes +{ +} + +sal_Bool SAL_CALL CollectionEnumeration::hasMoreElements() throw (uno::RuntimeException) +{ + return mnCurrIndex <= mxCollection->getCount(); +} + +uno::Any SAL_CALL CollectionEnumeration::nextElement() throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException) +{ + if( hasMoreElements() ) + return mxCollection->getItemByIndex( mnCurrIndex++ ); + throw container::NoSuchElementException(); +} + +// ---------------------------------------------------------------------------- + +struct IsLessIgnoreCase +{ + inline bool operator()( const ::rtl::OUString& rName1, const ::rtl::OUString& rName2 ) const + { return ::rtl_ustr_compareIgnoreAsciiCase_WithLength( rName1.getStr(), rName1.getLength(), rName2.getStr(), rName2.getLength() ) < 0; } +}; + +// ---------------------------------------------------------------------------- + +class SequenceToContainer : public ::cppu::WeakImplHelper2< container::XIndexAccess, container::XNameAccess > +{ +public: + explicit SequenceToContainer( const ::std::vector< uno::Reference< container::XNamed > >& rElements, const uno::Type& rElementType ); + explicit SequenceToContainer( const ::std::vector< beans::NamedValue >& rElements, const uno::Type& rElementType ); + // XIndexAccess + virtual sal_Int32 SAL_CALL getCount() throw (uno::RuntimeException); + virtual uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, lang::WrappedTargetException, uno::RuntimeException); + // XNameAccess + virtual uno::Any SAL_CALL getByName( const ::rtl::OUString& rName ) throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException); + virtual uno::Sequence< ::rtl::OUString > SAL_CALL getElementNames() throw (uno::RuntimeException); + virtual sal_Bool SAL_CALL hasByName( const ::rtl::OUString& rName ) throw (uno::RuntimeException); + // XElementAccess + virtual uno::Type SAL_CALL getElementType() throw (uno::RuntimeException); + virtual sal_Bool SAL_CALL hasElements() throw (uno::RuntimeException); + +private: + typedef uno::Sequence< ::rtl::OUString > ElementNameSequence; + typedef ::std::vector< uno::Any > ElementVector; + typedef ::std::map< ::rtl::OUString, uno::Any, IsLessIgnoreCase > ElementMap; + + ElementNameSequence maElementNames; + ElementVector maElements; + ElementMap maElementMap; + uno::Type maElementType; +}; + +SequenceToContainer::SequenceToContainer( const ::std::vector< uno::Reference< container::XNamed > >& rElements, const uno::Type& rElementType ) : + maElementType( rElementType ) +{ + maElementNames.realloc( static_cast< sal_Int32 >( rElements.size() ) ); + maElements.reserve( rElements.size() ); + ::rtl::OUString* pElementName = maElementNames.getArray(); + for( ::std::vector< uno::Reference< container::XNamed > >::const_iterator aIt = rElements.begin(), aEnd = rElements.end(); aIt != aEnd; ++aIt, ++pElementName ) + { + uno::Reference< container::XNamed > xNamed = *aIt; + *pElementName = xNamed->getName(); + maElements.push_back( uno::Any( xNamed ) ); + // same name may occur multiple times, VBA returns first occurance + if( maElementMap.count( *pElementName ) == 0 ) + maElementMap[ *pElementName ] <<= xNamed; + } +} + +SequenceToContainer::SequenceToContainer( const ::std::vector< beans::NamedValue >& rElements, const uno::Type& rElementType ) : + maElementType( rElementType ) +{ + maElementNames.realloc( static_cast< sal_Int32 >( rElements.size() ) ); + maElements.reserve( rElements.size() ); + ::rtl::OUString* pElementName = maElementNames.getArray(); + for( ::std::vector< beans::NamedValue >::const_iterator aIt = rElements.begin(), aEnd = rElements.end(); aIt != aEnd; ++aIt, ++pElementName ) + { + *pElementName = aIt->Name; + maElements.push_back( aIt->Value ); + // same name may occur multiple times, VBA returns first occurance + if( maElementMap.count( *pElementName ) == 0 ) + maElementMap[ *pElementName ] = aIt->Value; + } +} + +sal_Int32 SAL_CALL SequenceToContainer::getCount() throw (uno::RuntimeException) +{ + return static_cast< sal_Int32 >( maElements.size() ); +} + +uno::Any SAL_CALL SequenceToContainer::getByIndex( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, lang::WrappedTargetException, uno::RuntimeException) +{ + if( (0 <= nIndex) && (nIndex < getCount()) ) + return maElements[ static_cast< size_t >( nIndex ) ]; + throw lang::IndexOutOfBoundsException(); +} + +uno::Any SAL_CALL SequenceToContainer::getByName( const ::rtl::OUString& rName ) throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException) +{ + ElementMap::iterator aIt = maElementMap.find( rName ); + if( aIt != maElementMap.end() ) + return aIt->second; + throw container::NoSuchElementException(); +} + +uno::Sequence< ::rtl::OUString > SAL_CALL SequenceToContainer::getElementNames() throw (uno::RuntimeException) +{ + return maElementNames; +} + +sal_Bool SAL_CALL SequenceToContainer::hasByName( const ::rtl::OUString& rName ) throw (uno::RuntimeException) +{ + return maElementMap.count( rName ) > 0; +} + +uno::Type SAL_CALL SequenceToContainer::getElementType() throw (uno::RuntimeException) +{ + return maElementType; +} + +sal_Bool SAL_CALL SequenceToContainer::hasElements() throw (uno::RuntimeException) +{ + return !maElements.empty(); +} + +} // namespace + +// ============================================================================ + +CollectionBase::CollectionBase( const uno::Type& rElementType ) : + maElementType( rElementType ), + mbConvertOnDemand( false ) +{ +} + +sal_Int32 SAL_CALL CollectionBase::getCount() throw (uno::RuntimeException) +{ + if( mxIndexAccess.is() ) + return mxIndexAccess->getCount(); + if( mxNameAccess.is() ) + return mxNameAccess->getElementNames().getLength(); + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No element container set." ) ), 0 ); +} + +uno::Reference< container::XEnumeration > SAL_CALL CollectionBase::createEnumeration() throw (uno::RuntimeException) +{ + return new CollectionEnumeration( this ); +} + +uno::Type SAL_CALL CollectionBase::getElementType() throw (uno::RuntimeException) +{ + return maElementType; +} + +sal_Bool SAL_CALL CollectionBase::hasElements() throw (uno::RuntimeException) +{ + if( mxIndexAccess.is() ) + return mxIndexAccess->hasElements(); + if( mxNameAccess.is() ) + return mxNameAccess->hasElements(); + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No element container set." ) ), 0 ); +} + +::rtl::OUString SAL_CALL CollectionBase::getDefaultMethodName() throw (uno::RuntimeException) +{ + static ::rtl::OUString saDefMethodName( RTL_CONSTASCII_USTRINGPARAM( "Item" ) ); + return saDefMethodName; +} + +// ---------------------------------------------------------------------------- + +void CollectionBase::initContainer( + const uno::Reference< container::XElementAccess >& rxElementAccess, + ContainerType eContainerType ) throw (uno::RuntimeException) +{ + mxIndexAccess.set( rxElementAccess, uno::UNO_QUERY ); + mxNameAccess.set( rxElementAccess, uno::UNO_QUERY ); + switch( eContainerType ) + { + case CONTAINER_NATIVE_VBA: + mbConvertOnDemand = false; + break; + case CONTAINER_CONVERT_ON_DEMAND: + mbConvertOnDemand = true; + break; + } +} + +void CollectionBase::initElements( const ::std::vector< uno::Reference< container::XNamed > >& rElements, ContainerType eContainerType ) throw (uno::RuntimeException) +{ + // SequenceToContainer derives twice from XElementAccess, need to resolve ambiguity + initContainer( static_cast< container::XIndexAccess* >( new SequenceToContainer( rElements, maElementType ) ), eContainerType ); +} + +void CollectionBase::initElements( const ::std::vector< beans::NamedValue >& rElements, ContainerType eContainerType ) throw (uno::RuntimeException) +{ + // SequenceToContainer derives twice from XElementAccess, need to resolve ambiguity + initContainer( static_cast< container::XIndexAccess* >( new SequenceToContainer( rElements, maElementType ) ), eContainerType ); +} + +uno::Any CollectionBase::createCollectionItem( const uno::Any& rElement, const uno::Any& rIndex ) throw (css::uno::RuntimeException) +{ + uno::Any aItem = mbConvertOnDemand ? implCreateCollectionItem( rElement, rIndex ) : rElement; + if( aItem.hasValue() ) + return aItem; + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid item." ) ), 0 ); +} + +uno::Any CollectionBase::getItemByIndex( sal_Int32 nIndex ) throw (uno::RuntimeException) +{ + if( mxIndexAccess.is() ) + { + if( (1 <= nIndex) && (nIndex <= mxIndexAccess->getCount()) ) + // createCollectionItem() will convert from container element to VBA item + return createCollectionItem( mxIndexAccess->getByIndex( nIndex - 1 ), uno::Any( nIndex ) ); + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Index out of bounds." ) ), 0 ); + } + if( mxNameAccess.is() ) + { + uno::Sequence< ::rtl::OUString > aElementNames = mxNameAccess->getElementNames(); + if( (1 <= nIndex) && (nIndex <= aElementNames.getLength()) ) + // createCollectionItem() will convert from container element to VBA item + return createCollectionItem( mxNameAccess->getByName( aElementNames[ nIndex - 1 ] ), uno::Any( aElementNames[ nIndex - 1 ] ) ); + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Index out of bounds." ) ), 0 ); + } + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No element container set." ) ), 0 ); +} + +uno::Any CollectionBase::getItemByName( const ::rtl::OUString& rName ) throw (uno::RuntimeException) +{ + if( mxNameAccess.is() ) + { + if( rName.getLength() > 0 ) + // createCollectionItem() will convert from container element to VBA item + return createCollectionItem( mxNameAccess->getByName( rName ), uno::Any( rName ) ); + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid item name." ) ), 0 ); + } + if( mxIndexAccess.is() ) + { + for( sal_Int32 nIndex = 0, nSize = mxIndexAccess->getCount(); nIndex < nSize; ++nIndex ) + { + uno::Any aElement = mxIndexAccess->getByIndex( nIndex ); + uno::Reference< container::XNamed > xNamed( aElement, uno::UNO_QUERY ); + if( xNamed.is() && xNamed->getName().equalsIgnoreAsciiCase( rName ) ) + // createCollectionItem() will convert from container element to VBA item + return createCollectionItem( aElement, uno::Any( nIndex ) ); + } + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid item name." ) ), 0 ); + } + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No element container set." ) ), 0 ); +} + +uno::Any CollectionBase::getAnyItemOrThis( const uno::Any& rIndex ) throw (uno::RuntimeException) +{ + if( !rIndex.hasValue() ) + return uno::Any( uno::Reference< XCollectionBase >( this ) ); + if( rIndex.has< ::rtl::OUString >() ) + return getItemByName( rIndex.get< ::rtl::OUString >() ); + // extractIntFromAny() throws if no index can be extracted + return getItemByIndex( extractIntFromAny( rIndex ) ); +} + +// protected ------------------------------------------------------------------ + +uno::Any CollectionBase::implCreateCollectionItem( const uno::Any& /*rElement*/, const uno::Any& /*rIndex*/ ) throw (uno::RuntimeException) +{ + throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Creation of VBA implementation object not implemented." ) ), 0 ); +} + +// ============================================================================ + +} // namespace vbahelper |