1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
11  *   Licensed to the Apache Software Foundation (ASF) under one or more
12  *   contributor license agreements. See the NOTICE file distributed
13  *   with this work for additional information regarding copyright
14  *   ownership. The ASF licenses this file to you under the Apache
15  *   License, Version 2.0 (the "License"); you may not use this file
16  *   except in compliance with the License. You may obtain a copy of
17  *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <tools/debug.hxx>
23 #include <tools/stream.hxx>
24 #include <basic/sbx.hxx>
25 #include <runtime.hxx>
26 
27 #include <boost/optional.hpp>
28 
29 using namespace std;
30 
31 struct SbxVarEntry
32 {
33     SbxVariableRef mpVar;
34     boost::optional<OUString> maAlias;
35 };
36 
37 
38 //  SbxArray
39 
SbxArray(SbxDataType t)40 SbxArray::SbxArray( SbxDataType t ) : SbxBase()
41 {
42     eType = t;
43     if( t != SbxVARIANT )
44         SetFlag( SbxFlagBits::Fixed );
45 }
46 
operator =(const SbxArray & rArray)47 SbxArray& SbxArray::operator=( const SbxArray& rArray )
48 {
49     if( &rArray != this )
50     {
51         eType = rArray.eType;
52         Clear();
53         for( const auto& rpSrcRef : rArray.mVarEntries )
54         {
55             SbxVariableRef pSrc_ = rpSrcRef.mpVar;
56             if( !pSrc_.is() )
57                 continue;
58 
59             if( eType != SbxVARIANT )
60             {
61                 // Convert no objects
62                 if( eType != SbxOBJECT || pSrc_->GetClass() != SbxClassType::Object )
63                 {
64                     pSrc_->Convert(eType);
65                 }
66             }
67             mVarEntries.push_back( rpSrcRef );
68         }
69     }
70     return *this;
71 }
72 
~SbxArray()73 SbxArray::~SbxArray()
74 {
75 }
76 
GetType() const77 SbxDataType SbxArray::GetType() const
78 {
79     return static_cast<SbxDataType>( eType | SbxARRAY );
80 }
81 
Clear()82 void SbxArray::Clear()
83 {
84     mVarEntries.clear();
85 }
86 
Count32() const87 sal_uInt32 SbxArray::Count32() const
88 {
89     return mVarEntries.size();
90 }
91 
Count() const92 sal_uInt16 SbxArray::Count() const
93 {
94     sal_uInt32 nCount = mVarEntries.size();
95     DBG_ASSERT( nCount <= SBX_MAXINDEX, "SBX: Array-Index > SBX_MAXINDEX" );
96     return static_cast<sal_uInt16>(nCount);
97 }
98 
GetRef32(sal_uInt32 nIdx)99 SbxVariableRef& SbxArray::GetRef32( sal_uInt32 nIdx )
100 {
101     // If necessary extend the array
102     DBG_ASSERT( nIdx <= SBX_MAXINDEX32, "SBX: Array-Index > SBX_MAXINDEX32" );
103     // Very Hot Fix
104     if( nIdx > SBX_MAXINDEX32 )
105     {
106         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
107         nIdx = 0;
108     }
109     if ( mVarEntries.size() <= nIdx )
110         mVarEntries.resize(nIdx+1);
111 
112     return mVarEntries[nIdx].mpVar;
113 }
114 
GetRef(sal_uInt16 nIdx)115 SbxVariableRef& SbxArray::GetRef( sal_uInt16 nIdx )
116 {
117     // If necessary extend the array
118     DBG_ASSERT( nIdx <= SBX_MAXINDEX, "SBX: Array-Index > SBX_MAXINDEX" );
119     // Very Hot Fix
120     if( nIdx > SBX_MAXINDEX )
121     {
122         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
123         nIdx = 0;
124     }
125     if ( mVarEntries.size() <= nIdx )
126         mVarEntries.resize(nIdx+1);
127 
128     return mVarEntries[nIdx].mpVar;
129 }
130 
Get32(sal_uInt32 nIdx)131 SbxVariable* SbxArray::Get32( sal_uInt32 nIdx )
132 {
133     if( !CanRead() )
134     {
135         SetError( ERRCODE_BASIC_PROP_WRITEONLY );
136         return nullptr;
137     }
138     SbxVariableRef& rRef = GetRef32( nIdx );
139 
140     if ( !rRef.is() )
141         rRef = new SbxVariable( eType );
142 
143     return rRef.get();
144 }
145 
Get(sal_uInt16 nIdx)146 SbxVariable* SbxArray::Get( sal_uInt16 nIdx )
147 {
148     if( !CanRead() )
149     {
150         SetError( ERRCODE_BASIC_PROP_WRITEONLY );
151         return nullptr;
152     }
153     SbxVariableRef& rRef = GetRef( nIdx );
154 
155     if ( !rRef.is() )
156         rRef = new SbxVariable( eType );
157 
158     return rRef.get();
159 }
160 
Put32(SbxVariable * pVar,sal_uInt32 nIdx)161 void SbxArray::Put32( SbxVariable* pVar, sal_uInt32 nIdx )
162 {
163     if( !CanWrite() )
164         SetError( ERRCODE_BASIC_PROP_READONLY );
165     else
166     {
167         if( pVar )
168             if( eType != SbxVARIANT )
169                 // Convert no objects
170                 if( eType != SbxOBJECT || pVar->GetClass() != SbxClassType::Object )
171                     pVar->Convert( eType );
172         SbxVariableRef& rRef = GetRef32( nIdx );
173         if( rRef.get() != pVar )
174         {
175             rRef = pVar;
176             SetFlag( SbxFlagBits::Modified );
177         }
178     }
179 }
180 
Put(SbxVariable * pVar,sal_uInt16 nIdx)181 void SbxArray::Put( SbxVariable* pVar, sal_uInt16 nIdx )
182 {
183     if( !CanWrite() )
184         SetError( ERRCODE_BASIC_PROP_READONLY );
185     else
186     {
187         if( pVar )
188             if( eType != SbxVARIANT )
189                 // Convert no objects
190                 if( eType != SbxOBJECT || pVar->GetClass() != SbxClassType::Object )
191                     pVar->Convert( eType );
192         SbxVariableRef& rRef = GetRef( nIdx );
193         // tdf#122250. It is possible that I hold the last reference to myself, so check, otherwise I might
194         // call SetFlag on myself after I have died.
195         bool removingMyself = rRef.get() && rRef->GetParameters() == this && GetRefCount() == 1;
196         if(rRef.get() != pVar )
197         {
198             rRef = pVar;
199             if (!removingMyself)
200                 SetFlag( SbxFlagBits::Modified );
201         }
202     }
203 }
204 
GetAlias(sal_uInt16 nIdx)205 OUString SbxArray::GetAlias( sal_uInt16 nIdx )
206 {
207     if( !CanRead() )
208     {
209         SetError( ERRCODE_BASIC_PROP_WRITEONLY );
210         return OUString();
211     }
212     SbxVarEntry& rRef = reinterpret_cast<SbxVarEntry&>(GetRef( nIdx ));
213 
214     if (!rRef.maAlias)
215         return OUString();
216 
217     return *rRef.maAlias;
218 }
219 
PutAlias(const OUString & rAlias,sal_uInt16 nIdx)220 void SbxArray::PutAlias( const OUString& rAlias, sal_uInt16 nIdx )
221 {
222     if( !CanWrite() )
223     {
224         SetError( ERRCODE_BASIC_PROP_READONLY );
225     }
226     else
227     {
228         SbxVarEntry& rRef = reinterpret_cast<SbxVarEntry&>( GetRef( nIdx ) );
229         rRef.maAlias = rAlias;
230     }
231 }
232 
Insert32(SbxVariable * pVar,sal_uInt32 nIdx)233 void SbxArray::Insert32( SbxVariable* pVar, sal_uInt32 nIdx )
234 {
235     DBG_ASSERT( mVarEntries.size() <= SBX_MAXINDEX32, "SBX: Array gets too big" );
236     if( mVarEntries.size() > SBX_MAXINDEX32 )
237     {
238             return;
239     }
240     SbxVarEntry p;
241     p.mpVar = pVar;
242     size_t nSize = mVarEntries.size();
243     if( nIdx > nSize )
244     {
245         nIdx = nSize;
246     }
247     if( eType != SbxVARIANT && pVar )
248     {
249         p.mpVar->Convert(eType);
250     }
251     if( nIdx == nSize )
252     {
253         mVarEntries.push_back( p );
254     }
255     else
256     {
257         mVarEntries.insert( mVarEntries.begin() + nIdx, p );
258     }
259     SetFlag( SbxFlagBits::Modified );
260 }
261 
Insert(SbxVariable * pVar,sal_uInt16 nIdx)262 void SbxArray::Insert( SbxVariable* pVar, sal_uInt16 nIdx )
263 {
264     DBG_ASSERT( mVarEntries.size() <= 0x3FF0, "SBX: Array gets too big" );
265     if( mVarEntries.size() > 0x3FF0 )
266     {
267         return;
268     }
269     Insert32( pVar, nIdx );
270 }
271 
Remove(sal_uInt32 nIdx)272 void SbxArray::Remove( sal_uInt32 nIdx )
273 {
274     if( nIdx < mVarEntries.size() )
275     {
276         mVarEntries.erase( mVarEntries.begin() + nIdx );
277         SetFlag( SbxFlagBits::Modified );
278     }
279 }
280 
Remove(SbxVariable const * pVar)281 void SbxArray::Remove( SbxVariable const * pVar )
282 {
283     if( pVar )
284     {
285         for( size_t i = 0; i < mVarEntries.size(); i++ )
286         {
287             if (mVarEntries[i].mpVar.get() == pVar)
288             {
289                 Remove( i ); break;
290             }
291         }
292     }
293 }
294 
295 // Taking over of the data from the passed array, at which
296 // the variable of the same name will be overwritten.
297 
Merge(SbxArray * p)298 void SbxArray::Merge( SbxArray* p )
299 {
300     if (!p)
301         return;
302 
303     for (auto& rEntry1: p->mVarEntries)
304     {
305         if (!rEntry1.mpVar.is())
306             continue;
307 
308         OUString aName = rEntry1.mpVar->GetName();
309         sal_uInt16 nHash = rEntry1.mpVar->GetHashCode();
310 
311         // Is the element by the same name already inside?
312         // Then overwrite!
313         for (auto& rEntry2: mVarEntries)
314         {
315             if (!rEntry2.mpVar.is())
316                 continue;
317 
318             if (rEntry2.mpVar->GetHashCode() == nHash &&
319                 rEntry2.mpVar->GetName().equalsIgnoreAsciiCase(aName))
320             {
321                 // Take this element and clear the original.
322                 rEntry2.mpVar = rEntry1.mpVar;
323                 rEntry1.mpVar.clear();
324                 break;
325             }
326         }
327 
328         if (rEntry1.mpVar.is())
329         {
330             // We don't have element with the same name.  Add a new entry.
331             SbxVarEntry aNewEntry;
332             aNewEntry.mpVar = rEntry1.mpVar;
333             if (rEntry1.maAlias)
334                 aNewEntry.maAlias = *rEntry1.maAlias;
335             mVarEntries.push_back(aNewEntry);
336         }
337     }
338 }
339 
340 // Search of an element by his name and type. If an element is an object,
341 // it will also be scanned...
342 
Find(const OUString & rName,SbxClassType t)343 SbxVariable* SbxArray::Find( const OUString& rName, SbxClassType t )
344 {
345     SbxVariable* p = nullptr;
346     if( mVarEntries.empty() )
347         return nullptr;
348     bool bExtSearch = IsSet( SbxFlagBits::ExtSearch );
349     sal_uInt16 nHash = SbxVariable::MakeHashCode( rName );
350     for (auto& rEntry : mVarEntries)
351     {
352         if (!rEntry.mpVar.is() || !rEntry.mpVar->IsVisible())
353             continue;
354 
355         // The very secure search works as well, if there is no hashcode!
356         sal_uInt16 nVarHash = rEntry.mpVar->GetHashCode();
357         if ( (!nVarHash || nVarHash == nHash)
358             && (t == SbxClassType::DontCare || rEntry.mpVar->GetClass() == t)
359             && (rEntry.mpVar->GetName().equalsIgnoreAsciiCase(rName)))
360         {
361             p = rEntry.mpVar.get();
362             p->ResetFlag(SbxFlagBits::ExtFound);
363             break;
364         }
365 
366         // Did we have an array/object with extended search?
367         if (bExtSearch && rEntry.mpVar->IsSet(SbxFlagBits::ExtSearch))
368         {
369             switch (rEntry.mpVar->GetClass())
370             {
371                 case SbxClassType::Object:
372                 {
373                     // Objects are not allowed to scan their parent.
374                     SbxFlagBits nOld = rEntry.mpVar->GetFlags();
375                     rEntry.mpVar->ResetFlag(SbxFlagBits::GlobalSearch);
376                     p = static_cast<SbxObject&>(*rEntry.mpVar).Find(rName, t);
377                     rEntry.mpVar->SetFlags(nOld);
378                 }
379                 break;
380                 case SbxClassType::Array:
381                     // Casting SbxVariable to SbxArray?  Really?
382                     p = reinterpret_cast<SbxArray&>(*rEntry.mpVar).Find(rName, t);
383                 break;
384                 default:
385                     ;
386             }
387 
388             if (p)
389             {
390                 p->SetFlag(SbxFlagBits::ExtFound);
391                 break;
392             }
393         }
394     }
395     return p;
396 }
397 
LoadData(SvStream & rStrm,sal_uInt16)398 bool SbxArray::LoadData( SvStream& rStrm, sal_uInt16 /*nVer*/ )
399 {
400     sal_uInt16 nElem;
401     Clear();
402     bool bRes = true;
403     SbxFlagBits f = nFlags;
404     nFlags |= SbxFlagBits::Write;
405     rStrm.ReadUInt16( nElem );
406     nElem &= 0x7FFF;
407     for( sal_uInt32 n = 0; n < nElem; n++ )
408     {
409         sal_uInt16 nIdx;
410         rStrm.ReadUInt16( nIdx );
411         SbxVariable* pVar = static_cast<SbxVariable*>(Load( rStrm ));
412         if( pVar )
413         {
414             SbxVariableRef& rRef = GetRef( nIdx );
415             rRef = pVar;
416         }
417         else
418         {
419             bRes = false;
420             break;
421         }
422     }
423     nFlags = f;
424     return bRes;
425 }
426 
StoreData(SvStream & rStrm) const427 bool SbxArray::StoreData( SvStream& rStrm ) const
428 {
429     sal_uInt32 nElem = 0;
430     // Which elements are even defined?
431     for( auto& rEntry: mVarEntries )
432     {
433         if (rEntry.mpVar.is() && !(rEntry.mpVar->GetFlags() & SbxFlagBits::DontStore))
434             nElem++;
435     }
436     rStrm.WriteUInt16( nElem );
437     for( size_t n = 0; n < mVarEntries.size(); n++ )
438     {
439         const SbxVarEntry& rEntry = mVarEntries[n];
440         if (rEntry.mpVar.is() && !(rEntry.mpVar->GetFlags() & SbxFlagBits::DontStore))
441         {
442             rStrm.WriteUInt16( n );
443             if (!rEntry.mpVar->Store(rStrm))
444                 return false;
445         }
446     }
447     return true;
448 }
449 
450 // #100883 Method to set method directly to parameter array
PutDirect(SbxVariable * pVar,sal_uInt32 nIdx)451 void SbxArray::PutDirect( SbxVariable* pVar, sal_uInt32 nIdx )
452 {
453     SbxVariableRef& rRef = GetRef32( nIdx );
454     rRef = pVar;
455 }
456 
457 
458 //  SbxArray
459 
SbxDimArray(SbxDataType t)460 SbxDimArray::SbxDimArray( SbxDataType t ) : SbxArray( t ), mbHasFixedSize( false )
461 {
462 }
463 
operator =(const SbxDimArray & rArray)464 SbxDimArray& SbxDimArray::operator=( const SbxDimArray& rArray )
465 {
466     if( &rArray != this )
467     {
468         SbxArray::operator=( static_cast<const SbxArray&>(rArray) );
469         m_vDimensions = rArray.m_vDimensions;
470         mbHasFixedSize = rArray.mbHasFixedSize;
471     }
472     return *this;
473 }
474 
~SbxDimArray()475 SbxDimArray::~SbxDimArray()
476 {
477 }
478 
Clear()479 void SbxDimArray::Clear()
480 {
481     m_vDimensions.clear();
482     SbxArray::Clear();
483 }
484 
485 // Add a dimension
486 
AddDimImpl32(sal_Int32 lb,sal_Int32 ub,bool bAllowSize0)487 void SbxDimArray::AddDimImpl32( sal_Int32 lb, sal_Int32 ub, bool bAllowSize0 )
488 {
489     ErrCode eRes = ERRCODE_NONE;
490     if( ub < lb && !bAllowSize0 )
491     {
492         eRes = ERRCODE_BASIC_OUT_OF_RANGE;
493         ub = lb;
494     }
495     SbxDim d;
496     d.nLbound = lb;
497     d.nUbound = ub;
498     d.nSize   = ub - lb + 1;
499     m_vDimensions.push_back(d);
500     if( eRes )
501         SetError( eRes );
502 }
503 
504 
AddDim(short lb,short ub)505 void SbxDimArray::AddDim( short lb, short ub )
506 {
507     AddDimImpl32( lb, ub, false );
508 }
509 
unoAddDim(short lb,short ub)510 void SbxDimArray::unoAddDim( short lb, short ub )
511 {
512     AddDimImpl32( lb, ub, true );
513 }
514 
AddDim32(sal_Int32 lb,sal_Int32 ub)515 void SbxDimArray::AddDim32( sal_Int32 lb, sal_Int32 ub )
516 {
517     AddDimImpl32( lb, ub, false );
518 }
519 
unoAddDim32(sal_Int32 lb,sal_Int32 ub)520 void SbxDimArray::unoAddDim32( sal_Int32 lb, sal_Int32 ub )
521 {
522     AddDimImpl32( lb, ub, true );
523 }
524 
525 
526 // Readout dimension data
527 
GetDim32(sal_Int32 n,sal_Int32 & rlb,sal_Int32 & rub) const528 bool SbxDimArray::GetDim32( sal_Int32 n, sal_Int32& rlb, sal_Int32& rub ) const
529 {
530     if( n < 1 || n > static_cast<sal_Int32>(m_vDimensions.size()) )
531     {
532         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
533         rub = rlb = 0;
534         return false;
535     }
536     SbxDim d = m_vDimensions[n - 1];
537     rub = d.nUbound;
538     rlb = d.nLbound;
539     return true;
540 }
541 
GetDim(short n,short & rlb,short & rub) const542 bool SbxDimArray::GetDim( short n, short& rlb, short& rub ) const
543 {
544     sal_Int32 rlb32, rub32;
545     bool bRet = GetDim32( n, rlb32, rub32 );
546     rub = static_cast<short>(rub32);
547     rlb = static_cast<short>(rlb32);
548     if( bRet )
549     {
550         if( rlb32 < -SBX_MAXINDEX || rub32 > SBX_MAXINDEX )
551         {
552             SetError( ERRCODE_BASIC_OUT_OF_RANGE );
553             return false;
554         }
555     }
556     return bRet;
557 }
558 
559 // Element-Ptr with the help of an index list
560 
Offset32(const sal_Int32 * pIdx)561 sal_uInt32 SbxDimArray::Offset32( const sal_Int32* pIdx )
562 {
563     sal_uInt32 nPos = 0;
564     for( const auto& rDimension : m_vDimensions )
565     {
566         sal_Int32 nIdx = *pIdx++;
567         if( nIdx < rDimension.nLbound || nIdx > rDimension.nUbound )
568         {
569             nPos = sal_uInt32(SBX_MAXINDEX32) + 1; break;
570         }
571         nPos = nPos * rDimension.nSize + nIdx - rDimension.nLbound;
572     }
573     if( m_vDimensions.empty() || nPos > SBX_MAXINDEX32 )
574     {
575         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
576         nPos = 0;
577     }
578     return nPos;
579 }
580 
Offset(const short * pIdx)581 sal_uInt16 SbxDimArray::Offset( const short* pIdx )
582 {
583     long nPos = 0;
584     for (auto const& vDimension : m_vDimensions)
585     {
586         short nIdx = *pIdx++;
587         if( nIdx < vDimension.nLbound || nIdx > vDimension.nUbound )
588         {
589             nPos = SBX_MAXINDEX + 1;
590             break;
591         }
592         nPos = nPos * vDimension.nSize + nIdx - vDimension.nLbound;
593     }
594     if( m_vDimensions.empty() || nPos > SBX_MAXINDEX )
595     {
596         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
597         nPos = 0;
598     }
599     return static_cast<sal_uInt16>(nPos);
600 }
601 
Get(const short * pIdx)602 SbxVariable* SbxDimArray::Get( const short* pIdx )
603 {
604     return SbxArray::Get( Offset( pIdx ) );
605 }
606 
Put(SbxVariable * p,const short * pIdx)607 void SbxDimArray::Put( SbxVariable* p, const short* pIdx  )
608 {
609     SbxArray::Put( p, Offset( pIdx ) );
610 }
611 
Get32(const sal_Int32 * pIdx)612 SbxVariable* SbxDimArray::Get32( const sal_Int32* pIdx )
613 {
614     return SbxArray::Get32( Offset32( pIdx ) );
615 }
616 
Put32(SbxVariable * p,const sal_Int32 * pIdx)617 void SbxDimArray::Put32( SbxVariable* p, const sal_Int32* pIdx  )
618 {
619     SbxArray::Put32( p, Offset32( pIdx ) );
620 }
621 
622 // Element-Number with the help of Parameter-Array
Offset32(SbxArray * pPar)623 sal_uInt32 SbxDimArray::Offset32( SbxArray* pPar )
624 {
625 #if HAVE_FEATURE_SCRIPTING
626     if (m_vDimensions.empty() || !pPar ||
627         ((m_vDimensions.size() != sal::static_int_cast<size_t>(pPar->Count() - 1))
628             && SbiRuntime::isVBAEnabled()))
629     {
630         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
631         return 0;
632     }
633 #endif
634     sal_uInt32 nPos = 0;
635     sal_uInt16 nOff = 1;    // Non element 0!
636     for (auto const& vDimension : m_vDimensions)
637     {
638         sal_Int32 nIdx = pPar->Get( nOff++ )->GetLong();
639         if( nIdx < vDimension.nLbound || nIdx > vDimension.nUbound )
640         {
641             nPos = sal_uInt32(SBX_MAXINDEX32)+1;
642             break;
643         }
644         nPos = nPos * vDimension.nSize + nIdx - vDimension.nLbound;
645         if (IsError())
646             break;
647     }
648     if( nPos > sal_uInt32(SBX_MAXINDEX32) )
649     {
650         SetError( ERRCODE_BASIC_OUT_OF_RANGE );
651         nPos = 0;
652     }
653     return nPos;
654 }
655 
Get(SbxArray * pPar)656 SbxVariable* SbxDimArray::Get( SbxArray* pPar )
657 {
658     return SbxArray::Get32( Offset32( pPar ) );
659 }
660 
LoadData(SvStream & rStrm,sal_uInt16 nVer)661 bool SbxDimArray::LoadData( SvStream& rStrm, sal_uInt16 nVer )
662 {
663     short nDimension;
664     rStrm.ReadInt16( nDimension );
665     for( short i = 0; i < nDimension && rStrm.GetError() == ERRCODE_NONE; i++ )
666     {
667         sal_Int16 lb(0), ub(0);
668         rStrm.ReadInt16( lb ).ReadInt16( ub );
669         AddDim( lb, ub );
670     }
671     return SbxArray::LoadData( rStrm, nVer );
672 }
673 
StoreData(SvStream & rStrm) const674 bool SbxDimArray::StoreData( SvStream& rStrm ) const
675 {
676     rStrm.WriteInt16( m_vDimensions.size() );
677     for( short i = 0; i < static_cast<short>(m_vDimensions.size()); i++ )
678     {
679         short lb, ub;
680         GetDim( i, lb, ub );
681         rStrm.WriteInt16( lb ).WriteInt16( ub );
682     }
683     return SbxArray::StoreData( rStrm );
684 }
685 
686 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
687