1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3  * This file is part of the LibreOffice project.
4  *
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/.
8  *
9  * This file incorporates work covered by the following license notice:
10  *
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 .
18  */
19 
20 #include <config_features.h>
21 
22 #include <tools/stream.hxx>
23 #include <svl/SfxBroadcaster.hxx>
24 
25 #include <basic/sbx.hxx>
26 #include <runtime.hxx>
27 #include "sbxres.hxx"
28 #include "sbxconv.hxx"
29 #include <sbunoobj.hxx>
30 #include <rtl/character.hxx>
31 #include <rtl/ustrbuf.hxx>
32 #include <sal/log.hxx>
33 
34 #include <com/sun/star/uno/XInterface.hpp>
35 using namespace com::sun::star::uno;
36 
37 // SbxVariable
38 
SbxVariable()39 SbxVariable::SbxVariable() : SbxValue()
40 {
41 }
42 
SbxVariable(const SbxVariable & r)43 SbxVariable::SbxVariable( const SbxVariable& r )
44     : SvRefBase( r ),
45       SbxValue( r ),
46       m_aDeclareClassName( r.m_aDeclareClassName ),
47       m_xComListener( r.m_xComListener),
48       mpPar( r.mpPar ),
49       pInfo( r.pInfo )
50 {
51 #if HAVE_FEATURE_SCRIPTING
52     if( r.m_xComListener.is() )
53     {
54         registerComListenerVariableForBasic( this, r.m_pComListenerParentBasic );
55     }
56 #endif
57     if( r.CanRead() )
58     {
59         pParent = r.pParent;
60         nUserData = r.nUserData;
61         maName = r.maName;
62         nHash = r.nHash;
63     }
64 }
65 
SbxEnsureParentVariable(const SbxVariable & r)66 SbxEnsureParentVariable::SbxEnsureParentVariable(const SbxVariable& r)
67     : SbxVariable(r)
68     , xParent(const_cast<SbxVariable&>(r).GetParent())
69 {
70     assert(GetParent() == xParent.get());
71 }
72 
SetParent(SbxObject * p)73 void SbxEnsureParentVariable::SetParent(SbxObject* p)
74 {
75     assert(GetParent() == xParent.get());
76     SbxVariable::SetParent(p);
77     xParent = SbxObjectRef(p);
78     assert(GetParent() == xParent.get());
79 }
80 
SbxVariable(SbxDataType t)81 SbxVariable::SbxVariable( SbxDataType t ) : SbxValue( t )
82 {
83 }
84 
~SbxVariable()85 SbxVariable::~SbxVariable()
86 {
87 #if HAVE_FEATURE_SCRIPTING
88     if( IsSet( SbxFlagBits::DimAsNew ))
89     {
90         removeDimAsNewRecoverItem( this );
91     }
92 #endif
93     mpBroadcaster.reset();
94 }
95 
96 // Broadcasting
97 
GetBroadcaster()98 SfxBroadcaster& SbxVariable::GetBroadcaster()
99 {
100     if( !mpBroadcaster )
101     {
102         mpBroadcaster.reset( new SfxBroadcaster );
103     }
104     return *mpBroadcaster;
105 }
106 
GetParameters() const107 SbxArray* SbxVariable::GetParameters() const
108 {
109     return mpPar.get();
110 }
111 
112 
113 // Perhaps some day one could cut the parameter 0.
114 // Then the copying will be dropped...
115 
Broadcast(SfxHintId nHintId)116 void SbxVariable::Broadcast( SfxHintId nHintId )
117 {
118     if( !mpBroadcaster || IsSet( SbxFlagBits::NoBroadcast ) )
119         return;
120 
121     // Because the method could be called from outside, check the
122     // rights here again
123     if( nHintId == SfxHintId::BasicDataWanted )
124     {
125         if( !CanRead() )
126         {
127             return;
128         }
129     }
130     if( nHintId == SfxHintId::BasicDataChanged )
131     {
132         if( !CanWrite() )
133         {
134             return;
135         }
136     }
137 
138     //fdo#86843 Add a ref during the following block to guard against
139     //getting deleted before completing this method
140     SbxVariableRef aBroadcastGuard(this);
141 
142     // Avoid further broadcasting
143     std::unique_ptr<SfxBroadcaster> pSave = std::move(mpBroadcaster);
144     SbxFlagBits nSaveFlags = GetFlags();
145     SetFlag( SbxFlagBits::ReadWrite );
146     if( mpPar.is() )
147     {
148         // Register this as element 0, but don't change over the parent!
149         mpPar->GetRef(0) = this;
150     }
151     pSave->Broadcast( SbxHint( nHintId, this ) );
152     mpBroadcaster = std::move(pSave);
153     SetFlags( nSaveFlags );
154 }
155 
GetInfo()156 SbxInfo* SbxVariable::GetInfo()
157 {
158     if( !pInfo.is() )
159     {
160         Broadcast( SfxHintId::BasicInfoWanted );
161         if( pInfo.is() )
162         {
163             SetModified( true );
164         }
165     }
166     return pInfo.get();
167 }
168 
SetInfo(SbxInfo * p)169 void SbxVariable::SetInfo( SbxInfo* p )
170 {
171     pInfo = p;
172 }
173 
SetParameters(SbxArray * p)174 void SbxVariable::SetParameters( SbxArray* p )
175 {
176     mpPar = p;
177 }
178 
179 
180 // Name of the variables
181 
SetName(const OUString & rName)182 void SbxVariable::SetName( const OUString& rName )
183 {
184     maName = rName;
185     nHash = MakeHashCode( rName );
186 }
187 
GetName(SbxNameType t) const188 const OUString& SbxVariable::GetName( SbxNameType t ) const
189 {
190     static const char cSuffixes[] = "  %&!#@ $";
191     if( t == SbxNameType::NONE )
192     {
193         return maName;
194     }
195     // Request parameter-information (not for objects)
196     const_cast<SbxVariable*>(this)->GetInfo();
197     // Append nothing, if it is a simple property (no empty brackets)
198     if (!pInfo.is() || (pInfo->m_Params.empty() && GetClass() == SbxClassType::Property))
199     {
200         return maName;
201     }
202     sal_Unicode cType = ' ';
203     OUStringBuffer aTmp( maName );
204     // short type? Then fetch it, possible this is 0.
205     SbxDataType et = GetType();
206     if( t == SbxNameType::ShortTypes )
207     {
208         if( et <= SbxSTRING )
209         {
210             cType = cSuffixes[ et ];
211         }
212         if( cType != ' ' )
213         {
214             aTmp.append(cType);
215         }
216     }
217     aTmp.append("(");
218 
219     for (SbxParams::const_iterator iter = pInfo->m_Params.begin(); iter != pInfo->m_Params.end(); ++iter)
220     {
221         auto const& i = *iter;
222         int nt = i->eType & 0x0FFF;
223         if (iter != pInfo->m_Params.begin())
224         {
225             aTmp.append(",");
226         }
227         if( i->nFlags & SbxFlagBits::Optional )
228         {
229             aTmp.append( GetSbxRes( StringId::Optional ) );
230         }
231         if( i->eType & SbxBYREF )
232         {
233             aTmp.append( GetSbxRes( StringId::ByRef ) );
234         }
235         aTmp.append( i->aName );
236         cType = ' ';
237         // short type? Then fetch it, possible this is 0.
238         if( t == SbxNameType::ShortTypes )
239         {
240             if( nt <= SbxSTRING )
241             {
242                 cType = cSuffixes[ nt ];
243             }
244         }
245         if( cType != ' ' )
246         {
247             aTmp.append(cType);
248             if( i->eType & SbxARRAY )
249             {
250                 aTmp.append("()");
251             }
252         }
253         else
254         {
255             if( i->eType & SbxARRAY )
256             {
257                 aTmp.append("()");
258             }
259             // long type?
260             aTmp.append(GetSbxRes( StringId::As ));
261             if( nt < 32 )
262             {
263                 aTmp.append(GetSbxRes( static_cast<StringId>( static_cast<int>( StringId::Types ) + nt ) ));
264             }
265             else
266             {
267                 aTmp.append(GetSbxRes( StringId::Any ));
268             }
269         }
270     }
271     aTmp.append(")");
272     const_cast<SbxVariable*>(this)->aToolString = aTmp.makeStringAndClear();
273     return aToolString;
274 }
275 
276 // Operators
277 
operator =(const SbxVariable & r)278 SbxVariable& SbxVariable::operator=( const SbxVariable& r )
279 {
280     if (this != &r)
281     {
282         SbxValue::operator=( r );
283         m_aDeclareClassName = r.m_aDeclareClassName;
284         m_xComListener = r.m_xComListener;
285         m_pComListenerParentBasic = r.m_pComListenerParentBasic;
286 #if HAVE_FEATURE_SCRIPTING
287         if( m_xComListener.is() )
288         {
289             registerComListenerVariableForBasic( this, m_pComListenerParentBasic );
290         }
291 #endif
292     }
293     return *this;
294 }
295 
296 // Conversion
297 
GetType() const298 SbxDataType SbxVariable::GetType() const
299 {
300     if( aData.eType == SbxOBJECT )
301     {
302         return aData.pObj ? aData.pObj->GetType() : SbxOBJECT;
303     }
304     else if( aData.eType == SbxVARIANT )
305     {
306         return aData.pObj ? aData.pObj->GetType() : SbxVARIANT;
307     }
308     else
309     {
310         return aData.eType;
311     }
312 }
313 
GetClass() const314 SbxClassType SbxVariable::GetClass() const
315 {
316     return SbxClassType::Variable;
317 }
318 
SetModified(bool b)319 void SbxVariable::SetModified( bool b )
320 {
321     if( IsSet( SbxFlagBits::NoModify ) )
322     {
323         return;
324     }
325     SbxBase::SetModified( b );
326     if( pParent && pParent != this ) //??? HotFix: Recursion out here MM
327     {
328         pParent->SetModified( b );
329     }
330 }
331 
SetParent(SbxObject * p)332 void SbxVariable::SetParent( SbxObject* p )
333 {
334 #ifdef DBG_UTIL
335     // Will the parent of a SbxObject be set?
336     if (p && dynamic_cast<SbxObject*>(this))
337     {
338         // then this had to be a child of the new parent
339         bool bFound = false;
340         SbxArray *pChildren = p->GetObjects();
341         if ( pChildren )
342         {
343             for (sal_uInt32 nIdx = 0; !bFound && nIdx < pChildren->Count(); ++nIdx)
344             {
345                 bFound = (this == pChildren->Get(nIdx));
346             }
347         }
348         SAL_INFO_IF(
349             !bFound, "basic.sbx",
350             "dangling: [" << GetName() << "].SetParent([" << p->GetName()
351                 << "])");
352     }
353 #endif
354 
355     pParent = p;
356 }
357 
GetDeclareClassName() const358 const OUString& SbxVariable::GetDeclareClassName() const
359 {
360     return m_aDeclareClassName;
361 }
362 
SetDeclareClassName(const OUString & rDeclareClassName)363 void SbxVariable::SetDeclareClassName( const OUString& rDeclareClassName )
364 {
365     m_aDeclareClassName = rDeclareClassName;
366 }
367 
SetComListener(const css::uno::Reference<css::uno::XInterface> & xComListener,StarBASIC * pParentBasic)368 void SbxVariable::SetComListener( const css::uno::Reference< css::uno::XInterface >& xComListener,
369                                   StarBASIC* pParentBasic )
370 {
371     m_xComListener = xComListener;
372     m_pComListenerParentBasic = pParentBasic;
373 #if HAVE_FEATURE_SCRIPTING
374     registerComListenerVariableForBasic( this, pParentBasic );
375 #endif
376 }
377 
ClearComListener()378 void SbxVariable::ClearComListener()
379 {
380     m_xComListener.clear();
381 }
382 
383 
384 // Loading/Saving
385 
LoadData(SvStream & rStrm,sal_uInt16 nVer)386 bool SbxVariable::LoadData( SvStream& rStrm, sal_uInt16 nVer )
387 {
388     sal_uInt8 cMark;
389     rStrm.ReadUChar( cMark );
390     if( cMark == 0xFF )
391     {
392         if( !SbxValue::LoadData( rStrm, nVer ) )
393         {
394             return false;
395         }
396         maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
397                                                                 RTL_TEXTENCODING_ASCII_US);
398         sal_uInt32 nTemp;
399         rStrm.ReadUInt32( nTemp );
400         nUserData = nTemp;
401     }
402     else
403     {
404         sal_uInt16 nType;
405         rStrm.SeekRel( -1 );
406         rStrm.ReadUInt16( nType );
407         maName = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
408                                                                 RTL_TEXTENCODING_ASCII_US);
409         sal_uInt32 nTemp;
410         rStrm.ReadUInt32( nTemp );
411         nUserData = nTemp;
412         // correction: old methods have instead of SbxNULL now SbxEMPTY
413         if( nType == SbxNULL && GetClass() == SbxClassType::Method )
414         {
415             nType = SbxEMPTY;
416         }
417         SbxValues aTmp;
418         OUString aTmpString;
419         OUString aVal;
420         aTmp.eType = aData.eType = static_cast<SbxDataType>(nType);
421         aTmp.pOUString = &aVal;
422         switch( nType )
423         {
424         case SbxBOOL:
425         case SbxERROR:
426         case SbxINTEGER:
427             rStrm.ReadInt16( aTmp.nInteger ); break;
428         case SbxLONG:
429             rStrm.ReadInt32( aTmp.nLong ); break;
430         case SbxSINGLE:
431         {
432             // Floats as ASCII
433             aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(
434                     rStrm, RTL_TEXTENCODING_ASCII_US);
435             double d;
436             SbxDataType t;
437             if( ImpScan( aTmpString, d, t, nullptr, true ) != ERRCODE_NONE || t == SbxDOUBLE )
438             {
439                 aTmp.nSingle = 0;
440                 return false;
441             }
442             aTmp.nSingle = static_cast<float>(d);
443             break;
444         }
445         case SbxDATE:
446         case SbxDOUBLE:
447         {
448             // Floats as ASCII
449             aTmpString = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
450                                                                         RTL_TEXTENCODING_ASCII_US);
451             SbxDataType t;
452             if( ImpScan( aTmpString, aTmp.nDouble, t, nullptr, true ) != ERRCODE_NONE )
453             {
454                 aTmp.nDouble = 0;
455                 return false;
456             }
457             break;
458         }
459         case SbxSTRING:
460             aVal = read_uInt16_lenPrefixed_uInt8s_ToOUString(rStrm,
461                                                                   RTL_TEXTENCODING_ASCII_US);
462             break;
463         case SbxEMPTY:
464         case SbxNULL:
465             break;
466         default:
467             aData.eType = SbxNULL;
468             SAL_WARN( "basic.sbx", "Loaded a non-supported data type" );
469             return false;
470         }
471         // putt value
472         if( nType != SbxNULL && nType != SbxEMPTY && !Put( aTmp ) )
473         {
474             return false;
475         }
476     }
477     rStrm.ReadUChar( cMark );
478     // cMark is also a version number!
479     // 1: initial version
480     // 2: with nUserData
481     if( cMark )
482     {
483         if( cMark > 2 )
484         {
485             return false;
486         }
487         pInfo = new SbxInfo;
488         pInfo->LoadData( rStrm, static_cast<sal_uInt16>(cMark) );
489     }
490     Broadcast( SfxHintId::BasicDataChanged );
491     nHash =  MakeHashCode( maName );
492     SetModified( true );
493     return true;
494 }
495 
StoreData(SvStream & rStrm) const496 bool SbxVariable::StoreData( SvStream& rStrm ) const
497 {
498     rStrm.WriteUChar( 0xFF );      // Marker
499     bool bValStore;
500     if( dynamic_cast<const SbxMethod *>(this) != nullptr )
501     {
502         // #50200 Avoid that objects , which during the runtime
503         // as return-value are saved in the method as a value were saved
504         SbxVariable* pThis = const_cast<SbxVariable*>(this);
505         SbxFlagBits nSaveFlags = GetFlags();
506         pThis->SetFlag( SbxFlagBits::Write );
507         pThis->SbxValue::Clear();
508         pThis->SetFlags( nSaveFlags );
509 
510         // So that the method will not be executed in any case!
511         // CAST, to avoid const!
512         pThis->SetFlag( SbxFlagBits::NoBroadcast );
513         bValStore = SbxValue::StoreData( rStrm );
514         pThis->ResetFlag( SbxFlagBits::NoBroadcast );
515     }
516     else
517     {
518         bValStore = SbxValue::StoreData( rStrm );
519     }
520     if( !bValStore )
521     {
522         return false;
523     }
524     write_uInt16_lenPrefixed_uInt8s_FromOUString(rStrm, maName,
525                                                       RTL_TEXTENCODING_ASCII_US);
526     rStrm.WriteUInt32( nUserData );
527     if( pInfo.is() )
528     {
529         rStrm.WriteUChar( 2 );     // Version 2: with UserData!
530         pInfo->StoreData( rStrm );
531     }
532     else
533     {
534         rStrm.WriteUChar( 0 );
535     }
536     return true;
537 }
538 
539 // SbxInfo
540 
SbxInfo()541 SbxInfo::SbxInfo()
542         : aHelpFile(), nHelpId(0)
543 {}
544 
SbxInfo(const OUString & r,sal_uInt32 n)545 SbxInfo::SbxInfo( const OUString& r, sal_uInt32 n )
546        : aHelpFile( r ), nHelpId( n )
547 {}
548 
Dump(SvStream & rStrm,bool bFill)549 void SbxVariable::Dump( SvStream& rStrm, bool bFill )
550 {
551     OString aBNameStr(OUStringToOString(GetName( SbxNameType::ShortTypes ), RTL_TEXTENCODING_ASCII_US));
552     rStrm.WriteCharPtr( "Variable( " )
553          .WriteOString( OString::number(reinterpret_cast<sal_Int64>(this)) ).WriteCharPtr( "==" )
554          .WriteOString( aBNameStr );
555     OString aBParentNameStr(OUStringToOString(GetParent()->GetName(), RTL_TEXTENCODING_ASCII_US));
556     if ( GetParent() )
557     {
558         rStrm.WriteCharPtr( " in parent '" ).WriteOString( aBParentNameStr ).WriteCharPtr( "'" );
559     }
560     else
561     {
562         rStrm.WriteCharPtr( " no parent" );
563     }
564     rStrm.WriteCharPtr( " ) " );
565 
566     // output also the object at object-vars
567     if ( GetValues_Impl().eType == SbxOBJECT &&
568             GetValues_Impl().pObj &&
569             GetValues_Impl().pObj != this &&
570             GetValues_Impl().pObj != GetParent() )
571     {
572         rStrm.WriteCharPtr( " contains " );
573         static_cast<SbxObject*>(GetValues_Impl().pObj)->Dump( rStrm, bFill );
574     }
575     else
576     {
577         rStrm << endl;
578     }
579 }
580 
581 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
582