as the newer format already does The old format path set the new type without clearing an existing value first, so the new type bogusly redefined whatever the union still held. Signed-off-by: Caolán McNamara <caolan.mcnamara@collabora.com> Change-Id: I70d02026d24443ab353be37c0a57685b4e7ac969 Reviewed-on: https://gerrit.collaboraoffice.com/c/online/+/13600 Reviewed-by: Noel Grandin <noel.grandin@collabora.com> Tested-by: Jenkins CPCI <releng@collaboraoffice.com> (cherry picked from commit 27d9427be60d57d3054c01c8806c81b89bd3872d) Reviewed-on: https://gerrit.libreoffice.org/c/core/+/211530 Reviewed-by: Xisco Fauli <xiscofauli@libreoffice.org> Tested-by: Jenkins
609 lines
16 KiB
C++
609 lines
16 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*
|
|
* This file incorporates work covered by the following license notice:
|
|
*
|
|
* Licensed to the Apache Software Foundation (ASF) under one or more
|
|
* contributor license agreements. See the NOTICE file distributed
|
|
* with this work for additional information regarding copyright
|
|
* ownership. The ASF licenses this file to you under the Apache
|
|
* License, Version 2.0 (the "License"); you may not use this file
|
|
* except in compliance with the License. You may obtain a copy of
|
|
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
|
|
*/
|
|
|
|
#include <config_features.h>
|
|
|
|
#include <tools/stream.hxx>
|
|
#include <svl/SfxBroadcaster.hxx>
|
|
|
|
#include <basic/sbx.hxx>
|
|
#include <runtime.hxx>
|
|
#include "sbxres.hxx"
|
|
#include "sbxconv.hxx"
|
|
#include <sbunoobj.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <sal/log.hxx>
|
|
#include <global.hxx>
|
|
#include <unotools/transliterationwrapper.hxx>
|
|
|
|
#include <com/sun/star/uno/XInterface.hpp>
|
|
#include <utility>
|
|
#include <filefmt.hxx>
|
|
using namespace com::sun::star::uno;
|
|
|
|
// SbxVariable
|
|
|
|
SbxVariable::SbxVariable()
|
|
{
|
|
}
|
|
|
|
SbxVariable::SbxVariable( const SbxVariable& r )
|
|
: SvRefBase( r ),
|
|
SbxValue( r ),
|
|
m_aDeclareClassName( r.m_aDeclareClassName ),
|
|
m_xComListener( r.m_xComListener),
|
|
mpPar( r.mpPar ),
|
|
pInfo( r.pInfo )
|
|
{
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
if( r.m_xComListener.is() )
|
|
{
|
|
registerComListenerVariableForBasic( this, r.m_pComListenerParentBasic );
|
|
}
|
|
#endif
|
|
if( r.CanRead() )
|
|
{
|
|
pParent = r.pParent;
|
|
nUserData = r.nUserData;
|
|
maName = r.maName;
|
|
nHash = r.nHash;
|
|
}
|
|
}
|
|
|
|
SbxEnsureParentVariable::SbxEnsureParentVariable(const SbxVariable& r)
|
|
: SbxVariable(r)
|
|
, xParent(const_cast<SbxVariable&>(r).GetParent())
|
|
{
|
|
assert(GetParent() == xParent.get());
|
|
}
|
|
|
|
void SbxEnsureParentVariable::SetParent(SbxObject* p)
|
|
{
|
|
assert(GetParent() == xParent.get());
|
|
SbxVariable::SetParent(p);
|
|
xParent = SbxObjectRef(p);
|
|
assert(GetParent() == xParent.get());
|
|
}
|
|
|
|
SbxVariable::SbxVariable( SbxDataType t ) : SbxValue( t )
|
|
{
|
|
}
|
|
|
|
SbxVariable::~SbxVariable()
|
|
{
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
if( IsSet( SbxFlagBits::DimAsNew ))
|
|
{
|
|
removeDimAsNewRecoverItem( this );
|
|
}
|
|
#endif
|
|
mpBroadcaster.reset();
|
|
}
|
|
|
|
// Broadcasting
|
|
|
|
SfxBroadcaster& SbxVariable::GetBroadcaster()
|
|
{
|
|
if( !mpBroadcaster )
|
|
{
|
|
mpBroadcaster.reset( new SfxBroadcaster );
|
|
}
|
|
return *mpBroadcaster;
|
|
}
|
|
|
|
SbxArray* SbxVariable::GetParameters() const
|
|
{
|
|
return mpPar.get();
|
|
}
|
|
|
|
|
|
// Perhaps some day one could cut the parameter 0.
|
|
// Then the copying will be dropped...
|
|
|
|
void SbxVariable::Broadcast( SfxHintId nHintId )
|
|
{
|
|
if( !mpBroadcaster || IsSet( SbxFlagBits::NoBroadcast ) )
|
|
return;
|
|
|
|
// Because the method could be called from outside, check the
|
|
// rights here again
|
|
if( nHintId == SfxHintId::BasicDataWanted )
|
|
{
|
|
if( !CanRead() )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
if( nHintId == SfxHintId::BasicDataChanged )
|
|
{
|
|
if( !CanWrite() )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
//fdo#86843 Add a ref during the following block to guard against
|
|
//getting deleted before completing this method
|
|
SbxVariableRef aBroadcastGuard(this);
|
|
|
|
// Avoid further broadcasting
|
|
std::unique_ptr<SfxBroadcaster> pSave = std::move(mpBroadcaster);
|
|
SbxFlagBits nSaveFlags = GetFlags();
|
|
SetFlag( SbxFlagBits::ReadWrite );
|
|
if( mpPar.is() )
|
|
{
|
|
// Register this as element 0, but don't change over the parent!
|
|
mpPar->GetRef(0) = this;
|
|
}
|
|
pSave->Broadcast( SbxHint( nHintId, this ) );
|
|
mpBroadcaster = std::move(pSave);
|
|
SetFlags( nSaveFlags );
|
|
}
|
|
|
|
SbxInfo* SbxVariable::GetInfo()
|
|
{
|
|
if( !pInfo.is() )
|
|
{
|
|
Broadcast( SfxHintId::BasicInfoWanted );
|
|
if( pInfo.is() )
|
|
{
|
|
SetModified( true );
|
|
}
|
|
}
|
|
return pInfo.get();
|
|
}
|
|
|
|
void SbxVariable::SetInfo( SbxInfo* p )
|
|
{
|
|
pInfo = p;
|
|
}
|
|
|
|
void SbxVariable::SetParameters( SbxArray* p )
|
|
{
|
|
mpPar = p;
|
|
}
|
|
|
|
|
|
// Name of the variables
|
|
|
|
// static
|
|
OUString SbxVariable::NameToCaseInsensitiveName(const OUString& rName)
|
|
{
|
|
return SbGlobal::GetTransliteration().transliterate(rName, 0, rName.getLength());
|
|
}
|
|
|
|
void SbxVariable::SetName( const OUString& rName )
|
|
{
|
|
maName = rName;
|
|
nHash = MakeHashCode( rName );
|
|
maNameCI.clear();
|
|
}
|
|
|
|
const OUString& SbxVariable::GetName( SbxNameType t ) const
|
|
{
|
|
static const char cSuffixes[] = " %&!#@ $";
|
|
if( t == SbxNameType::NONE )
|
|
{
|
|
return maName;
|
|
}
|
|
if (t == SbxNameType::CaseInsensitive)
|
|
{
|
|
if (maNameCI.isEmpty() && !maName.isEmpty())
|
|
maNameCI = NameToCaseInsensitiveName(maName);
|
|
return maNameCI;
|
|
}
|
|
// Request parameter-information (not for objects)
|
|
const_cast<SbxVariable*>(this)->GetInfo();
|
|
// Append nothing, if it is a simple property (no empty brackets)
|
|
if (!pInfo.is() || (pInfo->m_Params.empty() && GetClass() == SbxClassType::Property))
|
|
{
|
|
return maName;
|
|
}
|
|
OUStringBuffer aTmp( maName );
|
|
if( t == SbxNameType::ShortTypes )
|
|
{
|
|
sal_Unicode cType = ' ';
|
|
// short type? Then fetch it, possible this is 0.
|
|
SbxDataType et = GetType();
|
|
if( et <= SbxSTRING )
|
|
{
|
|
assert(et >= 0 && size_t(et) < std::size(cSuffixes) - 1);
|
|
cType = cSuffixes[ et ];
|
|
}
|
|
if( cType != ' ' )
|
|
{
|
|
aTmp.append(cType);
|
|
}
|
|
}
|
|
aTmp.append("(");
|
|
|
|
bool first = true;
|
|
for (auto const& i : pInfo->m_Params)
|
|
{
|
|
int nt = i->eType & 0x0FFF;
|
|
if (!first)
|
|
{
|
|
aTmp.append(",");
|
|
}
|
|
first = false;
|
|
if( i->nFlags & SbxFlagBits::Optional )
|
|
{
|
|
aTmp.append( GetSbxRes( StringId::Optional ) );
|
|
}
|
|
if( i->eType & SbxBYREF )
|
|
{
|
|
aTmp.append( GetSbxRes( StringId::ByRef ) );
|
|
}
|
|
aTmp.append( i->aName );
|
|
sal_Unicode cType = ' ';
|
|
// short type? Then fetch it, possible this is 0.
|
|
if( t == SbxNameType::ShortTypes )
|
|
{
|
|
if( nt <= SbxSTRING )
|
|
{
|
|
cType = cSuffixes[ nt ];
|
|
}
|
|
}
|
|
if( cType != ' ' )
|
|
{
|
|
aTmp.append(cType);
|
|
if( i->eType & SbxARRAY )
|
|
{
|
|
aTmp.append("()");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( i->eType & SbxARRAY )
|
|
{
|
|
aTmp.append("()");
|
|
}
|
|
// long type?
|
|
aTmp.append(GetSbxRes( StringId::As ));
|
|
if( nt < 32 )
|
|
{
|
|
aTmp.append(GetSbxRes( static_cast<StringId>( static_cast<int>( StringId::Types ) + nt ) ));
|
|
}
|
|
else
|
|
{
|
|
aTmp.append(GetSbxRes( StringId::Any ));
|
|
}
|
|
}
|
|
}
|
|
aTmp.append(")");
|
|
const_cast<SbxVariable*>(this)->aToolString = aTmp.makeStringAndClear();
|
|
return aToolString;
|
|
}
|
|
|
|
// Operators
|
|
|
|
SbxVariable& SbxVariable::operator=( const SbxVariable& r )
|
|
{
|
|
if (this != &r)
|
|
{
|
|
SbxValue::operator=( r );
|
|
// tdf#144353 - copy information about a missing parameter. See SbiRuntime::SetIsMissing.
|
|
// We cannot unconditionally assign the data about a variable because we would overwrite
|
|
// the information about parameters (name, type, flags, and ids). For instance, in the case
|
|
// where a method will be initialized with a literal.
|
|
if (!pInfo)
|
|
pInfo = r.pInfo;
|
|
m_aDeclareClassName = r.m_aDeclareClassName;
|
|
m_xComListener = r.m_xComListener;
|
|
m_pComListenerParentBasic = r.m_pComListenerParentBasic;
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
if( m_xComListener.is() )
|
|
{
|
|
registerComListenerVariableForBasic( this, m_pComListenerParentBasic );
|
|
}
|
|
#endif
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// Conversion
|
|
|
|
SbxDataType SbxVariable::GetType() const
|
|
{
|
|
if( aData.eType == SbxOBJECT )
|
|
{
|
|
return aData.pObj ? aData.pObj->GetType() : SbxOBJECT;
|
|
}
|
|
else if( aData.eType == SbxVARIANT )
|
|
{
|
|
return aData.pObj ? aData.pObj->GetType() : SbxVARIANT;
|
|
}
|
|
else
|
|
{
|
|
return aData.eType;
|
|
}
|
|
}
|
|
|
|
SbxClassType SbxVariable::GetClass() const
|
|
{
|
|
return SbxClassType::Variable;
|
|
}
|
|
|
|
void SbxVariable::SetModified( bool b )
|
|
{
|
|
if( IsSet( SbxFlagBits::NoModify ) )
|
|
{
|
|
return;
|
|
}
|
|
SbxBase::SetModified( b );
|
|
if( pParent && pParent != this ) //??? HotFix: Recursion out here MM
|
|
{
|
|
pParent->SetModified( b );
|
|
}
|
|
}
|
|
|
|
void SbxVariable::SetParent( SbxObject* p )
|
|
{
|
|
#ifdef DBG_UTIL
|
|
// Will the parent of a SbxObject be set?
|
|
if (p && dynamic_cast<SbxObject*>(this))
|
|
{
|
|
// then this had to be a child of the new parent
|
|
bool bFound = false;
|
|
SbxArray *pChildren = p->GetObjects();
|
|
if ( pChildren )
|
|
{
|
|
for (sal_uInt32 nIdx = 0; !bFound && nIdx < pChildren->Count(); ++nIdx)
|
|
{
|
|
bFound = (this == pChildren->Get(nIdx));
|
|
}
|
|
}
|
|
SAL_INFO_IF(
|
|
!bFound, "basic.sbx",
|
|
"dangling: [" << GetName() << "].SetParent([" << p->GetName()
|
|
<< "])");
|
|
}
|
|
#endif
|
|
|
|
pParent = p;
|
|
}
|
|
|
|
const OUString& SbxVariable::GetDeclareClassName() const
|
|
{
|
|
return m_aDeclareClassName;
|
|
}
|
|
|
|
void SbxVariable::SetDeclareClassName( const OUString& rDeclareClassName )
|
|
{
|
|
m_aDeclareClassName = rDeclareClassName;
|
|
}
|
|
|
|
void SbxVariable::SetComListener( const css::uno::Reference< css::uno::XInterface >& xComListener,
|
|
StarBASIC* pParentBasic )
|
|
{
|
|
m_xComListener = xComListener;
|
|
m_pComListenerParentBasic = pParentBasic;
|
|
#if HAVE_FEATURE_SCRIPTING
|
|
registerComListenerVariableForBasic( this, pParentBasic );
|
|
#endif
|
|
}
|
|
|
|
void SbxVariable::ClearComListener()
|
|
{
|
|
m_xComListener.clear();
|
|
}
|
|
|
|
|
|
// Loading/Saving
|
|
|
|
bool SbxVariable::LoadData( SvStream& rStrm, sal_uInt16 nVer )
|
|
{
|
|
sal_uInt8 cMark;
|
|
rStrm.ReadUChar( cMark );
|
|
if( cMark == 0xFF )
|
|
{
|
|
if( !SbxValue::LoadData( rStrm, nVer ) )
|
|
{
|
|
return false;
|
|
}
|
|
maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
sal_uInt32 nTemp;
|
|
rStrm.ReadUInt32( nTemp );
|
|
nUserData = nTemp;
|
|
}
|
|
else
|
|
{
|
|
sal_uInt16 nType;
|
|
rStrm.SeekRel( -1 );
|
|
rStrm.ReadUInt16( nType );
|
|
maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
sal_uInt32 nTemp;
|
|
rStrm.ReadUInt32( nTemp );
|
|
nUserData = nTemp;
|
|
// correction: old methods have instead of SbxNULL now SbxEMPTY
|
|
if( nType == SbxNULL && GetClass() == SbxClassType::Method )
|
|
{
|
|
nType = SbxEMPTY;
|
|
}
|
|
SbxValues aTmp;
|
|
OUString aTmpString;
|
|
OUString aVal;
|
|
SbxValue::Clear();
|
|
aTmp.eType = aData.eType = static_cast<SbxDataType>(nType);
|
|
aTmp.pOUString = &aVal;
|
|
switch( nType )
|
|
{
|
|
case SbxBOOL:
|
|
case SbxERROR:
|
|
case SbxINTEGER:
|
|
rStrm.ReadInt16( aTmp.nInteger ); break;
|
|
case SbxLONG:
|
|
rStrm.ReadInt32( aTmp.nLong ); break;
|
|
case SbxSINGLE:
|
|
{
|
|
// Floats as ASCII
|
|
aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(
|
|
rStrm, RTL_TEXTENCODING_ASCII_US);
|
|
double d;
|
|
SbxDataType t;
|
|
if( ImpScan( aTmpString, d, t, nullptr ) != ERRCODE_NONE || t == SbxDOUBLE )
|
|
{
|
|
aTmp.nSingle = 0;
|
|
return false;
|
|
}
|
|
aTmp.nSingle = static_cast<float>(d);
|
|
break;
|
|
}
|
|
case SbxDATE:
|
|
case SbxDOUBLE:
|
|
{
|
|
// Floats as ASCII
|
|
aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
SbxDataType t;
|
|
if( ImpScan( aTmpString, aTmp.nDouble, t, nullptr ) != ERRCODE_NONE )
|
|
{
|
|
aTmp.nDouble = 0;
|
|
return false;
|
|
}
|
|
break;
|
|
}
|
|
case SbxSTRING:
|
|
aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
break;
|
|
case SbxEMPTY:
|
|
case SbxNULL:
|
|
break;
|
|
default:
|
|
aData.eType = SbxNULL;
|
|
SAL_WARN( "basic.sbx", "Loaded a non-supported data type" );
|
|
return false;
|
|
}
|
|
// putt value
|
|
if( nType != SbxNULL && nType != SbxEMPTY && !Put( aTmp ) )
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
rStrm.ReadUChar( cMark );
|
|
// cMark is also a version number!
|
|
// 1: initial version
|
|
// 2: with nUserData
|
|
if( cMark )
|
|
{
|
|
if( cMark > 2 )
|
|
{
|
|
return false;
|
|
}
|
|
pInfo = new SbxInfo;
|
|
pInfo->LoadData( rStrm, static_cast<sal_uInt16>(cMark) );
|
|
}
|
|
Broadcast( SfxHintId::BasicDataChanged );
|
|
nHash = MakeHashCode( maName );
|
|
SetModified( true );
|
|
return true;
|
|
}
|
|
|
|
std::pair<bool, sal_uInt32> SbxVariable::StoreData( SvStream& rStrm ) const
|
|
{
|
|
rStrm.WriteUChar( 0xFF ); // Marker
|
|
bool bValStore;
|
|
if( dynamic_cast<const SbxMethod *>(this) != nullptr )
|
|
{
|
|
// #50200 Avoid that objects , which during the runtime
|
|
// as return-value are saved in the method as a value were saved
|
|
SbxVariable* pThis = const_cast<SbxVariable*>(this);
|
|
SbxFlagBits nSaveFlags = GetFlags();
|
|
pThis->SetFlag( SbxFlagBits::Write );
|
|
pThis->SbxValue::Clear();
|
|
pThis->SetFlags( nSaveFlags );
|
|
|
|
// So that the method will not be executed in any case!
|
|
// CAST, to avoid const!
|
|
pThis->SetFlag( SbxFlagBits::NoBroadcast );
|
|
bValStore = SbxValue::StoreData( rStrm ).first;
|
|
pThis->ResetFlag( SbxFlagBits::NoBroadcast );
|
|
}
|
|
else
|
|
{
|
|
bValStore = SbxValue::StoreData( rStrm ).first;
|
|
}
|
|
if( !bValStore )
|
|
{
|
|
return { false, 0 };
|
|
}
|
|
write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, maName,
|
|
RTL_TEXTENCODING_ASCII_US);
|
|
rStrm.WriteUInt32( nUserData );
|
|
if( pInfo.is() )
|
|
{
|
|
rStrm.WriteUChar( 2 ); // Version 2: with UserData!
|
|
pInfo->StoreData( rStrm );
|
|
}
|
|
else
|
|
{
|
|
rStrm.WriteUChar( 0 );
|
|
}
|
|
return { true, B_IMG_VERSION_12 };
|
|
}
|
|
|
|
// SbxInfo
|
|
|
|
SbxInfo::SbxInfo()
|
|
: nHelpId(0)
|
|
{}
|
|
|
|
SbxInfo::SbxInfo( OUString a, sal_uInt32 n )
|
|
: aHelpFile(std::move( a )), nHelpId( n )
|
|
{}
|
|
|
|
SbxHint::~SbxHint() = default;
|
|
|
|
void SbxVariable::Dump( SvStream& rStrm, bool bFill )
|
|
{
|
|
OString aBNameStr(OUStringToOString(GetName( SbxNameType::ShortTypes ), RTL_TEXTENCODING_ASCII_US));
|
|
rStrm.WriteOString( "Variable( " )
|
|
.WriteOString( OString::number(reinterpret_cast<sal_IntPtr>(this)) ).WriteOString( "==" )
|
|
.WriteOString( aBNameStr );
|
|
OString aBParentNameStr(OUStringToOString(GetParent()->GetName(), RTL_TEXTENCODING_ASCII_US));
|
|
if ( GetParent() )
|
|
{
|
|
rStrm.WriteOString( " in parent '" ).WriteOString( aBParentNameStr ).WriteOString( "'" );
|
|
}
|
|
else
|
|
{
|
|
rStrm.WriteOString( " no parent" );
|
|
}
|
|
rStrm.WriteOString( " ) " );
|
|
|
|
// output also the object at object-vars
|
|
if ( GetValues_Impl().eType == SbxOBJECT &&
|
|
GetValues_Impl().pObj &&
|
|
GetValues_Impl().pObj != this &&
|
|
GetValues_Impl().pObj != GetParent() )
|
|
{
|
|
rStrm.WriteOString( " contains " );
|
|
static_cast<SbxObject*>(GetValues_Impl().pObj)->Dump( rStrm, bFill );
|
|
}
|
|
else
|
|
{
|
|
rStrm << endl;
|
|
}
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|