void SbxValue::Clear()
{ switch( aData.eType )
{ case SbxNULL: case SbxEMPTY: case SbxVOID: break; case SbxSTRING: delete aData.pOUString; aData.pOUString = nullptr; break; case SbxOBJECT: if( aData.pObj )
{ if( aData.pObj != this )
{
SAL_INFO("basic.sbx", "Not at Parent-Prop - otherwise CyclicRef");
SbxVariable *pThisVar = dynamic_cast<SbxVariable*>( this ); bool bParentProp = pThisVar && (pThisVar->GetUserData() & 0xFFFF) == 5345; if ( !bParentProp )
aData.pObj->ReleaseRef();
}
aData.pObj = nullptr;
} break; case SbxDECIMAL:
releaseDecimalPtr( aData.pDecimal ); break; case SbxDATAOBJECT:
aData.pData = nullptr; break; default:
{
SbxValues aEmpty;
aEmpty.clear(GetType());
Put( aEmpty );
}
}
}
// Dummy
void SbxValue::Broadcast( SfxHintId )
{}
//////////////////////////// Readout data
// Detect the "right" variables. If it is an object, will be addressed either // the object itself or its default property. // If the variable contain a variable or an object, this will be // addressed.
SbxValue* SbxValue::TheRealValue( bool bObjInObjError ) const
{
SbxValue* p = const_cast<SbxValue*>(this); for( ;; )
{
SbxDataType t = SbxDataType( p->aData.eType & 0x0FFF ); if( t == SbxOBJECT )
{ // The block contains an object or a variable
SbxObject* pObj = dynamic_cast<SbxObject*>( p->aData.pObj ); if( pObj )
{ // Has the object a default property?
SbxVariable* pDflt = pObj->GetDfltProperty();
// If this is an object and contains itself, // we cannot access on it // The old condition to set an error is not correct, // because e.g. a regular variant variable with an object // could be affected if another value should be assigned. // Therefore with flag. if( bObjInObjError && !pDflt && static_cast<SbxValue*>(pObj)->aData.eType == SbxOBJECT && static_cast<SbxValue*>(pObj)->aData.pObj == pObj )
{ #if !HAVE_FEATURE_SCRIPTING constbool bSuccess = false; #else bool bSuccess = handleToStringForCOMObjects( pObj, p ); #endif if( !bSuccess )
{
SetError( ERRCODE_BASIC_BAD_PROP_VALUE );
p = nullptr;
}
} elseif( pDflt )
p = pDflt; break;
} // Did we have an array?
SbxArray* pArray = dynamic_cast<SbxArray*>( p->aData.pObj ); if( pArray )
{ // When indicated get the parameter
SbxArray* pPar = nullptr;
SbxVariable* pVar = dynamic_cast<SbxVariable*>( p ); if( pVar )
pPar = pVar->GetParameters(); if( pPar )
{ // Did we have a dimensioned array?
SbxDimArray* pDimArray = dynamic_cast<SbxDimArray*>( p->aData.pObj ); if( pDimArray )
p = pDimArray->Get( pPar ); else
p = pArray->Get(pPar->Get(1)->GetInteger()); break;
}
} // Otherwise guess a SbxValue
SbxValue* pVal = dynamic_cast<SbxValue*>( p->aData.pObj ); if( pVal )
p = pVal; else break;
} else break;
} return p;
}
bool SbxValue::Get( SbxValues& rRes ) const
{ bool bRes = false;
ErrCode eOld = GetError(); if( eOld != ERRCODE_NONE )
ResetError(); if( !CanRead() )
{
SetError( ERRCODE_BASIC_PROP_WRITEONLY );
rRes.pObj = nullptr;
} else
{ // If an object or a VARIANT is requested, don't search the real values
SbxValue* p = const_cast<SbxValue*>(this); if( rRes.eType != SbxOBJECT && rRes.eType != SbxVARIANT )
p = TheRealValue( true ); if( p )
{
p->Broadcast( SfxHintId::BasicDataWanted ); switch( rRes.eType )
{ case SbxEMPTY: case SbxVOID: case SbxNULL: break; case SbxVARIANT: rRes = p->aData; break; case SbxINTEGER: rRes.nInteger = ImpGetInteger( &p->aData ); break; case SbxLONG: rRes.nLong = ImpGetLong( &p->aData ); break; case SbxSALINT64: rRes.nInt64 = ImpGetInt64( &p->aData ); break; case SbxSALUINT64: rRes.uInt64 = ImpGetUInt64( &p->aData ); break; case SbxSINGLE: rRes.nSingle = ImpGetSingle( &p->aData ); break; case SbxDOUBLE: rRes.nDouble = ImpGetDouble( &p->aData ); break; case SbxCURRENCY:rRes.nInt64 = ImpGetCurrency( &p->aData ); break; case SbxDECIMAL: rRes.pDecimal = ImpGetDecimal( &p->aData ); break; case SbxDATE: rRes.nDouble = ImpGetDate( &p->aData ); break; case SbxBOOL:
rRes.nUShort = sal::static_int_cast< sal_uInt16 >(
ImpGetBool( &p->aData ) ); break; case SbxCHAR: rRes.nChar = ImpGetChar( &p->aData ); break; case SbxBYTE: rRes.nByte = ImpGetByte( &p->aData ); break; case SbxUSHORT: rRes.nUShort = ImpGetUShort( &p->aData ); break; case SbxULONG: rRes.nULong = ImpGetULong( &p->aData ); break; case SbxLPSTR: case SbxSTRING: p->aPic = ImpGetString( &p->aData );
rRes.pOUString = &p->aPic; break; case SbxCoreSTRING: p->aPic = ImpGetCoreString( &p->aData );
rRes.pOUString = &p->aPic; break; case SbxINT:
rRes.nInt = static_cast<int>(ImpGetLong( &p->aData )); break; case SbxUINT:
rRes.nUInt = static_cast<int>(ImpGetULong( &p->aData )); break; case SbxOBJECT: if( p->aData.eType == SbxOBJECT )
rRes.pObj = p->aData.pObj; else
{
SetError( ERRCODE_BASIC_NO_OBJECT );
rRes.pObj = nullptr;
} break; default: if( p->aData.eType == rRes.eType )
rRes = p->aData; else
{
SetError( ERRCODE_BASIC_CONVERSION );
rRes.pObj = nullptr;
}
}
} else
{ // Object contained itself
SbxDataType eTemp = rRes.eType;
rRes.clear(eTemp);
}
} if( !IsError() )
{
bRes = true; if( eOld != ERRCODE_NONE )
SetError( eOld );
} return bRes;
}
// with advanced evaluation (International, "TRUE"/"FALSE") static OUString ImpConvStringExt(const OUString& rSrc, SbxDataType eTargetType)
{ // only special cases are handled, nothing on default switch (eTargetType)
{ // Consider international for floating point. Following default conversion (SbxValue::Put) // assumes internationalized strings, but the input may use standard decimal dot. case SbxSINGLE: case SbxDOUBLE: case SbxCURRENCY:
{
sal_Unicode cDecimalSep, cThousandSep, cDecimalSepAlt;
ImpGetIntntlSep(cDecimalSep, cThousandSep, cDecimalSepAlt);
// 1. If any of the returned decimal separators is dot, do nothing if (cDecimalSep == '.' || cDecimalSepAlt == '.') break;
// 2. If there are internationalized separators already, do nothing if (rSrc.indexOf(cDecimalSep) >= 0 || rSrc.indexOf(cDecimalSepAlt) >= 0) break;
// 3. Replace all dots with the primary separator. This resolves possible ambiguity with // dot as thousand separator, in favor of decimal dot; unlike "only change one dot" // approach, this prevents inconsistency like converting "234.567" to a number with // floating point 234.567, while "1.234.567" to a whole number 1234567. The latter will // be rejected now. return rSrc.replaceAll(".", OUStringChar(cDecimalSep));
}
// check as string in case of sal_Bool sal_True and sal_False case SbxBOOL: if (rSrc.equalsIgnoreAsciiCase("true")) return OUString::number(SbxTRUE); if (rSrc.equalsIgnoreAsciiCase("false")) return OUString::number(SbxFALSE); break;
default: break;
}
return rSrc;
}
// From 1996-03-28: // Method to execute a pretreatment of the strings at special types. // In particular necessary for BASIC-IDE, so that // the output in the Watch-Window can be written back with PutStringExt, // if Float were declared with either '.' or locale-specific decimal // separator, or BOOl explicit with "TRUE" or "FALSE". // Implementation in ImpConvStringExt void SbxValue::PutStringExt( const OUString& r )
{ // Identify the own type (not as in Put() with TheRealValue(), // Objects are not handled anyway)
SbxDataType eTargetType = SbxDataType( aData.eType & 0x0FFF );
OUString aStr(ImpConvStringExt(r, eTargetType));
// tinker a Source-Value
SbxValues aRes(SbxSTRING);
aRes.pOUString = &aStr;
// #34939: For Strings which contain a number, and if this has a Num-Type, // set a Fixed flag so that the type will not be changed
SbxFlagBits nFlags_ = GetFlags(); if( ( eTargetType >= SbxINTEGER && eTargetType <= SbxCURRENCY ) ||
( eTargetType >= SbxCHAR && eTargetType <= SbxUINT ) ||
eTargetType == SbxBOOL )
{
SbxValue aVal;
aVal.Put( aRes ); if( aVal.IsNumeric() )
SetFlag( SbxFlagBits::Fixed );
}
constbool bRet = Put(aRes);
// If FIXED resulted in an error, set it back // (UI-Action should not result in an error, but simply fail) if( !bRet )
ResetError();
bool SbxValue::Convert( SbxDataType eTo )
{
eTo = SbxDataType( eTo & 0x0FFF ); if( ( aData.eType & 0x0FFF ) == eTo ) returntrue; if( !CanWrite() ) returnfalse; if( eTo == SbxVARIANT )
{ // Trial to set the data type to Variant
ResetFlag( SbxFlagBits::Fixed ); if( IsFixed() )
{
SetError( ERRCODE_BASIC_CONVERSION ); returnfalse;
} else returntrue;
} // Converting from null doesn't work. Once null, always null! if( aData.eType == SbxNULL )
{
SetError( ERRCODE_BASIC_CONVERSION ); returnfalse;
}
// Conversion of the data:
SbxValues aNew(eTo); if( Get( aNew ) )
{ // The data type could be converted. It ends here with fixed elements, // because the data had not to be taken over if( !IsFixed() )
{
SetType( eTo );
Put( aNew );
SetModified( true );
} returntrue;
} else returnfalse;
} ////////////////////////////////// Calculating
static sal_Int64 MulAndDiv(sal_Int64 n, sal_Int64 mul, sal_Int64 div)
{ if (div == 0)
{
SbxBase::SetError(ERRCODE_BASIC_ZERODIV); return n;
} auto errorValue = [](sal_Int64 x, sal_Int64 y, sal_Int64 z)
{ constint i = (x < 0 ? -1 : 1) * (y < 0 ? -1 : 1) * (z < 0 ? -1 : 1); return i == 1 ? SAL_MAX_INT64 : SAL_MIN_INT64;
};
sal_Int64 result; // If x * integral part of (mul/div) overflows -> product does not fit if (o3tl::checked_multiply(n, mul / div, result))
{
SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW); return errorValue(n, mul, div);
} if (sal_Int64 mul_frac = mul % div)
{ // can't overflow: mul_frac < div
sal_Int64 result_frac = n / div * mul_frac; if (sal_Int64 x_frac = n % div)
result_frac += x_frac * mul_frac / div; if (o3tl::checked_add(result, result_frac, result))
{
SbxBase::SetError(ERRCODE_BASIC_MATH_OVERFLOW); return errorValue(n, mul, div);
}
} return result;
}
if (rOp.Get(aR) && Get(aL))
{ switch (eOp)
{ case SbxMUL:
aL.nInt64 = MulAndDiv(aL.nInt64, aR.nInt64, CURRENCY_FACTOR); break;
case SbxDIV:
aL.nInt64 = MulAndDiv(aL.nInt64, CURRENCY_FACTOR, aR.nInt64); break;
case SbxPLUS: if (o3tl::checked_add(aL.nInt64, aR.nInt64, aL.nInt64))
SetError(ERRCODE_BASIC_MATH_OVERFLOW); break;
case SbxNEG: // Use subtraction; allows to detect negation of SAL_MIN_INT64
aR.nInt64 = std::exchange(aL.nInt64, 0);
[[fallthrough]]; case SbxMINUS: if (o3tl::checked_sub(aL.nInt64, aR.nInt64, aL.nInt64))
SetError(ERRCODE_BASIC_MATH_OVERFLOW); break;
default:
SetError( ERRCODE_BASIC_BAD_ARGUMENT );
}
}
} else
Lbl_OpIsDouble:
{ // other types and operators including Date, Double and Single
aL.eType = aR.eType = SbxDOUBLE; if( rOp.Get( aR ) )
{ if( Get( aL ) )
{ switch( eOp )
{ case SbxEXP:
aL.nDouble = pow( aL.nDouble, aR.nDouble ); break; case SbxMUL:
aL.nDouble *= aR.nDouble; break; case SbxDIV: if( !aR.nDouble ) SetError( ERRCODE_BASIC_ZERODIV ); else aL.nDouble /= aR.nDouble; break; case SbxPLUS:
aL.nDouble += aR.nDouble; break; case SbxMINUS:
aL.nDouble -= aR.nDouble; break; case SbxNEG:
aL.nDouble = -aL.nDouble; break; default:
SetError( ERRCODE_BASIC_BAD_ARGUMENT );
} // Date with "+" or "-" needs special handling that // forces the Date type. If the operation is '+' the // result is always a Date, if '-' the result is only // a Date if one of lhs or rhs ( but not both ) is already // a Date if( GetType() == SbxDATE || rOp.GetType() == SbxDATE )
{ if( eOp == SbxPLUS || ( ( eOp == SbxMINUS ) && ( GetType() != rOp.GetType() ) ) )
aL.eType = SbxDATE;
}
template <typename T> staticbool CompareNormal(const T& l, const T& r, SbxOperator eOp)
{ switch (eOp)
{ case SbxEQ: return l == r; case SbxNE: return l != r; case SbxLT: return l < r; case SbxGT: return l > r; case SbxLE: return l <= r; case SbxGE: return l >= r; default:
assert(false);
}
SbxBase::SetError(ERRCODE_BASIC_BAD_ARGUMENT); returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.