Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / basic / source / sbx / sbxarray.cxx
blobf8bbafbc64fd64ddb38b7206111dfaefa453851c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <config_features.h>
22 #include <tools/debug.hxx>
23 #include <tools/stream.hxx>
24 #include <basic/sbx.hxx>
25 #include <runtime.hxx>
27 #include <boost/optional.hpp>
29 using namespace std;
31 struct SbxVarEntry
33 SbxVariableRef mpVar;
34 boost::optional<OUString> maAlias;
38 // SbxArray
40 SbxArray::SbxArray( SbxDataType t ) : SbxBase()
42 eType = t;
43 if( t != SbxVARIANT )
44 SetFlag( SbxFlagBits::Fixed );
47 SbxArray& SbxArray::operator=( const SbxArray& rArray )
49 if( &rArray != this )
51 eType = rArray.eType;
52 Clear();
53 for( const auto& rpSrcRef : rArray.mVarEntries )
55 SbxVariableRef pSrc_ = rpSrcRef.mpVar;
56 if( !pSrc_.is() )
57 continue;
59 if( eType != SbxVARIANT )
61 // Convert no objects
62 if( eType != SbxOBJECT || pSrc_->GetClass() != SbxClassType::Object )
64 pSrc_->Convert(eType);
67 mVarEntries.push_back( rpSrcRef );
70 return *this;
73 SbxArray::~SbxArray()
77 SbxDataType SbxArray::GetType() const
79 return static_cast<SbxDataType>( eType | SbxARRAY );
82 void SbxArray::Clear()
84 mVarEntries.clear();
87 sal_uInt32 SbxArray::Count32() const
89 return mVarEntries.size();
92 sal_uInt16 SbxArray::Count() const
94 sal_uInt32 nCount = mVarEntries.size();
95 DBG_ASSERT( nCount <= SBX_MAXINDEX, "SBX: Array-Index > SBX_MAXINDEX" );
96 return static_cast<sal_uInt16>(nCount);
99 SbxVariableRef& SbxArray::GetRef32( sal_uInt32 nIdx )
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 )
106 SetError( ERRCODE_BASIC_OUT_OF_RANGE );
107 nIdx = 0;
109 if ( mVarEntries.size() <= nIdx )
110 mVarEntries.resize(nIdx+1);
112 return mVarEntries[nIdx].mpVar;
115 SbxVariableRef& SbxArray::GetRef( sal_uInt16 nIdx )
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 )
122 SetError( ERRCODE_BASIC_OUT_OF_RANGE );
123 nIdx = 0;
125 if ( mVarEntries.size() <= nIdx )
126 mVarEntries.resize(nIdx+1);
128 return mVarEntries[nIdx].mpVar;
131 SbxVariable* SbxArray::Get32( sal_uInt32 nIdx )
133 if( !CanRead() )
135 SetError( ERRCODE_BASIC_PROP_WRITEONLY );
136 return nullptr;
138 SbxVariableRef& rRef = GetRef32( nIdx );
140 if ( !rRef.is() )
141 rRef = new SbxVariable( eType );
143 return rRef.get();
146 SbxVariable* SbxArray::Get( sal_uInt16 nIdx )
148 if( !CanRead() )
150 SetError( ERRCODE_BASIC_PROP_WRITEONLY );
151 return nullptr;
153 SbxVariableRef& rRef = GetRef( nIdx );
155 if ( !rRef.is() )
156 rRef = new SbxVariable( eType );
158 return rRef.get();
161 void SbxArray::Put32( SbxVariable* pVar, sal_uInt32 nIdx )
163 if( !CanWrite() )
164 SetError( ERRCODE_BASIC_PROP_READONLY );
165 else
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 )
175 rRef = pVar;
176 SetFlag( SbxFlagBits::Modified );
181 void SbxArray::Put( SbxVariable* pVar, sal_uInt16 nIdx )
183 if( !CanWrite() )
184 SetError( ERRCODE_BASIC_PROP_READONLY );
185 else
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 )
198 rRef = pVar;
199 if (!removingMyself)
200 SetFlag( SbxFlagBits::Modified );
205 OUString SbxArray::GetAlias( sal_uInt16 nIdx )
207 if( !CanRead() )
209 SetError( ERRCODE_BASIC_PROP_WRITEONLY );
210 return OUString();
212 SbxVarEntry& rRef = reinterpret_cast<SbxVarEntry&>(GetRef( nIdx ));
214 if (!rRef.maAlias)
215 return OUString();
217 return *rRef.maAlias;
220 void SbxArray::PutAlias( const OUString& rAlias, sal_uInt16 nIdx )
222 if( !CanWrite() )
224 SetError( ERRCODE_BASIC_PROP_READONLY );
226 else
228 SbxVarEntry& rRef = reinterpret_cast<SbxVarEntry&>( GetRef( nIdx ) );
229 rRef.maAlias = rAlias;
233 void SbxArray::Insert32( SbxVariable* pVar, sal_uInt32 nIdx )
235 DBG_ASSERT( mVarEntries.size() <= SBX_MAXINDEX32, "SBX: Array gets too big" );
236 if( mVarEntries.size() > SBX_MAXINDEX32 )
238 return;
240 SbxVarEntry p;
241 p.mpVar = pVar;
242 size_t nSize = mVarEntries.size();
243 if( nIdx > nSize )
245 nIdx = nSize;
247 if( eType != SbxVARIANT && pVar )
249 p.mpVar->Convert(eType);
251 if( nIdx == nSize )
253 mVarEntries.push_back( p );
255 else
257 mVarEntries.insert( mVarEntries.begin() + nIdx, p );
259 SetFlag( SbxFlagBits::Modified );
262 void SbxArray::Insert( SbxVariable* pVar, sal_uInt16 nIdx )
264 DBG_ASSERT( mVarEntries.size() <= 0x3FF0, "SBX: Array gets too big" );
265 if( mVarEntries.size() > 0x3FF0 )
267 return;
269 Insert32( pVar, nIdx );
272 void SbxArray::Remove( sal_uInt32 nIdx )
274 if( nIdx < mVarEntries.size() )
276 mVarEntries.erase( mVarEntries.begin() + nIdx );
277 SetFlag( SbxFlagBits::Modified );
281 void SbxArray::Remove( SbxVariable const * pVar )
283 if( pVar )
285 for( size_t i = 0; i < mVarEntries.size(); i++ )
287 if (mVarEntries[i].mpVar.get() == pVar)
289 Remove( i ); break;
295 // Taking over of the data from the passed array, at which
296 // the variable of the same name will be overwritten.
298 void SbxArray::Merge( SbxArray* p )
300 if (!p)
301 return;
303 for (auto& rEntry1: p->mVarEntries)
305 if (!rEntry1.mpVar.is())
306 continue;
308 OUString aName = rEntry1.mpVar->GetName();
309 sal_uInt16 nHash = rEntry1.mpVar->GetHashCode();
311 // Is the element by the same name already inside?
312 // Then overwrite!
313 for (auto& rEntry2: mVarEntries)
315 if (!rEntry2.mpVar.is())
316 continue;
318 if (rEntry2.mpVar->GetHashCode() == nHash &&
319 rEntry2.mpVar->GetName().equalsIgnoreAsciiCase(aName))
321 // Take this element and clear the original.
322 rEntry2.mpVar = rEntry1.mpVar;
323 rEntry1.mpVar.clear();
324 break;
328 if (rEntry1.mpVar.is())
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);
340 // Search of an element by his name and type. If an element is an object,
341 // it will also be scanned...
343 SbxVariable* SbxArray::Find( const OUString& rName, SbxClassType t )
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)
352 if (!rEntry.mpVar.is() || !rEntry.mpVar->IsVisible())
353 continue;
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)))
361 p = rEntry.mpVar.get();
362 p->ResetFlag(SbxFlagBits::ExtFound);
363 break;
366 // Did we have an array/object with extended search?
367 if (bExtSearch && rEntry.mpVar->IsSet(SbxFlagBits::ExtSearch))
369 switch (rEntry.mpVar->GetClass())
371 case SbxClassType::Object:
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);
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:
388 if (p)
390 p->SetFlag(SbxFlagBits::ExtFound);
391 break;
395 return p;
398 bool SbxArray::LoadData( SvStream& rStrm, sal_uInt16 /*nVer*/ )
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++ )
409 sal_uInt16 nIdx;
410 rStrm.ReadUInt16( nIdx );
411 SbxVariable* pVar = static_cast<SbxVariable*>(Load( rStrm ));
412 if( pVar )
414 SbxVariableRef& rRef = GetRef( nIdx );
415 rRef = pVar;
417 else
419 bRes = false;
420 break;
423 nFlags = f;
424 return bRes;
427 bool SbxArray::StoreData( SvStream& rStrm ) const
429 sal_uInt32 nElem = 0;
430 // Which elements are even defined?
431 for( auto& rEntry: mVarEntries )
433 if (rEntry.mpVar.is() && !(rEntry.mpVar->GetFlags() & SbxFlagBits::DontStore))
434 nElem++;
436 rStrm.WriteUInt16( nElem );
437 for( size_t n = 0; n < mVarEntries.size(); n++ )
439 const SbxVarEntry& rEntry = mVarEntries[n];
440 if (rEntry.mpVar.is() && !(rEntry.mpVar->GetFlags() & SbxFlagBits::DontStore))
442 rStrm.WriteUInt16( n );
443 if (!rEntry.mpVar->Store(rStrm))
444 return false;
447 return true;
450 // #100883 Method to set method directly to parameter array
451 void SbxArray::PutDirect( SbxVariable* pVar, sal_uInt32 nIdx )
453 SbxVariableRef& rRef = GetRef32( nIdx );
454 rRef = pVar;
458 // SbxArray
460 SbxDimArray::SbxDimArray( SbxDataType t ) : SbxArray( t ), mbHasFixedSize( false )
464 SbxDimArray& SbxDimArray::operator=( const SbxDimArray& rArray )
466 if( &rArray != this )
468 SbxArray::operator=( static_cast<const SbxArray&>(rArray) );
469 m_vDimensions = rArray.m_vDimensions;
470 mbHasFixedSize = rArray.mbHasFixedSize;
472 return *this;
475 SbxDimArray::~SbxDimArray()
479 void SbxDimArray::Clear()
481 m_vDimensions.clear();
482 SbxArray::Clear();
485 // Add a dimension
487 void SbxDimArray::AddDimImpl32( sal_Int32 lb, sal_Int32 ub, bool bAllowSize0 )
489 ErrCode eRes = ERRCODE_NONE;
490 if( ub < lb && !bAllowSize0 )
492 eRes = ERRCODE_BASIC_OUT_OF_RANGE;
493 ub = lb;
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 );
505 void SbxDimArray::AddDim( short lb, short ub )
507 AddDimImpl32( lb, ub, false );
510 void SbxDimArray::unoAddDim( short lb, short ub )
512 AddDimImpl32( lb, ub, true );
515 void SbxDimArray::AddDim32( sal_Int32 lb, sal_Int32 ub )
517 AddDimImpl32( lb, ub, false );
520 void SbxDimArray::unoAddDim32( sal_Int32 lb, sal_Int32 ub )
522 AddDimImpl32( lb, ub, true );
526 // Readout dimension data
528 bool SbxDimArray::GetDim32( sal_Int32 n, sal_Int32& rlb, sal_Int32& rub ) const
530 if( n < 1 || n > static_cast<sal_Int32>(m_vDimensions.size()) )
532 SetError( ERRCODE_BASIC_OUT_OF_RANGE );
533 rub = rlb = 0;
534 return false;
536 SbxDim d = m_vDimensions[n - 1];
537 rub = d.nUbound;
538 rlb = d.nLbound;
539 return true;
542 bool SbxDimArray::GetDim( short n, short& rlb, short& rub ) const
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 )
550 if( rlb32 < -SBX_MAXINDEX || rub32 > SBX_MAXINDEX )
552 SetError( ERRCODE_BASIC_OUT_OF_RANGE );
553 return false;
556 return bRet;
559 // Element-Ptr with the help of an index list
561 sal_uInt32 SbxDimArray::Offset32( const sal_Int32* pIdx )
563 sal_uInt32 nPos = 0;
564 for( const auto& rDimension : m_vDimensions )
566 sal_Int32 nIdx = *pIdx++;
567 if( nIdx < rDimension.nLbound || nIdx > rDimension.nUbound )
569 nPos = sal_uInt32(SBX_MAXINDEX32) + 1; break;
571 nPos = nPos * rDimension.nSize + nIdx - rDimension.nLbound;
573 if( m_vDimensions.empty() || nPos > SBX_MAXINDEX32 )
575 SetError( ERRCODE_BASIC_OUT_OF_RANGE );
576 nPos = 0;
578 return nPos;
581 sal_uInt16 SbxDimArray::Offset( const short* pIdx )
583 long nPos = 0;
584 for (auto const& vDimension : m_vDimensions)
586 short nIdx = *pIdx++;
587 if( nIdx < vDimension.nLbound || nIdx > vDimension.nUbound )
589 nPos = SBX_MAXINDEX + 1;
590 break;
592 nPos = nPos * vDimension.nSize + nIdx - vDimension.nLbound;
594 if( m_vDimensions.empty() || nPos > SBX_MAXINDEX )
596 SetError( ERRCODE_BASIC_OUT_OF_RANGE );
597 nPos = 0;
599 return static_cast<sal_uInt16>(nPos);
602 SbxVariable* SbxDimArray::Get( const short* pIdx )
604 return SbxArray::Get( Offset( pIdx ) );
607 void SbxDimArray::Put( SbxVariable* p, const short* pIdx )
609 SbxArray::Put( p, Offset( pIdx ) );
612 SbxVariable* SbxDimArray::Get32( const sal_Int32* pIdx )
614 return SbxArray::Get32( Offset32( pIdx ) );
617 void SbxDimArray::Put32( SbxVariable* p, const sal_Int32* pIdx )
619 SbxArray::Put32( p, Offset32( pIdx ) );
622 // Element-Number with the help of Parameter-Array
623 sal_uInt32 SbxDimArray::Offset32( SbxArray* pPar )
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()))
630 SetError( ERRCODE_BASIC_OUT_OF_RANGE );
631 return 0;
633 #endif
634 sal_uInt32 nPos = 0;
635 sal_uInt16 nOff = 1; // Non element 0!
636 for (auto const& vDimension : m_vDimensions)
638 sal_Int32 nIdx = pPar->Get( nOff++ )->GetLong();
639 if( nIdx < vDimension.nLbound || nIdx > vDimension.nUbound )
641 nPos = sal_uInt32(SBX_MAXINDEX32)+1;
642 break;
644 nPos = nPos * vDimension.nSize + nIdx - vDimension.nLbound;
645 if (IsError())
646 break;
648 if( nPos > sal_uInt32(SBX_MAXINDEX32) )
650 SetError( ERRCODE_BASIC_OUT_OF_RANGE );
651 nPos = 0;
653 return nPos;
656 SbxVariable* SbxDimArray::Get( SbxArray* pPar )
658 return SbxArray::Get32( Offset32( pPar ) );
661 bool SbxDimArray::LoadData( SvStream& rStrm, sal_uInt16 nVer )
663 short nDimension;
664 rStrm.ReadInt16( nDimension );
665 for( short i = 0; i < nDimension && rStrm.GetError() == ERRCODE_NONE; i++ )
667 sal_Int16 lb(0), ub(0);
668 rStrm.ReadInt16( lb ).ReadInt16( ub );
669 AddDim( lb, ub );
671 return SbxArray::LoadData( rStrm, nVer );
674 bool SbxDimArray::StoreData( SvStream& rStrm ) const
676 rStrm.WriteInt16( m_vDimensions.size() );
677 for( short i = 0; i < static_cast<short>(m_vDimensions.size()); i++ )
679 short lb, ub;
680 GetDim( i, lb, ub );
681 rStrm.WriteInt16( lb ).WriteInt16( ub );
683 return SbxArray::StoreData( rStrm );
686 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */