/* -*- 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 #include #include #include #include #include "sbxres.hxx" #include "sbxconv.hxx" #include #include #include #include #include #include #include #include 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(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 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(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("("); for (SbxParams::const_iterator iter = pInfo->m_Params.begin(); iter != pInfo->m_Params.end(); ++iter) { auto const& i = *iter; int nt = i->eType & 0x0FFF; if (iter != pInfo->m_Params.begin()) { aTmp.append(","); } 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( static_cast( StringId::Types ) + nt ) )); } else { aTmp.append(GetSbxRes( StringId::Any )); } } } aTmp.append(")"); const_cast(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(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; aTmp.eType = aData.eType = static_cast(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(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(cMark) ); } Broadcast( SfxHintId::BasicDataChanged ); nHash = MakeHashCode( maName ); SetModified( true ); return true; } std::pair SbxVariable::StoreData( SvStream& rStrm ) const { rStrm.WriteUChar( 0xFF ); // Marker bool bValStore; if( dynamic_cast(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(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(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(GetValues_Impl().pObj)->Dump( rStrm, bFill ); } else { rStrm << endl; } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */