xmlsecurity: fix --without-system-nss build
[LibreOffice.git] / basic / source / sbx / sbxvalue.cxx
blob8fac8fd867efe098bd052246d96a2c95fad3cafa
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 <math.h>
23 #include <string_view>
25 #include <osl/diagnose.h>
26 #include <o3tl/float_int_conversion.hxx>
27 #include <o3tl/safeint.hxx>
28 #include <tools/debug.hxx>
29 #include <tools/stream.hxx>
30 #include <sal/log.hxx>
32 #include <basic/sbx.hxx>
33 #include <sbunoobj.hxx>
34 #include "sbxconv.hxx"
35 #include <runtime.hxx>
36 #include <filefmt.hxx>
39 ///////////////////////////// constructors
41 SbxValue::SbxValue()
43 aData.eType = SbxEMPTY;
46 SbxValue::SbxValue( SbxDataType t )
48 int n = t & 0x0FFF;
50 if( n == SbxVARIANT )
51 n = SbxEMPTY;
52 else
53 SetFlag( SbxFlagBits::Fixed );
54 aData.clear(SbxDataType( n ));
57 SbxValue::SbxValue( const SbxValue& r )
58 : SvRefBase( r ), SbxBase( r )
60 if( !r.CanRead() )
62 SetError( ERRCODE_BASIC_PROP_WRITEONLY );
63 if( !IsFixed() )
64 aData.eType = SbxNULL;
66 else
68 const_cast<SbxValue*>(&r)->Broadcast( SfxHintId::BasicDataWanted );
69 aData = r.aData;
70 // Copy pointer, increment references
71 switch( aData.eType )
73 case SbxSTRING:
74 if( aData.pOUString )
75 aData.pOUString = new OUString( *aData.pOUString );
76 break;
77 case SbxOBJECT:
78 if( aData.pObj )
79 aData.pObj->AddFirstRef();
80 break;
81 case SbxDECIMAL:
82 if( aData.pDecimal )
83 aData.pDecimal->addRef();
84 break;
85 default: break;
90 SbxValue& SbxValue::operator=( const SbxValue& r )
92 if( &r != this )
94 if( !CanWrite() )
95 SetError( ERRCODE_BASIC_PROP_READONLY );
96 else
98 // string -> byte array
99 if( IsFixed() && (aData.eType == SbxOBJECT)
100 && aData.pObj && ( aData.pObj->GetType() == (SbxARRAY | SbxBYTE) )
101 && (r.aData.eType == SbxSTRING) )
103 OUString aStr = r.GetOUString();
104 SbxArray* pArr = StringToByteArray(aStr);
105 PutObject(pArr);
106 return *this;
108 // byte array -> string
109 if( r.IsFixed() && (r.aData.eType == SbxOBJECT)
110 && r.aData.pObj && ( r.aData.pObj->GetType() == (SbxARRAY | SbxBYTE) )
111 && (aData.eType == SbxSTRING) )
113 SbxBase* pObj = r.GetObject();
114 SbxArray* pArr = dynamic_cast<SbxArray*>( pObj );
115 if( pArr )
117 OUString aStr = ByteArrayToString( pArr );
118 PutString(aStr);
119 return *this;
122 // Readout the content of the variables
123 SbxValues aNew;
124 if( IsFixed() )
125 // then the type has to match
126 aNew.eType = aData.eType;
127 else if( r.IsFixed() )
128 // Source fixed: copy the type
129 aNew.eType = SbxDataType( r.aData.eType & 0x0FFF );
130 else
131 // both variant: then don't care
132 aNew.eType = SbxVARIANT;
133 if( r.Get( aNew ) )
134 Put( aNew );
137 return *this;
140 SbxValue::~SbxValue()
142 SetFlag( SbxFlagBits::Write );
143 // cid#1486004 silence Uncaught exception
144 suppress_fun_call_w_exception(SbxValue::Clear());
147 void SbxValue::Clear()
149 switch( aData.eType )
151 case SbxNULL:
152 case SbxEMPTY:
153 case SbxVOID:
154 break;
155 case SbxSTRING:
156 delete aData.pOUString; aData.pOUString = nullptr;
157 break;
158 case SbxOBJECT:
159 if( aData.pObj )
161 if( aData.pObj != this )
163 SAL_INFO("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
164 SbxVariable *pThisVar = dynamic_cast<SbxVariable*>( this );
165 bool bParentProp = pThisVar && (pThisVar->GetUserData() & 0xFFFF) == 5345;
166 if ( !bParentProp )
167 aData.pObj->ReleaseRef();
169 aData.pObj = nullptr;
171 break;
172 case SbxDECIMAL:
173 releaseDecimalPtr( aData.pDecimal );
174 break;
175 case SbxDATAOBJECT:
176 aData.pData = nullptr; break;
177 default:
179 SbxValues aEmpty;
180 aEmpty.clear(GetType());
181 Put( aEmpty );
186 // Dummy
188 void SbxValue::Broadcast( SfxHintId )
191 //////////////////////////// Readout data
193 // Detect the "right" variables. If it is an object, will be addressed either
194 // the object itself or its default property.
195 // If the variable contain a variable or an object, this will be
196 // addressed.
198 SbxValue* SbxValue::TheRealValue( bool bObjInObjError ) const
200 SbxValue* p = const_cast<SbxValue*>(this);
201 for( ;; )
203 SbxDataType t = SbxDataType( p->aData.eType & 0x0FFF );
204 if( t == SbxOBJECT )
206 // The block contains an object or a variable
207 SbxObject* pObj = dynamic_cast<SbxObject*>( p->aData.pObj );
208 if( pObj )
210 // Has the object a default property?
211 SbxVariable* pDflt = pObj->GetDfltProperty();
213 // If this is an object and contains itself,
214 // we cannot access on it
215 // The old condition to set an error is not correct,
216 // because e.g. a regular variant variable with an object
217 // could be affected if another value should be assigned.
218 // Therefore with flag.
219 if( bObjInObjError && !pDflt &&
220 static_cast<SbxValue*>(pObj)->aData.eType == SbxOBJECT &&
221 static_cast<SbxValue*>(pObj)->aData.pObj == pObj )
223 #if !HAVE_FEATURE_SCRIPTING
224 const bool bSuccess = false;
225 #else
226 bool bSuccess = handleToStringForCOMObjects( pObj, p );
227 #endif
228 if( !bSuccess )
230 SetError( ERRCODE_BASIC_BAD_PROP_VALUE );
231 p = nullptr;
234 else if( pDflt )
235 p = pDflt;
236 break;
238 // Did we have an array?
239 SbxArray* pArray = dynamic_cast<SbxArray*>( p->aData.pObj );
240 if( pArray )
242 // When indicated get the parameter
243 SbxArray* pPar = nullptr;
244 SbxVariable* pVar = dynamic_cast<SbxVariable*>( p );
245 if( pVar )
246 pPar = pVar->GetParameters();
247 if( pPar )
249 // Did we have a dimensioned array?
250 SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( p->aData.pObj );
251 if( pDimArray )
252 p = pDimArray->Get( pPar );
253 else
254 p = pArray->Get(pPar->Get(1)->GetInteger());
255 break;
258 // Otherwise guess a SbxValue
259 SbxValue* pVal = dynamic_cast<SbxValue*>( p->aData.pObj );
260 if( pVal )
261 p = pVal;
262 else
263 break;
265 else
266 break;
268 return p;
271 bool SbxValue::Get( SbxValues& rRes ) const
273 bool bRes = false;
274 ErrCode eOld = GetError();
275 if( eOld != ERRCODE_NONE )
276 ResetError();
277 if( !CanRead() )
279 SetError( ERRCODE_BASIC_PROP_WRITEONLY );
280 rRes.pObj = nullptr;
282 else
284 // If an object or a VARIANT is requested, don't search the real values
285 SbxValue* p = const_cast<SbxValue*>(this);
286 if( rRes.eType != SbxOBJECT && rRes.eType != SbxVARIANT )
287 p = TheRealValue( true );
288 if( p )
290 p->Broadcast( SfxHintId::BasicDataWanted );
291 switch( rRes.eType )
293 case SbxEMPTY:
294 case SbxVOID:
295 case SbxNULL: break;
296 case SbxVARIANT: rRes = p->aData; break;
297 case SbxINTEGER: rRes.nInteger = ImpGetInteger( &p->aData ); break;
298 case SbxLONG: rRes.nLong = ImpGetLong( &p->aData ); break;
299 case SbxSALINT64: rRes.nInt64 = ImpGetInt64( &p->aData ); break;
300 case SbxSALUINT64: rRes.uInt64 = ImpGetUInt64( &p->aData ); break;
301 case SbxSINGLE: rRes.nSingle = ImpGetSingle( &p->aData ); break;
302 case SbxDOUBLE: rRes.nDouble = ImpGetDouble( &p->aData ); break;
303 case SbxCURRENCY:rRes.nInt64 = ImpGetCurrency( &p->aData ); break;
304 case SbxDECIMAL: rRes.pDecimal = ImpGetDecimal( &p->aData ); break;
305 case SbxDATE: rRes.nDouble = ImpGetDate( &p->aData ); break;
306 case SbxBOOL:
307 rRes.nUShort = sal::static_int_cast< sal_uInt16 >(
308 ImpGetBool( &p->aData ) );
309 break;
310 case SbxCHAR: rRes.nChar = ImpGetChar( &p->aData ); break;
311 case SbxBYTE: rRes.nByte = ImpGetByte( &p->aData ); break;
312 case SbxUSHORT: rRes.nUShort = ImpGetUShort( &p->aData ); break;
313 case SbxULONG: rRes.nULong = ImpGetULong( &p->aData ); break;
314 case SbxLPSTR:
315 case SbxSTRING: p->aPic = ImpGetString( &p->aData );
316 rRes.pOUString = &p->aPic; break;
317 case SbxCoreSTRING: p->aPic = ImpGetCoreString( &p->aData );
318 rRes.pOUString = &p->aPic; break;
319 case SbxINT:
320 rRes.nInt = static_cast<int>(ImpGetLong( &p->aData ));
321 break;
322 case SbxUINT:
323 rRes.nUInt = static_cast<int>(ImpGetULong( &p->aData ));
324 break;
325 case SbxOBJECT:
326 if( p->aData.eType == SbxOBJECT )
327 rRes.pObj = p->aData.pObj;
328 else
330 SetError( ERRCODE_BASIC_NO_OBJECT );
331 rRes.pObj = nullptr;
333 break;
334 default:
335 if( p->aData.eType == rRes.eType )
336 rRes = p->aData;
337 else
339 SetError( ERRCODE_BASIC_CONVERSION );
340 rRes.pObj = nullptr;
344 else
346 // Object contained itself
347 SbxDataType eTemp = rRes.eType;
348 rRes.clear(eTemp);
351 if( !IsError() )
353 bRes = true;
354 if( eOld != ERRCODE_NONE )
355 SetError( eOld );
357 return bRes;
360 SbxValues SbxValue::Get(SbxDataType t) const
362 SbxValues aRes(t);
363 Get(aRes);
364 return aRes;
367 const OUString& SbxValue::GetCoreString() const
369 SbxValues aRes(SbxCoreSTRING);
370 if( Get( aRes ) )
372 const_cast<SbxValue*>(this)->aToolString = *aRes.pOUString;
374 else
376 const_cast<SbxValue*>(this)->aToolString.clear();
378 return aToolString;
381 OUString SbxValue::GetOUString() const
383 OUString aResult;
384 SbxValues aRes(SbxSTRING);
385 if( Get( aRes ) )
387 aResult = *aRes.pOUString;
389 return aResult;
392 //////////////////////////// Write data
394 bool SbxValue::Put( const SbxValues& rVal )
396 bool bRes = false;
397 ErrCode eOld = GetError();
398 if( eOld != ERRCODE_NONE )
399 ResetError();
400 if( !CanWrite() )
401 SetError( ERRCODE_BASIC_PROP_READONLY );
402 else if( rVal.eType & 0xF000 )
403 SetError( ERRCODE_BASIC_BAD_ARGUMENT );
404 else
406 // If an object is requested, don't search the real values
407 SbxValue* p = this;
408 if( rVal.eType != SbxOBJECT )
409 p = TheRealValue( false ); // Don't allow an error here
410 if( p )
412 if( !p->CanWrite() )
413 SetError( ERRCODE_BASIC_PROP_READONLY );
414 else if( p->IsFixed() || p->SetType( static_cast<SbxDataType>( rVal.eType & 0x0FFF ) ) )
415 switch( rVal.eType & 0x0FFF )
417 case SbxEMPTY:
418 case SbxVOID:
419 case SbxNULL: break;
420 case SbxINTEGER: ImpPutInteger( &p->aData, rVal.nInteger ); break;
421 case SbxLONG: ImpPutLong( &p->aData, rVal.nLong ); break;
422 case SbxSALINT64: ImpPutInt64( &p->aData, rVal.nInt64 ); break;
423 case SbxSALUINT64: ImpPutUInt64( &p->aData, rVal.uInt64 ); break;
424 case SbxSINGLE: ImpPutSingle( &p->aData, rVal.nSingle ); break;
425 case SbxDOUBLE: ImpPutDouble( &p->aData, rVal.nDouble ); break;
426 case SbxCURRENCY: ImpPutCurrency( &p->aData, rVal.nInt64 ); break;
427 case SbxDECIMAL: ImpPutDecimal( &p->aData, rVal.pDecimal ); break;
428 case SbxDATE: ImpPutDate( &p->aData, rVal.nDouble ); break;
429 case SbxBOOL: ImpPutBool( &p->aData, rVal.nInteger ); break;
430 case SbxCHAR: ImpPutChar( &p->aData, rVal.nChar ); break;
431 case SbxBYTE: ImpPutByte( &p->aData, rVal.nByte ); break;
432 case SbxUSHORT: ImpPutUShort( &p->aData, rVal.nUShort ); break;
433 case SbxULONG: ImpPutULong( &p->aData, rVal.nULong ); break;
434 case SbxLPSTR:
435 case SbxSTRING: ImpPutString( &p->aData, rVal.pOUString ); break;
436 case SbxINT:
437 ImpPutLong( &p->aData, static_cast<sal_Int32>(rVal.nInt) );
438 break;
439 case SbxUINT:
440 ImpPutULong( &p->aData, static_cast<sal_uInt32>(rVal.nUInt) );
441 break;
442 case SbxOBJECT:
443 if( !p->IsFixed() || p->aData.eType == SbxOBJECT )
445 // is already inside
446 if( p->aData.eType == SbxOBJECT && p->aData.pObj == rVal.pObj )
447 break;
449 // Delete only the value part!
450 p->SbxValue::Clear();
452 // real assignment
453 p->aData.pObj = rVal.pObj;
455 // if necessary increment Ref-Count
456 if( p->aData.pObj && p->aData.pObj != p )
458 if ( p != this )
460 OSL_FAIL( "TheRealValue" );
462 SAL_INFO("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
463 SbxVariable *pThisVar = dynamic_cast<SbxVariable*>( this );
464 bool bParentProp = pThisVar && (pThisVar->GetUserData() & 0xFFFF) == 5345;
465 if ( !bParentProp )
466 p->aData.pObj->AddFirstRef();
469 else
470 SetError( ERRCODE_BASIC_CONVERSION );
471 break;
472 default:
473 if( p->aData.eType == rVal.eType )
474 p->aData = rVal;
475 else
477 SetError( ERRCODE_BASIC_CONVERSION );
478 if( !p->IsFixed() )
479 p->aData.eType = SbxNULL;
482 if( !IsError() )
484 p->SetModified( true );
485 p->Broadcast( SfxHintId::BasicDataChanged );
486 if( eOld != ERRCODE_NONE )
487 SetError( eOld );
488 bRes = true;
492 return bRes;
495 // with advanced evaluation (International, "TRUE"/"FALSE")
496 static OUString ImpConvStringExt(const OUString& rSrc, SbxDataType eTargetType)
498 // only special cases are handled, nothing on default
499 switch (eTargetType)
501 // Consider international for floating point. Following default conversion (SbxValue::Put)
502 // assumes internationalized strings, but the input may use standard decimal dot.
503 case SbxSINGLE:
504 case SbxDOUBLE:
505 case SbxCURRENCY:
507 sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
508 ImpGetIntntlSep(cDecimalSep, cThousandSep, cDecimalSepAlt);
510 // 1. If any of the returned decimal separators is dot, do nothing
511 if (cDecimalSep == '.' || cDecimalSepAlt == '.')
512 break;
514 // 2. If there are internationalized separators already, do nothing
515 if (rSrc.indexOf(cDecimalSep) >= 0 || rSrc.indexOf(cDecimalSepAlt) >= 0)
516 break;
518 // 3. Replace all dots with the primary separator. This resolves possible ambiguity with
519 // dot as thousand separator, in favor of decimal dot; unlike "only change one dot"
520 // approach, this prevents inconsistency like converting "234.567" to a number with
521 // floating point 234.567, while "1.234.567" to a whole number 1234567. The latter will
522 // be rejected now.
523 return rSrc.replaceAll(".", OUStringChar(cDecimalSep));
526 // check as string in case of sal_Bool sal_True and sal_False
527 case SbxBOOL:
528 if (rSrc.equalsIgnoreAsciiCase("true"))
529 return OUString::number(SbxTRUE);
530 if (rSrc.equalsIgnoreAsciiCase("false"))
531 return OUString::number(SbxFALSE);
532 break;
534 default:
535 break;
538 return rSrc;
541 // From 1996-03-28:
542 // Method to execute a pretreatment of the strings at special types.
543 // In particular necessary for BASIC-IDE, so that
544 // the output in the Watch-Window can be written back with PutStringExt,
545 // if Float were declared with either '.' or locale-specific decimal
546 // separator, or BOOl explicit with "TRUE" or "FALSE".
547 // Implementation in ImpConvStringExt
548 void SbxValue::PutStringExt( const OUString& r )
550 // Identify the own type (not as in Put() with TheRealValue(),
551 // Objects are not handled anyway)
552 SbxDataType eTargetType = SbxDataType( aData.eType & 0x0FFF );
553 OUString aStr(ImpConvStringExt(r, eTargetType));
555 // tinker a Source-Value
556 SbxValues aRes(SbxSTRING);
557 aRes.pOUString = &aStr;
559 // #34939: For Strings which contain a number, and if this has a Num-Type,
560 // set a Fixed flag so that the type will not be changed
561 SbxFlagBits nFlags_ = GetFlags();
562 if( ( eTargetType >= SbxINTEGER && eTargetType <= SbxCURRENCY ) ||
563 ( eTargetType >= SbxCHAR && eTargetType <= SbxUINT ) ||
564 eTargetType == SbxBOOL )
566 SbxValue aVal;
567 aVal.Put( aRes );
568 if( aVal.IsNumeric() )
569 SetFlag( SbxFlagBits::Fixed );
572 const bool bRet = Put(aRes);
574 // If FIXED resulted in an error, set it back
575 // (UI-Action should not result in an error, but simply fail)
576 if( !bRet )
577 ResetError();
579 SetFlags( nFlags_ );
582 bool SbxValue::PutBool( bool b )
584 SbxValues aRes(SbxBOOL);
585 aRes.nUShort = sal::static_int_cast< sal_uInt16 >(b ? SbxTRUE : SbxFALSE);
586 return Put(aRes);
589 bool SbxValue::PutEmpty()
591 bool bRet = SetType( SbxEMPTY );
592 SetModified( true );
593 return bRet;
596 void SbxValue::PutNull()
598 bool bRet = SetType( SbxNULL );
599 if( bRet )
600 SetModified( true );
604 // Special decimal methods
605 void SbxValue::PutDecimal( css::bridge::oleautomation::Decimal const & rAutomationDec )
607 SbxValue::Clear();
608 aData.pDecimal = new SbxDecimal( rAutomationDec );
609 aData.pDecimal->addRef();
610 aData.eType = SbxDECIMAL;
613 void SbxValue::fillAutomationDecimal
614 ( css::bridge::oleautomation::Decimal& rAutomationDec ) const
616 SbxDecimal* pDecimal = GetDecimal();
617 if( pDecimal != nullptr )
619 pDecimal->fillAutomationDecimal( rAutomationDec );
624 bool SbxValue::PutString( const OUString& r )
626 SbxValues aRes(SbxSTRING);
627 aRes.pOUString = const_cast<OUString*>(&r);
628 return Put(aRes);
632 #define PUT( p, e, t, m ) \
633 bool SbxValue::p( t n ) \
634 { SbxValues aRes(e); aRes.m = n; return Put(aRes); }
636 void SbxValue::PutDate( double n )
637 { SbxValues aRes(SbxDATE); aRes.nDouble = n; Put( aRes ); }
638 void SbxValue::PutErr( sal_uInt16 n )
639 { SbxValues aRes(SbxERROR); aRes.nUShort = n; Put( aRes ); }
641 PUT( PutByte, SbxBYTE, sal_uInt8, nByte )
642 PUT( PutChar, SbxCHAR, sal_Unicode, nChar )
643 PUT( PutCurrency, SbxCURRENCY, sal_Int64, nInt64 )
644 PUT( PutDouble, SbxDOUBLE, double, nDouble )
645 PUT( PutInteger, SbxINTEGER, sal_Int16, nInteger )
646 PUT( PutLong, SbxLONG, sal_Int32, nLong )
647 PUT( PutObject, SbxOBJECT, SbxBase*, pObj )
648 PUT( PutSingle, SbxSINGLE, float, nSingle )
649 PUT( PutULong, SbxULONG, sal_uInt32, nULong )
650 PUT( PutUShort, SbxUSHORT, sal_uInt16, nUShort )
651 PUT( PutInt64, SbxSALINT64, sal_Int64, nInt64 )
652 PUT( PutUInt64, SbxSALUINT64, sal_uInt64, uInt64 )
653 PUT( PutDecimal, SbxDECIMAL, SbxDecimal*, pDecimal )
655 ////////////////////////// Setting of the data type
657 bool SbxValue::IsFixed() const
659 return (GetFlags() & SbxFlagBits::Fixed) || ((aData.eType & SbxBYREF) != 0);
662 // A variable is numeric, if it is EMPTY or really numeric
663 // or if it contains a complete convertible String
665 // #41692, implement it for RTL and Basic-Core separately
666 bool SbxValue::IsNumeric() const
668 return ImpIsNumeric( /*bOnlyIntntl*/false );
671 bool SbxValue::IsNumericRTL() const
673 return ImpIsNumeric( /*bOnlyIntntl*/true );
676 bool SbxValue::ImpIsNumeric( bool bOnlyIntntl ) const
679 if( !CanRead() )
681 SetError( ERRCODE_BASIC_PROP_WRITEONLY );
682 return false;
684 // Test downcast!!!
685 if( auto pSbxVar = dynamic_cast<const SbxVariable*>( this) )
686 const_cast<SbxVariable*>(pSbxVar)->Broadcast( SfxHintId::BasicDataWanted );
687 SbxDataType t = GetType();
688 if( t == SbxSTRING )
690 if( aData.pOUString )
692 OUString s( *aData.pOUString );
693 double n;
694 SbxDataType t2;
695 sal_Int32 nLen = 0;
696 bool bHasNumber = false;
697 if( ImpScan( s, n, t2, &nLen, &bHasNumber, bOnlyIntntl ) == ERRCODE_NONE )
698 return nLen == s.getLength() && bHasNumber;
700 return false;
702 #if HAVE_FEATURE_SCRIPTING
703 else if (t == SbxBOOL && bOnlyIntntl && SbiRuntime::isVBAEnabled())
704 return true;
705 #endif
706 else
707 return t == SbxEMPTY
708 || ( t >= SbxINTEGER && t <= SbxCURRENCY )
709 || ( t >= SbxCHAR && t <= SbxUINT );
712 SbxDataType SbxValue::GetType() const
714 return SbxDataType( aData.eType & 0x0FFF );
718 bool SbxValue::SetType( SbxDataType t )
720 DBG_ASSERT( !( t & 0xF000 ), "SetType of BYREF|ARRAY is forbidden!" );
721 if( ( t == SbxEMPTY && aData.eType == SbxVOID )
722 || ( aData.eType == SbxEMPTY && t == SbxVOID ) )
723 return true;
724 if( ( t & 0x0FFF ) == SbxVARIANT )
726 // Try to set the data type to Variant
727 ResetFlag( SbxFlagBits::Fixed );
728 if( IsFixed() )
730 SetError( ERRCODE_BASIC_CONVERSION );
731 return false;
733 t = SbxEMPTY;
735 if( ( t & 0x0FFF ) == ( aData.eType & 0x0FFF ) )
736 return true;
738 if( !CanWrite() || IsFixed() )
740 SetError( ERRCODE_BASIC_CONVERSION );
741 return false;
743 else
745 // De-allocate potential objects
746 switch( aData.eType )
748 case SbxSTRING:
749 delete aData.pOUString;
750 break;
751 case SbxOBJECT:
752 if( aData.pObj && aData.pObj != this )
754 SAL_WARN("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
755 SbxVariable *pThisVar = dynamic_cast<SbxVariable*>( this );
756 sal_uInt32 nSlotId = pThisVar
757 ? pThisVar->GetUserData() & 0xFFFF
758 : 0;
759 DBG_ASSERT( nSlotId != 5345 || pThisVar->GetName() == "Parent",
760 "SID_PARENTOBJECT is not named 'Parent'" );
761 bool bParentProp = nSlotId == 5345;
762 if ( !bParentProp )
763 aData.pObj->ReleaseRef();
765 break;
766 default: break;
768 aData.clear(t);
770 return true;
773 bool SbxValue::Convert( SbxDataType eTo )
775 eTo = SbxDataType( eTo & 0x0FFF );
776 if( ( aData.eType & 0x0FFF ) == eTo )
777 return true;
778 if( !CanWrite() )
779 return false;
780 if( eTo == SbxVARIANT )
782 // Trial to set the data type to Variant
783 ResetFlag( SbxFlagBits::Fixed );
784 if( IsFixed() )
786 SetError( ERRCODE_BASIC_CONVERSION );
787 return false;
789 else
790 return true;
792 // Converting from null doesn't work. Once null, always null!
793 if( aData.eType == SbxNULL )
795 SetError( ERRCODE_BASIC_CONVERSION );
796 return false;
799 // Conversion of the data:
800 SbxValues aNew(eTo);
801 if( Get( aNew ) )
803 // The data type could be converted. It ends here with fixed elements,
804 // because the data had not to be taken over
805 if( !IsFixed() )
807 SetType( eTo );
808 Put( aNew );
809 SetModified( true );
811 return true;
813 else
814 return false;
816 ////////////////////////////////// Calculating
818 static sal_Int64 MulAndDiv(sal_Int64 n, sal_Int64 mul, sal_Int64 div)
820 if (div == 0)
822 SbxBase::SetError(ERRCODE_BASIC_ZERODIV);
823 return n;
825 auto errorValue = [](sal_Int64 x, sal_Int64 y, sal_Int64 z)
827 const int i = (x < 0 ? -1 : 1) * (y < 0 ? -1 : 1) * (z < 0 ? -1 : 1);
828 return i == 1 ? SAL_MAX_INT64 : SAL_MIN_INT64;
830 sal_Int64 result;
831 // If x * integral part of (mul/div) overflows -> product does not fit
832 if (o3tl::checked_multiply(n, mul / div, result))
834 SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
835 return errorValue(n, mul, div);
837 if (sal_Int64 mul_frac = mul % div)
839 // can't overflow: mul_frac < div
840 sal_Int64 result_frac = n / div * mul_frac;
841 if (sal_Int64 x_frac = n % div)
842 result_frac += x_frac * mul_frac / div;
843 if (o3tl::checked_add(result, result_frac, result))
845 SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW);
846 return errorValue(n, mul, div);
849 return result;
852 bool SbxValue::Compute( SbxOperator eOp, const SbxValue& rOp )
854 #if !HAVE_FEATURE_SCRIPTING
855 const bool bVBAInterop = false;
856 #else
857 bool bVBAInterop = SbiRuntime::isVBAEnabled();
858 #endif
859 SbxDataType eThisType = GetType();
860 SbxDataType eOpType = rOp.GetType();
861 ErrCode eOld = GetError();
862 if( eOld != ERRCODE_NONE )
863 ResetError();
864 if( !CanWrite() )
865 SetError( ERRCODE_BASIC_PROP_READONLY );
866 else if( !rOp.CanRead() )
867 SetError( ERRCODE_BASIC_PROP_WRITEONLY );
868 // Special rule 1: If one operand is null, the result is null
869 else if( eThisType == SbxNULL || eOpType == SbxNULL )
870 SetType( SbxNULL );
871 else
873 SbxValues aL, aR;
874 bool bDecimal = false;
875 if( bVBAInterop && ( ( eThisType == SbxSTRING && eOpType != SbxSTRING && eOpType != SbxEMPTY ) ||
876 ( eThisType != SbxSTRING && eThisType != SbxEMPTY && eOpType == SbxSTRING ) ) &&
877 ( eOp == SbxMUL || eOp == SbxDIV || eOp == SbxPLUS || eOp == SbxMINUS ) )
879 goto Lbl_OpIsDouble;
881 else if( eThisType == SbxSTRING || eOp == SbxCAT || ( bVBAInterop && ( eOpType == SbxSTRING ) && ( eOp == SbxPLUS ) ) )
883 if( eOp == SbxCAT || eOp == SbxPLUS )
885 // From 1999-11-5, keep OUString in mind
886 aL.eType = aR.eType = SbxSTRING;
887 rOp.Get( aR );
888 // From 1999-12-8, #70399: Here call GetType() again, Get() can change the type!
889 if( rOp.GetType() == SbxEMPTY )
890 goto Lbl_OpIsEmpty; // concatenate empty, *this stays lhs as result
891 Get( aL );
893 // #30576: To begin with test, if the conversion worked
894 if( aL.pOUString != nullptr && aR.pOUString != nullptr )
896 // tdf#108039: catch possible bad_alloc
897 try {
898 *aL.pOUString += *aR.pOUString;
900 catch (const std::bad_alloc&) {
901 SetError(ERRCODE_BASIC_MATH_OVERFLOW);
904 // Not even Left OK?
905 else if( aL.pOUString == nullptr )
907 aL.pOUString = new OUString();
910 else
911 SetError( ERRCODE_BASIC_CONVERSION );
913 else if( eOpType == SbxSTRING && rOp.IsFixed() )
914 { // Numeric: there is no String allowed on the right side
915 SetError( ERRCODE_BASIC_CONVERSION );
916 // falls all the way out
918 else if( ( eOp >= SbxIDIV && eOp <= SbxNOT ) || eOp == SbxMOD )
920 if( GetType() == eOpType )
922 if (GetType() == SbxSALUINT64 || GetType() == SbxSALINT64 || GetType() == SbxULONG)
923 aL.eType = aR.eType = GetType();
924 else if (GetType() == SbxCURRENCY)
925 aL.eType = aR.eType = SbxSALINT64; // Convert to integer value before operation
926 // tdf#145960 - return type of boolean operators should be of type boolean
927 else if ( eOpType == SbxBOOL && eOp != SbxMOD && eOp != SbxIDIV )
928 aL.eType = aR.eType = SbxBOOL;
929 else
930 aL.eType = aR.eType = SbxLONG;
932 else
933 aL.eType = aR.eType = SbxLONG;
935 if (rOp.Get(aR) && Get(aL)) // re-do Get after type assigns above
937 switch( eOp )
939 /* TODO: For SbxEMPTY operands with boolean operators use
940 * the VBA Nothing definition of Comparing Nullable Types?
941 * https://docs.microsoft.com/en-us/dotnet/visual-basic/programming-guide/language-features/data-types/nullable-value-types
943 /* TODO: it is unclear yet whether this also should be done
944 * for the non-bVBAInterop case or not, or at all, consider
945 * user defined spreadsheet functions where an empty cell
946 * is SbxEMPTY and usually is treated as 0 zero or "" empty
947 * string.
949 case SbxIDIV:
950 if( aL.eType == SbxSALUINT64 )
951 if( !aR.uInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
952 else aL.uInt64 /= aR.uInt64;
953 else if( aL.eType == SbxCURRENCY || aL.eType == SbxSALINT64 )
954 if( !aR.nInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
955 else aL.nInt64 /= aR.nInt64;
956 else if( aL.eType == SbxLONG )
957 if( !aR.nLong ) SetError( ERRCODE_BASIC_ZERODIV );
958 else aL.nLong /= aR.nLong;
959 else
960 if( !aR.nULong ) SetError( ERRCODE_BASIC_ZERODIV );
961 else aL.nULong /= aR.nULong;
962 break;
963 case SbxMOD:
964 if( aL.eType == SbxCURRENCY || aL.eType == SbxSALINT64 )
965 if( !aR.nInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
966 else aL.nInt64 %= aR.nInt64;
967 else if( aL.eType == SbxSALUINT64 )
968 if( !aR.uInt64 ) SetError( ERRCODE_BASIC_ZERODIV );
969 else aL.uInt64 %= aR.uInt64;
970 else if( aL.eType == SbxLONG )
971 if( !aR.nLong ) SetError( ERRCODE_BASIC_ZERODIV );
972 else aL.nLong %= aR.nLong;
973 else
974 if( !aR.nULong ) SetError( ERRCODE_BASIC_ZERODIV );
975 else aL.nULong %= aR.nULong;
976 break;
977 case SbxAND:
978 if( aL.eType != SbxLONG && aL.eType != SbxULONG )
979 aL.nInt64 &= aR.nInt64;
980 else
981 aL.nLong &= aR.nLong;
982 break;
983 case SbxOR:
984 if( aL.eType != SbxLONG && aL.eType != SbxULONG )
985 aL.nInt64 |= aR.nInt64;
986 else
987 aL.nLong |= aR.nLong;
988 break;
989 case SbxXOR:
990 if( aL.eType != SbxLONG && aL.eType != SbxULONG )
991 aL.nInt64 ^= aR.nInt64;
992 else
993 aL.nLong ^= aR.nLong;
994 break;
995 case SbxEQV:
996 if( aL.eType != SbxLONG && aL.eType != SbxULONG )
997 aL.nInt64 = (aL.nInt64 & aR.nInt64) | (~aL.nInt64 & ~aR.nInt64);
998 else
999 aL.nLong = (aL.nLong & aR.nLong) | (~aL.nLong & ~aR.nLong);
1000 break;
1001 case SbxIMP:
1002 if( aL.eType != SbxLONG && aL.eType != SbxULONG )
1003 aL.nInt64 = ~aL.nInt64 | aR.nInt64;
1004 else
1005 aL.nLong = ~aL.nLong | aR.nLong;
1006 break;
1007 case SbxNOT:
1008 if( aL.eType != SbxLONG && aL.eType != SbxULONG )
1010 if ( aL.eType != SbxBOOL )
1011 aL.nInt64 = ~aL.nInt64;
1012 else
1013 aL.nLong = ~aL.nLong;
1015 else
1016 aL.nLong = ~aL.nLong;
1017 break;
1018 default: break;
1022 else if( ( GetType() == SbxDECIMAL || rOp.GetType() == SbxDECIMAL )
1023 && ( eOp == SbxMUL || eOp == SbxDIV || eOp == SbxPLUS || eOp == SbxMINUS || eOp == SbxNEG ) )
1025 aL.eType = aR.eType = SbxDECIMAL;
1026 bDecimal = true;
1027 if( rOp.Get( aR ) && Get( aL ) )
1029 if( aL.pDecimal && aR.pDecimal )
1031 bool bOk = true;
1032 switch( eOp )
1034 case SbxMUL:
1035 bOk = ( *(aL.pDecimal) *= *(aR.pDecimal) );
1036 break;
1037 case SbxDIV:
1038 if( aR.pDecimal->isZero() )
1039 SetError( ERRCODE_BASIC_ZERODIV );
1040 else
1041 bOk = ( *(aL.pDecimal) /= *(aR.pDecimal) );
1042 break;
1043 case SbxPLUS:
1044 bOk = ( *(aL.pDecimal) += *(aR.pDecimal) );
1045 break;
1046 case SbxMINUS:
1047 bOk = ( *(aL.pDecimal) -= *(aR.pDecimal) );
1048 break;
1049 case SbxNEG:
1050 bOk = ( aL.pDecimal->neg() );
1051 break;
1052 default:
1053 SetError( ERRCODE_BASIC_BAD_ARGUMENT );
1055 if( !bOk )
1056 SetError( ERRCODE_BASIC_MATH_OVERFLOW );
1058 else
1060 SetError( ERRCODE_BASIC_CONVERSION );
1064 else if( GetType() == SbxCURRENCY || rOp.GetType() == SbxCURRENCY )
1066 aL.eType = aR.eType = SbxCURRENCY;
1068 if (rOp.Get(aR) && Get(aL))
1070 switch (eOp)
1072 case SbxMUL:
1073 aL.nInt64 = MulAndDiv(aL.nInt64, aR.nInt64, CURRENCY_FACTOR);
1074 break;
1076 case SbxDIV:
1077 aL.nInt64 = MulAndDiv(aL.nInt64, CURRENCY_FACTOR, aR.nInt64);
1078 break;
1080 case SbxPLUS:
1081 if (o3tl::checked_add(aL.nInt64, aR.nInt64, aL.nInt64))
1082 SetError(ERRCODE_BASIC_MATH_OVERFLOW);
1083 break;
1085 case SbxNEG:
1086 // Use subtraction; allows to detect negation of SAL_MIN_INT64
1087 aR.nInt64 = std::exchange(aL.nInt64, 0);
1088 [[fallthrough]];
1089 case SbxMINUS:
1090 if (o3tl::checked_sub(aL.nInt64, aR.nInt64, aL.nInt64))
1091 SetError(ERRCODE_BASIC_MATH_OVERFLOW);
1092 break;
1094 default:
1095 SetError( ERRCODE_BASIC_BAD_ARGUMENT );
1099 else
1100 Lbl_OpIsDouble:
1101 { // other types and operators including Date, Double and Single
1102 aL.eType = aR.eType = SbxDOUBLE;
1103 if( rOp.Get( aR ) )
1105 if( Get( aL ) )
1107 switch( eOp )
1109 case SbxEXP:
1110 aL.nDouble = pow( aL.nDouble, aR.nDouble );
1111 break;
1112 case SbxMUL:
1113 aL.nDouble *= aR.nDouble; break;
1114 case SbxDIV:
1115 if( !aR.nDouble ) SetError( ERRCODE_BASIC_ZERODIV );
1116 else aL.nDouble /= aR.nDouble;
1117 break;
1118 case SbxPLUS:
1119 aL.nDouble += aR.nDouble; break;
1120 case SbxMINUS:
1121 aL.nDouble -= aR.nDouble; break;
1122 case SbxNEG:
1123 aL.nDouble = -aL.nDouble; break;
1124 default:
1125 SetError( ERRCODE_BASIC_BAD_ARGUMENT );
1127 // Date with "+" or "-" needs special handling that
1128 // forces the Date type. If the operation is '+' the
1129 // result is always a Date, if '-' the result is only
1130 // a Date if one of lhs or rhs ( but not both ) is already
1131 // a Date
1132 if( GetType() == SbxDATE || rOp.GetType() == SbxDATE )
1134 if( eOp == SbxPLUS || ( ( eOp == SbxMINUS ) && ( GetType() != rOp.GetType() ) ) )
1135 aL.eType = SbxDATE;
1142 if( !IsError() )
1143 Put( aL );
1144 if( bDecimal )
1146 releaseDecimalPtr( aL.pDecimal );
1147 releaseDecimalPtr( aR.pDecimal );
1150 Lbl_OpIsEmpty:
1152 bool bRes = !IsError();
1153 if( bRes && eOld != ERRCODE_NONE )
1154 SetError( eOld );
1155 return bRes;
1158 // The comparison routine deliver TRUE or FALSE.
1160 template <typename T> static bool CompareNormal(const T& l, const T& r, SbxOperator eOp)
1162 switch (eOp)
1164 case SbxEQ:
1165 return l == r;
1166 case SbxNE:
1167 return l != r;
1168 case SbxLT:
1169 return l < r;
1170 case SbxGT:
1171 return l > r;
1172 case SbxLE:
1173 return l <= r;
1174 case SbxGE:
1175 return l >= r;
1176 default:
1177 assert(false);
1179 SbxBase::SetError(ERRCODE_BASIC_BAD_ARGUMENT);
1180 return false;
1183 bool SbxValue::Compare( SbxOperator eOp, const SbxValue& rOp ) const
1185 #if !HAVE_FEATURE_SCRIPTING
1186 const bool bVBAInterop = false;
1187 #else
1188 bool bVBAInterop = SbiRuntime::isVBAEnabled();
1189 #endif
1191 bool bRes = false;
1192 ErrCode eOld = GetError();
1193 if( eOld != ERRCODE_NONE )
1194 ResetError();
1195 if( !CanRead() || !rOp.CanRead() )
1196 SetError( ERRCODE_BASIC_PROP_WRITEONLY );
1197 else if( GetType() == SbxNULL && rOp.GetType() == SbxNULL && !bVBAInterop )
1199 bRes = true;
1201 else if( GetType() == SbxEMPTY && rOp.GetType() == SbxEMPTY )
1202 bRes = !bVBAInterop || ( eOp == SbxEQ );
1203 // Special rule 1: If an operand is null, the result is FALSE
1204 else if( GetType() == SbxNULL || rOp.GetType() == SbxNULL )
1205 bRes = false;
1206 // Special rule 2: If both are variant and one is numeric
1207 // and the other is a String, num is < str
1208 else if( !IsFixed() && !rOp.IsFixed()
1209 && ( rOp.GetType() == SbxSTRING && GetType() != SbxSTRING && IsNumeric() ) && !bVBAInterop
1211 bRes = eOp == SbxLT || eOp == SbxLE || eOp == SbxNE;
1212 else if( !IsFixed() && !rOp.IsFixed()
1213 && ( GetType() == SbxSTRING && rOp.GetType() != SbxSTRING && rOp.IsNumeric() )
1214 && !bVBAInterop
1216 bRes = eOp == SbxGT || eOp == SbxGE || eOp == SbxNE;
1217 else
1219 SbxValues aL, aR;
1220 // If one of the operands is a String,
1221 // a String comparing take place
1222 if( GetType() == SbxSTRING || rOp.GetType() == SbxSTRING )
1224 aL.eType = aR.eType = SbxSTRING;
1225 if (Get(aL) && rOp.Get(aR))
1226 bRes = CompareNormal(*aL.pOUString, *aR.pOUString, eOp);
1228 // From 1995-12-19: If SbxSINGLE participate, then convert to SINGLE,
1229 // otherwise it shows a numeric error
1230 else if( GetType() == SbxSINGLE || rOp.GetType() == SbxSINGLE )
1232 aL.eType = aR.eType = SbxSINGLE;
1233 if( Get( aL ) && rOp.Get( aR ) )
1234 bRes = CompareNormal(aL.nSingle, aR.nSingle, eOp);
1236 else if( GetType() == SbxDECIMAL && rOp.GetType() == SbxDECIMAL )
1238 aL.eType = aR.eType = SbxDECIMAL;
1239 Get( aL );
1240 rOp.Get( aR );
1241 if( aL.pDecimal && aR.pDecimal )
1243 SbxDecimal::CmpResult eRes = compare( *aL.pDecimal, *aR.pDecimal );
1244 switch( eOp )
1246 case SbxEQ:
1247 bRes = ( eRes == SbxDecimal::CmpResult::EQ ); break;
1248 case SbxNE:
1249 bRes = ( eRes != SbxDecimal::CmpResult::EQ ); break;
1250 case SbxLT:
1251 bRes = ( eRes == SbxDecimal::CmpResult::LT ); break;
1252 case SbxGT:
1253 bRes = ( eRes == SbxDecimal::CmpResult::GT ); break;
1254 case SbxLE:
1255 bRes = ( eRes != SbxDecimal::CmpResult::GT ); break;
1256 case SbxGE:
1257 bRes = ( eRes != SbxDecimal::CmpResult::LT ); break;
1258 default:
1259 SetError( ERRCODE_BASIC_BAD_ARGUMENT );
1262 else
1264 SetError( ERRCODE_BASIC_CONVERSION );
1266 releaseDecimalPtr( aL.pDecimal );
1267 releaseDecimalPtr( aR.pDecimal );
1269 else if (GetType() == SbxCURRENCY && rOp.GetType() == SbxCURRENCY)
1271 aL.eType = aR.eType = GetType();
1272 if (Get(aL) && rOp.Get(aR))
1273 bRes = CompareNormal(aL.nInt64, aR.nInt64, eOp);
1275 // Everything else comparing on a SbxDOUBLE-Basis
1276 else
1278 aL.eType = aR.eType = SbxDOUBLE;
1279 bool bGetL = Get( aL );
1280 bool bGetR = rOp.Get( aR );
1281 if( bGetL && bGetR )
1282 bRes = CompareNormal(aL.nDouble, aR.nDouble, eOp);
1283 // at least one value was got
1284 // if this is VBA then a conversion error for one
1285 // side will yield a false result of an equality test
1286 else if ( bGetR || bGetL )
1288 if ( bVBAInterop && eOp == SbxEQ && GetError() == ERRCODE_BASIC_CONVERSION )
1290 #ifndef IOS
1291 ResetError();
1292 bRes = false;
1293 #endif
1298 if( eOld != ERRCODE_NONE )
1299 SetError( eOld );
1300 return bRes;
1303 ///////////////////////////// Reading/Writing
1305 bool SbxValue::LoadData( SvStream& r, sal_uInt16 )
1307 // #TODO see if these types are really dumped to any stream
1308 // more than likely this is functionality used in the binfilter alone
1309 SbxValue::Clear();
1310 sal_uInt16 nType;
1311 r.ReadUInt16( nType );
1312 aData.eType = SbxDataType( nType );
1313 switch( nType )
1315 case SbxBOOL:
1316 case SbxINTEGER:
1317 r.ReadInt16( aData.nInteger ); break;
1318 case SbxLONG:
1319 case SbxDATAOBJECT:
1320 r.ReadInt32( aData.nLong );
1321 break;
1322 case SbxSINGLE:
1324 // Floats as ASCII
1325 OUString aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(r,
1326 RTL_TEXTENCODING_ASCII_US);
1327 double d;
1328 SbxDataType t;
1329 if( ImpScan( aVal, d, t, nullptr ) != ERRCODE_NONE || t == SbxDOUBLE )
1331 aData.nSingle = 0.0F;
1332 return false;
1334 aData.nSingle = static_cast<float>(d);
1335 break;
1337 case SbxDATE:
1338 case SbxDOUBLE:
1340 // Floats as ASCII
1341 OUString aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(r,
1342 RTL_TEXTENCODING_ASCII_US);
1343 SbxDataType t;
1344 if( ImpScan( aVal, aData.nDouble, t, nullptr ) != ERRCODE_NONE )
1346 aData.nDouble = 0.0;
1347 return false;
1349 break;
1351 case SbxSALINT64:
1352 r.ReadInt64(aData.nInt64);
1353 break;
1354 case SbxSALUINT64:
1355 r.ReadUInt64( aData.uInt64 );
1356 break;
1357 case SbxCURRENCY:
1359 sal_uInt32 tmpHi = 0;
1360 sal_uInt32 tmpLo = 0;
1361 r.ReadUInt32( tmpHi ).ReadUInt32( tmpLo );
1362 aData.nInt64 = (static_cast<sal_Int64>(tmpHi) << 32);
1363 aData.nInt64 |= static_cast<sal_Int64>(tmpLo);
1364 break;
1366 case SbxSTRING:
1368 OUString aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(r,
1369 RTL_TEXTENCODING_ASCII_US);
1370 if( !aVal.isEmpty() )
1371 aData.pOUString = new OUString( aVal );
1372 else
1373 aData.pOUString = nullptr; // JSM 1995-09-22
1374 break;
1376 case SbxERROR:
1377 case SbxUSHORT:
1378 r.ReadUInt16( aData.nUShort ); break;
1379 case SbxOBJECT:
1381 sal_uInt8 nMode;
1382 r.ReadUChar( nMode );
1383 switch( nMode )
1385 case 0:
1386 aData.pObj = nullptr;
1387 break;
1388 case 1:
1390 auto ref = SbxBase::Load( r );
1391 aData.pObj = ref.get();
1392 // if necessary increment Ref-Count
1393 if (aData.pObj)
1394 aData.pObj->AddFirstRef();
1395 return ( aData.pObj != nullptr );
1397 case 2:
1398 aData.pObj = this;
1399 break;
1401 break;
1403 case SbxCHAR:
1405 char c;
1406 r.ReadChar( c );
1407 aData.nChar = c;
1408 break;
1410 case SbxBYTE:
1411 r.ReadUChar( aData.nByte ); break;
1412 case SbxULONG:
1413 r.ReadUInt32( aData.nULong ); break;
1414 case SbxINT:
1416 sal_uInt8 n;
1417 r.ReadUChar( n );
1418 // Match the Int on this system?
1419 if( n > SAL_TYPES_SIZEOFINT )
1421 r.ReadInt32( aData.nLong );
1422 aData.eType = SbxLONG;
1424 else {
1425 sal_Int32 nInt;
1426 r.ReadInt32( nInt );
1427 aData.nInt = nInt;
1429 break;
1431 case SbxUINT:
1433 sal_uInt8 n;
1434 r.ReadUChar( n );
1435 // Match the UInt on this system?
1436 if( n > SAL_TYPES_SIZEOFINT )
1438 r.ReadUInt32( aData.nULong );
1439 aData.eType = SbxULONG;
1441 else {
1442 sal_uInt32 nUInt;
1443 r.ReadUInt32( nUInt );
1444 aData.nUInt = nUInt;
1446 break;
1448 case SbxEMPTY:
1449 case SbxNULL:
1450 case SbxVOID:
1451 break;
1452 // #78919 For backwards compatibility
1453 case SbxWSTRING:
1454 case SbxWCHAR:
1455 break;
1456 default:
1457 aData.clear(SbxNULL);
1458 ResetFlag(SbxFlagBits::Fixed);
1459 SAL_WARN( "basic.sbx", "Loaded a non-supported data type" );
1461 return false;
1463 return true;
1466 std::pair<bool, sal_uInt32> SbxValue::StoreData( SvStream& r ) const
1468 sal_uInt16 nType = sal::static_int_cast< sal_uInt16 >(aData.eType);
1469 r.WriteUInt16( nType );
1470 switch( nType & 0x0FFF )
1472 case SbxBOOL:
1473 case SbxINTEGER:
1474 r.WriteInt16( aData.nInteger ); break;
1475 case SbxLONG:
1476 case SbxDATAOBJECT:
1477 r.WriteInt32( aData.nLong );
1478 break;
1479 case SbxDATE:
1480 // #49935: Save as double, otherwise an error during the read in
1481 const_cast<SbxValue*>(this)->aData.eType = static_cast<SbxDataType>( ( nType & 0xF000 ) | SbxDOUBLE );
1482 write_uInt16_lenPrefixed_uInt8s_FromOUString(r, GetCoreString(), RTL_TEXTENCODING_ASCII_US);
1483 const_cast<SbxValue*>(this)->aData.eType = static_cast<SbxDataType>(nType);
1484 break;
1485 case SbxSINGLE:
1486 case SbxDOUBLE:
1487 write_uInt16_lenPrefixed_uInt8s_FromOUString(r, GetCoreString(), RTL_TEXTENCODING_ASCII_US);
1488 break;
1489 case SbxSALUINT64:
1490 case SbxSALINT64:
1491 // see comment in SbxValue::StoreData
1492 r.WriteUInt64( aData.uInt64 );
1493 break;
1494 case SbxCURRENCY:
1496 sal_Int32 tmpHi = ( (aData.nInt64 >> 32) & 0xFFFFFFFF );
1497 sal_Int32 tmpLo = static_cast<sal_Int32>(aData.nInt64);
1498 r.WriteInt32( tmpHi ).WriteInt32( tmpLo );
1499 break;
1501 case SbxSTRING:
1502 if( aData.pOUString )
1504 write_uInt16_lenPrefixed_uInt8s_FromOUString(r, *aData.pOUString, RTL_TEXTENCODING_ASCII_US);
1506 else
1508 write_uInt16_lenPrefixed_uInt8s_FromOUString(r, std::u16string_view(), RTL_TEXTENCODING_ASCII_US);
1510 break;
1511 case SbxERROR:
1512 case SbxUSHORT:
1513 r.WriteUInt16( aData.nUShort ); break;
1514 case SbxOBJECT:
1515 // to save itself as Objectptr does not work!
1516 if( aData.pObj )
1518 if( dynamic_cast<SbxValue*>( aData.pObj) != this )
1520 r.WriteUChar( 1 );
1521 return aData.pObj->Store( r );
1523 else
1524 r.WriteUChar( 2 );
1526 else
1527 r.WriteUChar( 0 );
1528 break;
1529 case SbxCHAR:
1531 char c = sal::static_int_cast< char >(aData.nChar);
1532 r.WriteChar( c );
1533 break;
1535 case SbxBYTE:
1536 r.WriteUChar( aData.nByte ); break;
1537 case SbxULONG:
1538 r.WriteUInt32( aData.nULong ); break;
1539 case SbxINT:
1541 r.WriteUChar( SAL_TYPES_SIZEOFINT ).WriteInt32( aData.nInt );
1542 break;
1544 case SbxUINT:
1546 r.WriteUChar( SAL_TYPES_SIZEOFINT ).WriteUInt32( aData.nUInt );
1547 break;
1549 case SbxEMPTY:
1550 case SbxNULL:
1551 case SbxVOID:
1552 break;
1553 // #78919 For backwards compatibility
1554 case SbxWSTRING:
1555 case SbxWCHAR:
1556 break;
1557 default:
1558 SAL_WARN( "basic.sbx", "Saving a non-supported data type" );
1559 return { false, 0 };
1561 return { true, B_IMG_VERSION_12 };
1564 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */