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