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