1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2011 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 #include <vbahelper/collectionbase.hxx> 29 30 #include <map> 31 #include <com/sun/star/container/XIndexAccess.hpp> 32 #include <com/sun/star/container/XNameAccess.hpp> 33 #include <cppuhelper/implbase2.hxx> 34 35 namespace vbahelper { 36 37 using namespace ::com::sun::star; 38 using namespace ::ooo::vba; 39 40 // ============================================================================ 41 42 namespace { 43 44 // ---------------------------------------------------------------------------- 45 46 class CollectionEnumeration : public ::cppu::WeakImplHelper1< container::XEnumeration > 47 { 48 public: 49 explicit CollectionEnumeration( const ::rtl::Reference< CollectionBase >& rxCollection ); 50 virtual sal_Bool SAL_CALL hasMoreElements() throw (uno::RuntimeException); 51 virtual uno::Any SAL_CALL nextElement() throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException); 52 53 private: 54 ::rtl::Reference< CollectionBase > mxCollection; 55 sal_Int32 mnCurrIndex; 56 }; 57 58 CollectionEnumeration::CollectionEnumeration( const ::rtl::Reference< CollectionBase >& rxCollection ) : 59 mxCollection( rxCollection ), 60 mnCurrIndex( 1 ) // collection expects one-based indexes 61 { 62 } 63 64 sal_Bool SAL_CALL CollectionEnumeration::hasMoreElements() throw (uno::RuntimeException) 65 { 66 return mnCurrIndex <= mxCollection->getCount(); 67 } 68 69 uno::Any SAL_CALL CollectionEnumeration::nextElement() throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException) 70 { 71 if( hasMoreElements() ) 72 return mxCollection->getItemByIndex( mnCurrIndex++ ); 73 throw container::NoSuchElementException(); 74 } 75 76 // ---------------------------------------------------------------------------- 77 78 struct IsLessIgnoreCase 79 { 80 inline bool operator()( const ::rtl::OUString& rName1, const ::rtl::OUString& rName2 ) const 81 { return ::rtl_ustr_compareIgnoreAsciiCase_WithLength( rName1.getStr(), rName1.getLength(), rName2.getStr(), rName2.getLength() ) < 0; } 82 }; 83 84 // ---------------------------------------------------------------------------- 85 86 class SequenceToContainer : public ::cppu::WeakImplHelper2< container::XIndexAccess, container::XNameAccess > 87 { 88 public: 89 explicit SequenceToContainer( const ::std::vector< uno::Reference< container::XNamed > >& rElements, const uno::Type& rElementType ); 90 explicit SequenceToContainer( const ::std::vector< beans::NamedValue >& rElements, const uno::Type& rElementType ); 91 // XIndexAccess 92 virtual sal_Int32 SAL_CALL getCount() throw (uno::RuntimeException); 93 virtual uno::Any SAL_CALL getByIndex( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, lang::WrappedTargetException, uno::RuntimeException); 94 // XNameAccess 95 virtual uno::Any SAL_CALL getByName( const ::rtl::OUString& rName ) throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException); 96 virtual uno::Sequence< ::rtl::OUString > SAL_CALL getElementNames() throw (uno::RuntimeException); 97 virtual sal_Bool SAL_CALL hasByName( const ::rtl::OUString& rName ) throw (uno::RuntimeException); 98 // XElementAccess 99 virtual uno::Type SAL_CALL getElementType() throw (uno::RuntimeException); 100 virtual sal_Bool SAL_CALL hasElements() throw (uno::RuntimeException); 101 102 private: 103 typedef uno::Sequence< ::rtl::OUString > ElementNameSequence; 104 typedef ::std::vector< uno::Any > ElementVector; 105 typedef ::std::map< ::rtl::OUString, uno::Any, IsLessIgnoreCase > ElementMap; 106 107 ElementNameSequence maElementNames; 108 ElementVector maElements; 109 ElementMap maElementMap; 110 uno::Type maElementType; 111 }; 112 113 SequenceToContainer::SequenceToContainer( const ::std::vector< uno::Reference< container::XNamed > >& rElements, const uno::Type& rElementType ) : 114 maElementType( rElementType ) 115 { 116 maElementNames.realloc( static_cast< sal_Int32 >( rElements.size() ) ); 117 maElements.reserve( rElements.size() ); 118 ::rtl::OUString* pElementName = maElementNames.getArray(); 119 for( ::std::vector< uno::Reference< container::XNamed > >::const_iterator aIt = rElements.begin(), aEnd = rElements.end(); aIt != aEnd; ++aIt, ++pElementName ) 120 { 121 uno::Reference< container::XNamed > xNamed = *aIt; 122 *pElementName = xNamed->getName(); 123 maElements.push_back( uno::Any( xNamed ) ); 124 // same name may occur multiple times, VBA returns first occurance 125 if( maElementMap.count( *pElementName ) == 0 ) 126 maElementMap[ *pElementName ] <<= xNamed; 127 } 128 } 129 130 SequenceToContainer::SequenceToContainer( const ::std::vector< beans::NamedValue >& rElements, const uno::Type& rElementType ) : 131 maElementType( rElementType ) 132 { 133 maElementNames.realloc( static_cast< sal_Int32 >( rElements.size() ) ); 134 maElements.reserve( rElements.size() ); 135 ::rtl::OUString* pElementName = maElementNames.getArray(); 136 for( ::std::vector< beans::NamedValue >::const_iterator aIt = rElements.begin(), aEnd = rElements.end(); aIt != aEnd; ++aIt, ++pElementName ) 137 { 138 *pElementName = aIt->Name; 139 maElements.push_back( aIt->Value ); 140 // same name may occur multiple times, VBA returns first occurance 141 if( maElementMap.count( *pElementName ) == 0 ) 142 maElementMap[ *pElementName ] = aIt->Value; 143 } 144 } 145 146 sal_Int32 SAL_CALL SequenceToContainer::getCount() throw (uno::RuntimeException) 147 { 148 return static_cast< sal_Int32 >( maElements.size() ); 149 } 150 151 uno::Any SAL_CALL SequenceToContainer::getByIndex( sal_Int32 nIndex ) throw (lang::IndexOutOfBoundsException, lang::WrappedTargetException, uno::RuntimeException) 152 { 153 if( (0 <= nIndex) && (nIndex < getCount()) ) 154 return maElements[ static_cast< size_t >( nIndex ) ]; 155 throw lang::IndexOutOfBoundsException(); 156 } 157 158 uno::Any SAL_CALL SequenceToContainer::getByName( const ::rtl::OUString& rName ) throw (container::NoSuchElementException, lang::WrappedTargetException, uno::RuntimeException) 159 { 160 ElementMap::iterator aIt = maElementMap.find( rName ); 161 if( aIt != maElementMap.end() ) 162 return aIt->second; 163 throw container::NoSuchElementException(); 164 } 165 166 uno::Sequence< ::rtl::OUString > SAL_CALL SequenceToContainer::getElementNames() throw (uno::RuntimeException) 167 { 168 return maElementNames; 169 } 170 171 sal_Bool SAL_CALL SequenceToContainer::hasByName( const ::rtl::OUString& rName ) throw (uno::RuntimeException) 172 { 173 return maElementMap.count( rName ) > 0; 174 } 175 176 uno::Type SAL_CALL SequenceToContainer::getElementType() throw (uno::RuntimeException) 177 { 178 return maElementType; 179 } 180 181 sal_Bool SAL_CALL SequenceToContainer::hasElements() throw (uno::RuntimeException) 182 { 183 return !maElements.empty(); 184 } 185 186 } // namespace 187 188 // ============================================================================ 189 190 CollectionBase::CollectionBase( const uno::Type& rElementType ) : 191 maElementType( rElementType ), 192 mbConvertOnDemand( false ) 193 { 194 } 195 196 sal_Int32 SAL_CALL CollectionBase::getCount() throw (uno::RuntimeException) 197 { 198 if( mxIndexAccess.is() ) 199 return mxIndexAccess->getCount(); 200 if( mxNameAccess.is() ) 201 return mxNameAccess->getElementNames().getLength(); 202 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No element container set." ) ), 0 ); 203 } 204 205 uno::Reference< container::XEnumeration > SAL_CALL CollectionBase::createEnumeration() throw (uno::RuntimeException) 206 { 207 return new CollectionEnumeration( this ); 208 } 209 210 uno::Type SAL_CALL CollectionBase::getElementType() throw (uno::RuntimeException) 211 { 212 return maElementType; 213 } 214 215 sal_Bool SAL_CALL CollectionBase::hasElements() throw (uno::RuntimeException) 216 { 217 if( mxIndexAccess.is() ) 218 return mxIndexAccess->hasElements(); 219 if( mxNameAccess.is() ) 220 return mxNameAccess->hasElements(); 221 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No element container set." ) ), 0 ); 222 } 223 224 ::rtl::OUString SAL_CALL CollectionBase::getDefaultMethodName() throw (uno::RuntimeException) 225 { 226 static ::rtl::OUString saDefMethodName( RTL_CONSTASCII_USTRINGPARAM( "Item" ) ); 227 return saDefMethodName; 228 } 229 230 // ---------------------------------------------------------------------------- 231 232 void CollectionBase::initContainer( 233 const uno::Reference< container::XElementAccess >& rxElementAccess, 234 ContainerType eContainerType ) throw (uno::RuntimeException) 235 { 236 mxIndexAccess.set( rxElementAccess, uno::UNO_QUERY ); 237 mxNameAccess.set( rxElementAccess, uno::UNO_QUERY ); 238 switch( eContainerType ) 239 { 240 case CONTAINER_NATIVE_VBA: 241 mbConvertOnDemand = false; 242 break; 243 case CONTAINER_CONVERT_ON_DEMAND: 244 mbConvertOnDemand = true; 245 break; 246 } 247 } 248 249 void CollectionBase::initElements( const ::std::vector< uno::Reference< container::XNamed > >& rElements, ContainerType eContainerType ) throw (uno::RuntimeException) 250 { 251 // SequenceToContainer derives twice from XElementAccess, need to resolve ambiguity 252 initContainer( static_cast< container::XIndexAccess* >( new SequenceToContainer( rElements, maElementType ) ), eContainerType ); 253 } 254 255 void CollectionBase::initElements( const ::std::vector< beans::NamedValue >& rElements, ContainerType eContainerType ) throw (uno::RuntimeException) 256 { 257 // SequenceToContainer derives twice from XElementAccess, need to resolve ambiguity 258 initContainer( static_cast< container::XIndexAccess* >( new SequenceToContainer( rElements, maElementType ) ), eContainerType ); 259 } 260 261 uno::Any CollectionBase::createCollectionItem( const uno::Any& rElement, const uno::Any& rIndex ) throw (css::uno::RuntimeException) 262 { 263 uno::Any aItem = mbConvertOnDemand ? implCreateCollectionItem( rElement, rIndex ) : rElement; 264 if( aItem.hasValue() ) 265 return aItem; 266 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid item." ) ), 0 ); 267 } 268 269 uno::Any CollectionBase::getItemByIndex( sal_Int32 nIndex ) throw (uno::RuntimeException) 270 { 271 if( mxIndexAccess.is() ) 272 { 273 if( (1 <= nIndex) && (nIndex <= mxIndexAccess->getCount()) ) 274 // createCollectionItem() will convert from container element to VBA item 275 return createCollectionItem( mxIndexAccess->getByIndex( nIndex - 1 ), uno::Any( nIndex ) ); 276 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Index out of bounds." ) ), 0 ); 277 } 278 if( mxNameAccess.is() ) 279 { 280 uno::Sequence< ::rtl::OUString > aElementNames = mxNameAccess->getElementNames(); 281 if( (1 <= nIndex) && (nIndex <= aElementNames.getLength()) ) 282 // createCollectionItem() will convert from container element to VBA item 283 return createCollectionItem( mxNameAccess->getByName( aElementNames[ nIndex - 1 ] ), uno::Any( aElementNames[ nIndex - 1 ] ) ); 284 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Index out of bounds." ) ), 0 ); 285 } 286 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No element container set." ) ), 0 ); 287 } 288 289 uno::Any CollectionBase::getItemByName( const ::rtl::OUString& rName ) throw (uno::RuntimeException) 290 { 291 if( mxNameAccess.is() ) 292 { 293 if( rName.getLength() > 0 ) 294 // createCollectionItem() will convert from container element to VBA item 295 return createCollectionItem( mxNameAccess->getByName( rName ), uno::Any( rName ) ); 296 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid item name." ) ), 0 ); 297 } 298 if( mxIndexAccess.is() ) 299 { 300 for( sal_Int32 nIndex = 0, nSize = mxIndexAccess->getCount(); nIndex < nSize; ++nIndex ) 301 { 302 uno::Any aElement = mxIndexAccess->getByIndex( nIndex ); 303 uno::Reference< container::XNamed > xNamed( aElement, uno::UNO_QUERY ); 304 if( xNamed.is() && xNamed->getName().equalsIgnoreAsciiCase( rName ) ) 305 // createCollectionItem() will convert from container element to VBA item 306 return createCollectionItem( aElement, uno::Any( nIndex ) ); 307 } 308 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Invalid item name." ) ), 0 ); 309 } 310 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "No element container set." ) ), 0 ); 311 } 312 313 uno::Any CollectionBase::getAnyItemOrThis( const uno::Any& rIndex ) throw (uno::RuntimeException) 314 { 315 if( !rIndex.hasValue() ) 316 return uno::Any( uno::Reference< XCollectionBase >( this ) ); 317 if( rIndex.has< ::rtl::OUString >() ) 318 return getItemByName( rIndex.get< ::rtl::OUString >() ); 319 // extractIntFromAny() throws if no index can be extracted 320 return getItemByIndex( extractIntFromAny( rIndex ) ); 321 } 322 323 // protected ------------------------------------------------------------------ 324 325 uno::Any CollectionBase::implCreateCollectionItem( const uno::Any& /*rElement*/, const uno::Any& /*rIndex*/ ) throw (uno::RuntimeException) 326 { 327 throw uno::RuntimeException( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Creation of VBA implementation object not implemented." ) ), 0 ); 328 } 329 330 // ============================================================================ 331 332 } // namespace vbahelper 333