xref: /core/basic/source/classes/sb.cxx (revision 4dc40659)
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 <sb.hxx>
21 #include <rtl/ustrbuf.hxx>
22 #include <tools/stream.hxx>
23 #include <tools/debug.hxx>
24 #include <vcl/errinf.hxx>
25 #include <comphelper/solarmutex.hxx>
26 #include <basic/sbx.hxx>
27 #include <vcl/svapp.hxx>
28 #include <comphelper/processfactory.hxx>
29 #include <image.hxx>
30 #include <sbunoobj.hxx>
31 #include <sbjsmeth.hxx>
32 #include <sbjsmod.hxx>
33 #include <sbintern.hxx>
34 #include <runtime.hxx>
35 #include <basic/sberrors.hxx>
36 #include <basic/sbuno.hxx>
37 #include <sbprop.hxx>
38 #include <sbobjmod.hxx>
39 #include <stdobj.hxx>
40 #include <basic.hrc>
41 #include <cppuhelper/implbase.hxx>
42 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
43 #include <com/sun/star/util/XCloseBroadcaster.hpp>
44 #include <com/sun/star/util/XCloseListener.hpp>
45 #include <sal/log.hxx>
46 #include <errobject.hxx>
47 #include <memory>
48 #include <unordered_map>
49 
50 #include <com/sun/star/script/ModuleType.hpp>
51 #include <com/sun/star/script/ModuleInfo.hpp>
52 
53 using namespace ::com::sun::star::script;
54 
55 constexpr OUStringLiteral SB_RTLNAME = u"@SBRTL";
56 //  i#i68894#
57 using namespace ::com::sun::star;
58 using namespace ::com::sun::star::uno;
59 using com::sun::star::uno::Reference;
60 using com::sun::star::uno::Any;
61 using com::sun::star::uno::UNO_QUERY;
62 using com::sun::star::lang::XMultiServiceFactory;
63 
64 
65 class DocBasicItem : public ::cppu::WeakImplHelper< util::XCloseListener >
66 {
67 public:
68     explicit DocBasicItem( StarBASIC& rDocBasic );
69     virtual ~DocBasicItem() override;
70 
71     const SbxObjectRef& getClassModules() const { return mxClassModules; }
72     bool isDocClosed() const { return mbDocClosed; }
73 
74     void clearDependingVarsOnDelete( StarBASIC& rDeletedBasic );
75 
76     void startListening();
77     void stopListening();
78 
79     void setDisposed( bool bDisposed )
80     {
81         mbDisposed = bDisposed;
82     }
83 
84     virtual void SAL_CALL queryClosing( const lang::EventObject& rSource, sal_Bool bGetsOwnership ) override;
85     virtual void SAL_CALL notifyClosing( const lang::EventObject& rSource ) override;
86     virtual void SAL_CALL disposing( const lang::EventObject& rSource ) override;
87 
88 private:
89     StarBASIC&      mrDocBasic;
90     SbxObjectRef    mxClassModules;
91     bool            mbDocClosed;
92     bool            mbDisposed;
93 };
94 
95 
96 DocBasicItem::DocBasicItem( StarBASIC& rDocBasic ) :
97     mrDocBasic( rDocBasic ),
98     mxClassModules( new SbxObject( OUString() ) ),
99     mbDocClosed( false ),
100     mbDisposed( false )
101 {
102 }
103 
104 DocBasicItem::~DocBasicItem()
105 {
106     // tdf#90969 HACK: don't use SolarMutexGuard - there is a horrible global
107     // map GaDocBasicItems holding instances, and these get deleted from exit
108     // handlers, when the SolarMutex is already dead
109     comphelper::SolarMutex *pSolarMutex = comphelper::SolarMutex::get();
110     if ( pSolarMutex )
111         pSolarMutex->acquire();
112 
113     try
114     {
115         stopListening();
116         mxClassModules.clear(); // release with SolarMutex locked
117     }
118     catch (...)
119     {
120         assert(false);
121     }
122 
123     pSolarMutex = comphelper::SolarMutex::get();
124     if ( pSolarMutex )
125         pSolarMutex->release();
126 }
127 
128 void DocBasicItem::clearDependingVarsOnDelete( StarBASIC& rDeletedBasic )
129 {
130     mrDocBasic.implClearDependingVarsOnDelete( &rDeletedBasic );
131 }
132 
133 void DocBasicItem::startListening()
134 {
135     Any aThisComp;
136     mrDocBasic.GetUNOConstant( "ThisComponent", aThisComp );
137     Reference< util::XCloseBroadcaster > xCloseBC( aThisComp, UNO_QUERY );
138     mbDisposed = !xCloseBC.is();
139     if( xCloseBC.is() )
140     {
141         try { xCloseBC->addCloseListener( this ); } catch(const uno::Exception& ) {}
142     }
143 }
144 
145 void DocBasicItem::stopListening()
146 {
147     if( mbDisposed ) return;
148     mbDisposed = true;
149     Any aThisComp;
150     if (!mrDocBasic.GetUNOConstant("ThisComponent", aThisComp))
151         return;
152 
153     Reference< util::XCloseBroadcaster > xCloseBC( aThisComp, UNO_QUERY );
154     if( xCloseBC.is() )
155     {
156         try { xCloseBC->removeCloseListener( this ); } catch(const uno::Exception& ) {}
157     }
158 }
159 
160 void SAL_CALL DocBasicItem::queryClosing( const lang::EventObject& /*rSource*/, sal_Bool /*bGetsOwnership*/ )
161 {
162 }
163 
164 void SAL_CALL DocBasicItem::notifyClosing( const lang::EventObject& /*rEvent*/ )
165 {
166     stopListening();
167     mbDocClosed = true;
168 }
169 
170 void SAL_CALL DocBasicItem::disposing( const lang::EventObject& /*rEvent*/ )
171 {
172     stopListening();
173 }
174 
175 
176 namespace {
177 
178 typedef ::rtl::Reference< DocBasicItem > DocBasicItemRef;
179 
180 std::unordered_map< const StarBASIC *, DocBasicItemRef > gaDocBasicItems;
181 
182 const DocBasicItem* lclFindDocBasicItem( const StarBASIC* pDocBasic )
183 {
184     auto it = gaDocBasicItems.find( pDocBasic );
185     auto end = gaDocBasicItems.end();
186     return (it != end) ? it->second.get() : nullptr;
187 }
188 
189 void lclInsertDocBasicItem( StarBASIC& rDocBasic )
190 {
191     DocBasicItemRef& rxDocBasicItem = gaDocBasicItems[ &rDocBasic ];
192     rxDocBasicItem.set( new DocBasicItem( rDocBasic ) );
193     rxDocBasicItem->startListening();
194 }
195 
196 void lclRemoveDocBasicItem( StarBASIC& rDocBasic )
197 {
198     auto it = gaDocBasicItems.find( &rDocBasic );
199     if( it != gaDocBasicItems.end() )
200     {
201         it->second->stopListening();
202         gaDocBasicItems.erase( it );
203     }
204     for( auto& rEntry : gaDocBasicItems )
205     {
206         rEntry.second->clearDependingVarsOnDelete( rDocBasic );
207     }
208 }
209 
210 StarBASIC* lclGetDocBasicForModule( SbModule* pModule )
211 {
212     StarBASIC* pRetBasic = nullptr;
213     SbxObject* pCurParent = pModule;
214     while( pCurParent->GetParent() != nullptr )
215     {
216         pCurParent = pCurParent->GetParent();
217         StarBASIC* pDocBasic = dynamic_cast<StarBASIC*>( pCurParent  );
218         if( pDocBasic != nullptr && pDocBasic->IsDocBasic() )
219         {
220             pRetBasic = pDocBasic;
221             break;
222         }
223     }
224     return pRetBasic;
225 }
226 
227 } // namespace
228 
229 
230 SbxObject* StarBASIC::getVBAGlobals( )
231 {
232     if ( !pVBAGlobals.is() )
233     {
234         Any aThisDoc;
235         if ( GetUNOConstant("ThisComponent", aThisDoc) )
236         {
237             Reference< XMultiServiceFactory > xDocFac( aThisDoc, UNO_QUERY );
238             if ( xDocFac.is() )
239             {
240                 try
241                 {
242                     xDocFac->createInstance("ooo.vba.VBAGlobals");
243                 }
244                 catch(const Exception& )
245                 {
246                     // Ignore
247                 }
248             }
249         }
250         pVBAGlobals = static_cast<SbUnoObject*>(Find( "VBAGlobals" , SbxClassType::DontCare ));
251     }
252     return pVBAGlobals.get();
253 }
254 
255 //  i#i68894#
256 SbxVariable* StarBASIC::VBAFind( const OUString& rName, SbxClassType t )
257 {
258     if( rName == "ThisComponent" )
259     {
260         return nullptr;
261     }
262     // rename to init globals
263     if ( getVBAGlobals( ) )
264     {
265         return pVBAGlobals->Find( rName, t );
266     }
267     return nullptr;
268 }
269 
270 namespace {
271 
272 // Create array for conversion SFX <-> VB error code
273 struct SFX_VB_ErrorItem
274 {
275     sal_uInt16  nErrorVB;
276     ErrCode nErrorSFX;
277 };
278 
279 }
280 
281 const SFX_VB_ErrorItem SFX_VB_ErrorTab[] =
282 {
283     { 1, ERRCODE_BASIC_EXCEPTION },  // #87844 Map exception to error code 1
284     { 2, ERRCODE_BASIC_SYNTAX },
285     { 3, ERRCODE_BASIC_NO_GOSUB },
286     { 4, ERRCODE_BASIC_REDO_FROM_START },
287     { 5, ERRCODE_BASIC_BAD_ARGUMENT },
288     { 6, ERRCODE_BASIC_MATH_OVERFLOW },
289     { 7, ERRCODE_BASIC_NO_MEMORY },
290     { 8, ERRCODE_BASIC_ALREADY_DIM },
291     { 9, ERRCODE_BASIC_OUT_OF_RANGE },
292     { 10, ERRCODE_BASIC_DUPLICATE_DEF },
293     { 11, ERRCODE_BASIC_ZERODIV },
294     { 12, ERRCODE_BASIC_VAR_UNDEFINED },
295     { 13, ERRCODE_BASIC_CONVERSION },
296     { 14, ERRCODE_BASIC_BAD_PARAMETER },
297     { 18, ERRCODE_BASIC_USER_ABORT },
298     { 20, ERRCODE_BASIC_BAD_RESUME },
299     { 28, ERRCODE_BASIC_STACK_OVERFLOW },
300     { 35, ERRCODE_BASIC_PROC_UNDEFINED },
301     { 48, ERRCODE_BASIC_BAD_DLL_LOAD },
302     { 49, ERRCODE_BASIC_BAD_DLL_CALL },
303     { 51, ERRCODE_BASIC_INTERNAL_ERROR },
304     { 52, ERRCODE_BASIC_BAD_CHANNEL },
305     { 53, ERRCODE_BASIC_FILE_NOT_FOUND },
306     { 54, ERRCODE_BASIC_BAD_FILE_MODE },
307     { 55, ERRCODE_BASIC_FILE_ALREADY_OPEN },
308     { 57, ERRCODE_BASIC_IO_ERROR },
309     { 58, ERRCODE_BASIC_FILE_EXISTS },
310     { 59, ERRCODE_BASIC_BAD_RECORD_LENGTH },
311     { 61, ERRCODE_BASIC_DISK_FULL },
312     { 62, ERRCODE_BASIC_READ_PAST_EOF },
313     { 63, ERRCODE_BASIC_BAD_RECORD_NUMBER },
314     { 67, ERRCODE_BASIC_TOO_MANY_FILES },
315     { 68, ERRCODE_BASIC_NO_DEVICE },
316     { 70, ERRCODE_BASIC_ACCESS_DENIED },
317     { 71, ERRCODE_BASIC_NOT_READY },
318     { 73, ERRCODE_BASIC_NOT_IMPLEMENTED },
319     { 74, ERRCODE_BASIC_DIFFERENT_DRIVE },
320     { 75, ERRCODE_BASIC_ACCESS_ERROR },
321     { 76, ERRCODE_BASIC_PATH_NOT_FOUND },
322     { 91, ERRCODE_BASIC_NO_OBJECT },
323     { 93, ERRCODE_BASIC_BAD_PATTERN },
324     { 94, ERRCODE_BASIC_IS_NULL },
325     { 250, ERRCODE_BASIC_DDE_ERROR },
326     { 280, ERRCODE_BASIC_DDE_WAITINGACK },
327     { 281, ERRCODE_BASIC_DDE_OUTOFCHANNELS },
328     { 282, ERRCODE_BASIC_DDE_NO_RESPONSE },
329     { 283, ERRCODE_BASIC_DDE_MULT_RESPONSES },
330     { 284, ERRCODE_BASIC_DDE_CHANNEL_LOCKED },
331     { 285, ERRCODE_BASIC_DDE_NOTPROCESSED },
332     { 286, ERRCODE_BASIC_DDE_TIMEOUT },
333     { 287, ERRCODE_BASIC_DDE_USER_INTERRUPT },
334     { 288, ERRCODE_BASIC_DDE_BUSY },
335     { 289, ERRCODE_BASIC_DDE_NO_DATA },
336     { 290, ERRCODE_BASIC_DDE_WRONG_DATA_FORMAT },
337     { 291, ERRCODE_BASIC_DDE_PARTNER_QUIT },
338     { 292, ERRCODE_BASIC_DDE_CONV_CLOSED },
339     { 293, ERRCODE_BASIC_DDE_NO_CHANNEL },
340     { 294, ERRCODE_BASIC_DDE_INVALID_LINK },
341     { 295, ERRCODE_BASIC_DDE_QUEUE_OVERFLOW },
342     { 296, ERRCODE_BASIC_DDE_LINK_ALREADY_EST },
343     { 297, ERRCODE_BASIC_DDE_LINK_INV_TOPIC },
344     { 298, ERRCODE_BASIC_DDE_DLL_NOT_FOUND },
345     { 323, ERRCODE_BASIC_CANNOT_LOAD },
346     { 341, ERRCODE_BASIC_BAD_INDEX },
347     { 366, ERRCODE_BASIC_NO_ACTIVE_OBJECT },
348     { 380, ERRCODE_BASIC_BAD_PROP_VALUE },
349     { 382, ERRCODE_BASIC_PROP_READONLY },
350     { 394, ERRCODE_BASIC_PROP_WRITEONLY },
351     { 420, ERRCODE_BASIC_INVALID_OBJECT },
352     { 423, ERRCODE_BASIC_NO_METHOD },
353     { 424, ERRCODE_BASIC_NEEDS_OBJECT },
354     { 425, ERRCODE_BASIC_INVALID_USAGE_OBJECT },
355     { 430, ERRCODE_BASIC_NO_OLE },
356     { 438, ERRCODE_BASIC_BAD_METHOD },
357     { 440, ERRCODE_BASIC_OLE_ERROR },
358     { 445, ERRCODE_BASIC_BAD_ACTION },
359     { 446, ERRCODE_BASIC_NO_NAMED_ARGS },
360     { 447, ERRCODE_BASIC_BAD_LOCALE },
361     { 448, ERRCODE_BASIC_NAMED_NOT_FOUND },
362     { 449, ERRCODE_BASIC_NOT_OPTIONAL },
363     { 450, ERRCODE_BASIC_WRONG_ARGS },
364     { 451, ERRCODE_BASIC_NOT_A_COLL },
365     { 452, ERRCODE_BASIC_BAD_ORDINAL },
366     { 453, ERRCODE_BASIC_DLLPROC_NOT_FOUND },
367     { 460, ERRCODE_BASIC_BAD_CLIPBD_FORMAT },
368     { 951, ERRCODE_BASIC_UNEXPECTED },
369     { 952, ERRCODE_BASIC_EXPECTED },
370     { 953, ERRCODE_BASIC_SYMBOL_EXPECTED },
371     { 954, ERRCODE_BASIC_VAR_EXPECTED },
372     { 955, ERRCODE_BASIC_LABEL_EXPECTED },
373     { 956, ERRCODE_BASIC_LVALUE_EXPECTED },
374     { 957, ERRCODE_BASIC_VAR_DEFINED },
375     { 958, ERRCODE_BASIC_PROC_DEFINED },
376     { 959, ERRCODE_BASIC_LABEL_DEFINED },
377     { 960, ERRCODE_BASIC_UNDEF_VAR },
378     { 961, ERRCODE_BASIC_UNDEF_ARRAY },
379     { 962, ERRCODE_BASIC_UNDEF_PROC },
380     { 963, ERRCODE_BASIC_UNDEF_LABEL },
381     { 964, ERRCODE_BASIC_UNDEF_TYPE },
382     { 965, ERRCODE_BASIC_BAD_EXIT },
383     { 966, ERRCODE_BASIC_BAD_BLOCK },
384     { 967, ERRCODE_BASIC_BAD_BRACKETS },
385     { 968, ERRCODE_BASIC_BAD_DECLARATION },
386     { 969, ERRCODE_BASIC_BAD_PARAMETERS },
387     { 970, ERRCODE_BASIC_BAD_CHAR_IN_NUMBER },
388     { 971, ERRCODE_BASIC_MUST_HAVE_DIMS },
389     { 972, ERRCODE_BASIC_NO_IF },
390     { 973, ERRCODE_BASIC_NOT_IN_SUBR },
391     { 974, ERRCODE_BASIC_NOT_IN_MAIN },
392     { 975, ERRCODE_BASIC_WRONG_DIMS },
393     { 976, ERRCODE_BASIC_BAD_OPTION },
394     { 977, ERRCODE_BASIC_CONSTANT_REDECLARED },
395     { 978, ERRCODE_BASIC_PROG_TOO_LARGE },
396     { 979, ERRCODE_BASIC_NO_STRINGS_ARRAYS },
397     { 1000, ERRCODE_BASIC_PROPERTY_NOT_FOUND },
398     { 1001, ERRCODE_BASIC_METHOD_NOT_FOUND },
399     { 1002, ERRCODE_BASIC_ARG_MISSING },
400     { 1003, ERRCODE_BASIC_BAD_NUMBER_OF_ARGS },
401     { 1004, ERRCODE_BASIC_METHOD_FAILED },
402     { 1005, ERRCODE_BASIC_SETPROP_FAILED },
403     { 1006, ERRCODE_BASIC_GETPROP_FAILED },
404     { 1007, ERRCODE_BASIC_COMPAT },
405     { 0xFFFF, ErrCode(0xFFFFFFFFUL) }     // End mark
406 };
407 
408 // The StarBASIC factory is a hack. When a SbModule is created, its pointer
409 // is saved and given to the following SbProperties/SbMethods. This restores
410 // the Module-relationship. But it works only when a module is loaded.
411 // Can cause troubles with separately loaded properties!
412 
413 SbxBaseRef SbiFactory::Create( sal_uInt16 nSbxId, sal_uInt32 nCreator )
414 {
415     if( nCreator ==  SBXCR_SBX )
416     {
417         switch( nSbxId )
418         {
419         case SBXID_BASIC:
420             return new StarBASIC( nullptr );
421         case SBXID_BASICMOD:
422             return new SbModule( "" );
423         case SBXID_BASICPROP:
424             return new SbProperty( "", SbxVARIANT, nullptr );
425         case SBXID_BASICMETHOD:
426             return new SbMethod( "", SbxVARIANT, nullptr );
427         case SBXID_JSCRIPTMOD:
428             return new SbJScriptModule;
429         case SBXID_JSCRIPTMETH:
430             return new SbJScriptMethod( SbxVARIANT );
431         }
432     }
433     return nullptr;
434 }
435 
436 SbxObjectRef SbiFactory::CreateObject( const OUString& rClass )
437 {
438     if( rClass.equalsIgnoreAsciiCase( "StarBASIC" ) )
439     {
440         return new StarBASIC( nullptr );
441     }
442     else if( rClass.equalsIgnoreAsciiCase( "StarBASICModule" ) )
443     {
444         return new SbModule( OUString() );
445     }
446     else if( rClass.equalsIgnoreAsciiCase( "Collection" ) )
447     {
448         return new BasicCollection( "Collection" );
449     }
450     else if( rClass.equalsIgnoreAsciiCase( "FileSystemObject" ) )
451     {
452         try
453         {
454             Reference< XMultiServiceFactory > xFactory( comphelper::getProcessServiceFactory(), UNO_SET_THROW );
455             OUString aServiceName("ooo.vba.FileSystemObject");
456             Reference< XInterface > xInterface( xFactory->createInstance( aServiceName ), UNO_SET_THROW );
457             return new SbUnoObject( aServiceName, uno::Any( xInterface ) );
458         }
459         catch(const Exception& )
460         {
461         }
462     }
463     return nullptr;
464 }
465 
466 
467 SbxBaseRef SbOLEFactory::Create( sal_uInt16, sal_uInt32 )
468 {
469     // Not supported
470     return nullptr;
471 }
472 
473 SbxObjectRef SbOLEFactory::CreateObject( const OUString& rClassName )
474 {
475     SbxObjectRef pRet = createOLEObject_Impl( rClassName );
476     return pRet;
477 }
478 
479 
480 // SbFormFactory, show user forms by: dim as new <user form name>
481 
482 SbxBaseRef SbFormFactory::Create( sal_uInt16, sal_uInt32 )
483 {
484     // Not supported
485     return nullptr;
486 }
487 
488 SbxObjectRef SbFormFactory::CreateObject( const OUString& rClassName )
489 {
490     if( SbModule* pMod = GetSbData()->pMod )
491     {
492         if( SbxVariable* pVar = pMod->Find( rClassName, SbxClassType::Object ) )
493         {
494             if( SbUserFormModule* pFormModule = dynamic_cast<SbUserFormModule*>( pVar->GetObject() )  )
495             {
496                 bool bInitState = pFormModule->getInitState();
497                 if( bInitState )
498                 {
499                     // Not the first instantiate, reset
500                     pFormModule->ResetApiObj( false/*bTriggerTerminateEvent*/ );
501                     pFormModule->setInitState( false );
502                 }
503                 else
504                 {
505                     pFormModule->Load();
506                 }
507                 return pFormModule->CreateInstance();
508             }
509         }
510     }
511     return nullptr;
512 }
513 
514 
515 // SbTypeFactory
516 
517 SbxObjectRef cloneTypeObjectImpl( const SbxObject& rTypeObj )
518 {
519     SbxObjectRef pRet = new SbxObject( rTypeObj );
520     pRet->PutObject( pRet.get() );
521 
522     // Copy the properties, not only the reference to them
523     SbxArray* pProps = pRet->GetProperties();
524     sal_uInt32 nCount = pProps->Count();
525     for( sal_uInt32 i = 0 ; i < nCount ; i++ )
526     {
527         SbxVariable* pVar = pProps->Get(i);
528         SbxProperty* pProp = dynamic_cast<SbxProperty*>( pVar  );
529         if( pProp )
530         {
531             SbxProperty* pNewProp = new SbxProperty( *pProp );
532             SbxDataType eVarType = pVar->GetType();
533             if( eVarType & SbxARRAY )
534             {
535                 SbxBase* pParObj = pVar->GetObject();
536                 SbxDimArray* pSource = dynamic_cast<SbxDimArray*>( pParObj );
537                 SbxDimArray* pDest = new SbxDimArray( pVar->GetType() );
538 
539                 pDest->setHasFixedSize( pSource && pSource->hasFixedSize() );
540                 if (pSource && pSource->GetDims() && pSource->hasFixedSize())
541                 {
542                     sal_Int32 lb = 0;
543                     sal_Int32 ub = 0;
544                     for (sal_Int32 j = 1; j <= pSource->GetDims(); ++j)
545                     {
546                         pSource->GetDim(j, lb, ub);
547                         pDest->AddDim(lb, ub);
548                     }
549                 }
550                 else
551                 {
552                     pDest->unoAddDim(0, -1); // variant array
553                 }
554                 SbxFlagBits nSavFlags = pVar->GetFlags();
555                 pNewProp->ResetFlag( SbxFlagBits::Fixed );
556                 // need to reset the FIXED flag
557                 // when calling PutObject ( because the type will not match Object )
558                 pNewProp->PutObject( pDest );
559                 pNewProp->SetFlags( nSavFlags );
560             }
561             if( eVarType == SbxOBJECT )
562             {
563                 SbxBase* pObjBase = pVar->GetObject();
564                 SbxObject* pSrcObj = dynamic_cast<SbxObject*>( pObjBase );
565                 SbxObjectRef pDestObj;
566                 if( pSrcObj != nullptr )
567                     pDestObj = cloneTypeObjectImpl( *pSrcObj );
568                 pNewProp->PutObject( pDestObj.get() );
569             }
570             pProps->PutDirect( pNewProp, i );
571         }
572     }
573     return pRet;
574 }
575 
576 SbxBaseRef SbTypeFactory::Create( sal_uInt16, sal_uInt32 )
577 {
578     // Not supported
579     return nullptr;
580 }
581 
582 SbxObjectRef SbTypeFactory::CreateObject( const OUString& rClassName )
583 {
584     SbxObjectRef pRet;
585     SbModule* pMod = GetSbData()->pMod;
586     if( pMod )
587     {
588         const SbxObject* pObj = pMod->FindType( rClassName );
589         if( pObj )
590         {
591             pRet = cloneTypeObjectImpl( *pObj );
592         }
593     }
594     return pRet;
595 }
596 
597 SbxObjectRef createUserTypeImpl( const OUString& rClassName )
598 {
599     SbxObjectRef pRetObj = GetSbData()->pTypeFac->CreateObject( rClassName );
600     return pRetObj;
601 }
602 
603 
604 SbClassModuleObject::SbClassModuleObject( SbModule* pClassModule )
605     : SbModule( pClassModule->GetName() )
606     , mpClassModule( pClassModule )
607     , mbInitializeEventDone( false )
608 {
609     aOUSource = pClassModule->aOUSource;
610     aComment = pClassModule->aComment;
611     // see comment in destructor about these two
612     pImage.reset(pClassModule->pImage.get());
613     pBreaks = pClassModule->pBreaks;
614 
615     SetClassName( pClassModule->GetName() );
616 
617     // Allow search only internally
618     ResetFlag( SbxFlagBits::GlobalSearch );
619 
620     // Copy the methods from original class module
621     SbxArray* pClassMethods = pClassModule->GetMethods().get();
622     sal_uInt32 nMethodCount = pClassMethods->Count();
623     sal_uInt32 i;
624     for( i = 0 ; i < nMethodCount ; i++ )
625     {
626         SbxVariable* pVar = pClassMethods->Get(i);
627 
628         // Exclude SbIfaceMapperMethod to copy them in a second step
629         SbIfaceMapperMethod* pIfaceMethod = dynamic_cast<SbIfaceMapperMethod*>( pVar  );
630         if( !pIfaceMethod )
631         {
632             SbMethod* pMethod = dynamic_cast<SbMethod*>( pVar  );
633             if( pMethod )
634             {
635                 SbxFlagBits nFlags_ = pMethod->GetFlags();
636                 pMethod->SetFlag( SbxFlagBits::NoBroadcast );
637                 SbMethod* pNewMethod = new SbMethod( *pMethod );
638                 pNewMethod->ResetFlag( SbxFlagBits::NoBroadcast );
639                 pMethod->SetFlags( nFlags_ );
640                 pNewMethod->pMod = this;
641                 pNewMethod->SetParent( this );
642                 pMethods->PutDirect( pNewMethod, i );
643                 StartListening(pNewMethod->GetBroadcaster(), DuplicateHandling::Prevent);
644             }
645         }
646     }
647 
648     // Copy SbIfaceMapperMethod in a second step to ensure that
649     // the corresponding base methods have already been copied
650     for( i = 0 ; i < nMethodCount ; i++ )
651     {
652         SbxVariable* pVar = pClassMethods->Get(i);
653 
654         SbIfaceMapperMethod* pIfaceMethod = dynamic_cast<SbIfaceMapperMethod*>( pVar  );
655         if( pIfaceMethod )
656         {
657             SbMethod* pImplMethod = pIfaceMethod->getImplMethod();
658             if( !pImplMethod )
659             {
660                 OSL_FAIL( "No ImplMethod" );
661                 continue;
662             }
663 
664             // Search for own copy of ImplMethod
665             SbxVariable* p = pMethods->Find( pImplMethod->GetName(), SbxClassType::Method );
666             SbMethod* pImplMethodCopy = dynamic_cast<SbMethod*>( p );
667             if( !pImplMethodCopy )
668             {
669                 OSL_FAIL( "Found no ImplMethod copy" );
670                 continue;
671             }
672             SbIfaceMapperMethod* pNewIfaceMethod =
673                 new SbIfaceMapperMethod( pIfaceMethod->GetName(), pImplMethodCopy );
674             pMethods->PutDirect( pNewIfaceMethod, i );
675         }
676     }
677 
678     // Copy the properties from original class module
679     SbxArray* pClassProps = pClassModule->GetProperties();
680     sal_uInt32 nPropertyCount = pClassProps->Count();
681     for( i = 0 ; i < nPropertyCount ; i++ )
682     {
683         SbxVariable* pVar = pClassProps->Get(i);
684         SbProcedureProperty* pProcedureProp = dynamic_cast<SbProcedureProperty*>( pVar  );
685         if( pProcedureProp )
686         {
687             SbxFlagBits nFlags_ = pProcedureProp->GetFlags();
688             pProcedureProp->SetFlag( SbxFlagBits::NoBroadcast );
689             SbProcedureProperty* pNewProp = new SbProcedureProperty
690                 ( pProcedureProp->GetName(), pProcedureProp->GetType() );
691             pNewProp->SetFlags( nFlags_ ); // Copy flags
692             pNewProp->ResetFlag( SbxFlagBits::NoBroadcast ); // except the Broadcast if it was set
693             pProcedureProp->SetFlags( nFlags_ );
694             pProps->PutDirect( pNewProp, i );
695             StartListening(pNewProp->GetBroadcaster(), DuplicateHandling::Prevent);
696         }
697         else
698         {
699             SbxProperty* pProp = dynamic_cast<SbxProperty*>( pVar  );
700             if( pProp )
701             {
702                 SbxFlagBits nFlags_ = pProp->GetFlags();
703                 pProp->SetFlag( SbxFlagBits::NoBroadcast );
704                 SbxProperty* pNewProp = new SbxProperty( *pProp );
705 
706                 // Special handling for modules instances and collections, they need
707                 // to be instantiated, otherwise all refer to the same base object
708                 SbxDataType eVarType = pProp->GetType();
709                 if( eVarType == SbxOBJECT )
710                 {
711                     SbxBase* pObjBase = pProp->GetObject();
712                     SbxObject* pObj = dynamic_cast<SbxObject*>( pObjBase );
713                     if( pObj != nullptr )
714                     {
715                         const OUString& aObjClass = pObj->GetClassName();
716 
717                         SbClassModuleObject* pClassModuleObj = dynamic_cast<SbClassModuleObject*>( pObjBase );
718                         if( pClassModuleObj != nullptr )
719                         {
720                             SbModule* pLclClassModule = pClassModuleObj->getClassModule();
721                             SbClassModuleObject* pNewObj = new SbClassModuleObject( pLclClassModule );
722                             pNewObj->SetName( pProp->GetName() );
723                             pNewObj->SetParent( pLclClassModule->pParent );
724                             pNewProp->PutObject( pNewObj );
725                         }
726                         else if( aObjClass.equalsIgnoreAsciiCase( "Collection" ) )
727                         {
728                             BasicCollection* pNewCollection = new BasicCollection( "Collection" );
729                             pNewCollection->SetName( pProp->GetName() );
730                             pNewCollection->SetParent( pClassModule->pParent );
731                             pNewProp->PutObject( pNewCollection );
732                         }
733                     }
734                 }
735 
736                 pNewProp->ResetFlag( SbxFlagBits::NoBroadcast );
737                 pNewProp->SetParent( this );
738                 pProps->PutDirect( pNewProp, i );
739                 pProp->SetFlags( nFlags_ );
740             }
741         }
742     }
743     SetModuleType( ModuleType::CLASS );
744     mbVBACompat = pClassModule->mbVBACompat;
745 }
746 
747 SbClassModuleObject::~SbClassModuleObject()
748 {
749     // do not trigger termination event when document is already closed
750     if( StarBASIC::IsRunning() )
751         if( StarBASIC* pDocBasic = lclGetDocBasicForModule( this ) )
752             if( const DocBasicItem* pDocBasicItem = lclFindDocBasicItem( pDocBasic ) )
753                 if( !pDocBasicItem->isDocClosed() )
754                     triggerTerminateEvent();
755 
756     // prevent the base class destructor from deleting these because
757     // we do not actually own them
758     pImage.release();
759     pBreaks = nullptr;
760 }
761 
762 void SbClassModuleObject::Notify( SfxBroadcaster& rBC, const SfxHint& rHint )
763 {
764     handleProcedureProperties( rBC, rHint );
765 }
766 
767 SbxVariable* SbClassModuleObject::Find( const OUString& rName, SbxClassType t )
768 {
769     SbxVariable* pRes = SbxObject::Find( rName, t );
770     if( pRes )
771     {
772         triggerInitializeEvent();
773 
774         SbIfaceMapperMethod* pIfaceMapperMethod = dynamic_cast<SbIfaceMapperMethod*>( pRes );
775         if( pIfaceMapperMethod )
776         {
777             pRes = pIfaceMapperMethod->getImplMethod();
778             pRes->SetFlag( SbxFlagBits::ExtFound );
779         }
780     }
781     return pRes;
782 }
783 
784 void SbClassModuleObject::triggerInitializeEvent()
785 {
786     if( mbInitializeEventDone )
787     {
788         return;
789     }
790 
791     mbInitializeEventDone = true;
792 
793     // Search method
794     SbxVariable* pMeth = SbxObject::Find("Class_Initialize", SbxClassType::Method);
795     if( pMeth )
796     {
797         SbxValues aVals;
798         pMeth->Get( aVals );
799     }
800 }
801 
802 void SbClassModuleObject::triggerTerminateEvent()
803 {
804     if( !mbInitializeEventDone || GetSbData()->bRunInit )
805     {
806         return;
807     }
808     // Search method
809     SbxVariable* pMeth = SbxObject::Find("Class_Terminate", SbxClassType::Method );
810     if( pMeth )
811     {
812         SbxValues aVals;
813         pMeth->Get( aVals );
814     }
815 }
816 
817 
818 SbClassData::SbClassData()
819 {
820     mxIfaces = new SbxArray();
821 }
822 
823 void SbClassData::clear()
824 {
825     mxIfaces->Clear();
826     maRequiredTypes.clear();
827 }
828 
829 SbClassFactory::SbClassFactory()
830 {
831     xClassModules = new SbxObject( OUString() );
832 }
833 
834 SbClassFactory::~SbClassFactory()
835 {}
836 
837 void SbClassFactory::AddClassModule( SbModule* pClassModule )
838 {
839     SbxObjectRef xToUseClassModules = xClassModules;
840 
841     if( StarBASIC* pDocBasic = lclGetDocBasicForModule( pClassModule ) )
842         if( const DocBasicItem* pDocBasicItem = lclFindDocBasicItem( pDocBasic ) )
843             xToUseClassModules = pDocBasicItem->getClassModules();
844 
845     SbxObject* pParent = pClassModule->GetParent();
846     xToUseClassModules->Insert( pClassModule );
847     pClassModule->SetParent( pParent );
848 }
849 
850 void SbClassFactory::RemoveClassModule( SbModule* pClassModule )
851 {
852     xClassModules->Remove( pClassModule );
853 }
854 
855 SbxBaseRef SbClassFactory::Create( sal_uInt16, sal_uInt32 )
856 {
857     // Not supported
858     return nullptr;
859 }
860 
861 SbxObjectRef SbClassFactory::CreateObject( const OUString& rClassName )
862 {
863     SbxObjectRef xToUseClassModules = xClassModules;
864 
865     if( SbModule* pMod = GetSbData()->pMod )
866     {
867         if( StarBASIC* pDocBasic = lclGetDocBasicForModule( pMod ) )
868         {
869             if( const DocBasicItem* pDocBasicItem = lclFindDocBasicItem( pDocBasic ) )
870             {
871                 xToUseClassModules = pDocBasicItem->getClassModules();
872             }
873         }
874     }
875     SbxVariable* pVar = xToUseClassModules->Find( rClassName, SbxClassType::Object );
876     SbxObjectRef pRet;
877     if( pVar )
878     {
879         SbModule* pVarMod = static_cast<SbModule*>(pVar);
880         pRet = new SbClassModuleObject( pVarMod );
881     }
882     return pRet;
883 }
884 
885 SbModule* SbClassFactory::FindClass( const OUString& rClassName )
886 {
887     SbxVariable* pVar = xClassModules->Find( rClassName, SbxClassType::DontCare );
888     SbModule* pMod = pVar ? static_cast<SbModule*>(pVar) : nullptr;
889     return pMod;
890 }
891 
892 StarBASIC::StarBASIC( StarBASIC* p, bool bIsDocBasic  )
893     : SbxObject("StarBASIC"), bDocBasic( bIsDocBasic )
894 {
895     SetParent( p );
896     bNoRtl = bBreak = false;
897     bVBAEnabled = false;
898 
899     if( !GetSbData()->nInst++ )
900     {
901         GetSbData()->pSbFac.reset( new SbiFactory );
902         AddFactory( GetSbData()->pSbFac.get() );
903         GetSbData()->pTypeFac.reset(new SbTypeFactory);
904         AddFactory( GetSbData()->pTypeFac.get() );
905         GetSbData()->pClassFac.reset(new SbClassFactory);
906         AddFactory( GetSbData()->pClassFac.get() );
907         GetSbData()->pOLEFac.reset(new SbOLEFactory);
908         AddFactory( GetSbData()->pOLEFac.get() );
909         GetSbData()->pFormFac.reset(new SbFormFactory);
910         AddFactory( GetSbData()->pFormFac.get() );
911         GetSbData()->pUnoFac.reset( new SbUnoFactory );
912         AddFactory( GetSbData()->pUnoFac.get() );
913     }
914     pRtl = new SbiStdObject(SB_RTLNAME, this );
915     // Search via StarBasic is always global
916     SetFlag( SbxFlagBits::GlobalSearch );
917     pVBAGlobals = nullptr;
918     bQuit = false;
919 
920     if( bDocBasic )
921     {
922         lclInsertDocBasicItem( *this );
923     }
924 }
925 
926 // #51727 Override SetModified so that the modified state
927 // is not given to the parent
928 void StarBASIC::SetModified( bool b )
929 {
930     SbxBase::SetModified( b );
931 }
932 
933 StarBASIC::~StarBASIC()
934 {
935     // Needs to be first action as it can trigger events
936     disposeComVariablesForBasic( this );
937 
938     if( !--GetSbData()->nInst )
939     {
940         RemoveFactory( GetSbData()->pSbFac.get() );
941         GetSbData()->pSbFac.reset();
942         RemoveFactory( GetSbData()->pUnoFac.get() );
943         GetSbData()->pUnoFac.reset();
944         RemoveFactory( GetSbData()->pTypeFac.get() );
945         GetSbData()->pTypeFac.reset();
946         RemoveFactory( GetSbData()->pClassFac.get() );
947         GetSbData()->pClassFac.reset();
948         RemoveFactory( GetSbData()->pOLEFac.get() );
949         GetSbData()->pOLEFac.reset();
950         RemoveFactory( GetSbData()->pFormFac.get() );
951         GetSbData()->pFormFac.reset();
952 
953         if( SbiGlobals::pGlobals )
954         {
955             delete SbiGlobals::pGlobals;
956             SbiGlobals::pGlobals = nullptr;
957         }
958     }
959     else if( bDocBasic )
960     {
961         ErrCode eOld = SbxBase::GetError();
962 
963         lclRemoveDocBasicItem( *this );
964 
965         SbxBase::ResetError();
966         if( eOld != ERRCODE_NONE )
967         {
968             SbxBase::SetError( eOld );
969         }
970     }
971 
972     // #100326 Set Parent NULL in registered listeners
973     if( xUnoListeners.is() )
974     {
975         sal_uInt32 uCount = xUnoListeners->Count();
976         for( sal_uInt32 i = 0 ; i < uCount ; i++ )
977         {
978             SbxVariable* pListenerObj = xUnoListeners->Get(i);
979             pListenerObj->SetParent( nullptr );
980         }
981         xUnoListeners = nullptr;
982     }
983 
984     clearUnoMethodsForBasic( this );
985 }
986 
987 void StarBASIC::implClearDependingVarsOnDelete( StarBASIC* pDeletedBasic )
988 {
989     if( this != pDeletedBasic )
990     {
991         for( const auto& pModule: pModules)
992         {
993             pModule->ClearVarsDependingOnDeletedBasic( pDeletedBasic );
994         }
995     }
996 
997     for (sal_uInt32 nObj = 0; nObj < pObjs->Count(); nObj++)
998     {
999         SbxVariable* pVar = pObjs->Get(nObj);
1000         StarBASIC* pBasic = dynamic_cast<StarBASIC*>( pVar );
1001         if( pBasic && pBasic != pDeletedBasic )
1002         {
1003             pBasic->implClearDependingVarsOnDelete( pDeletedBasic );
1004         }
1005     }
1006 }
1007 
1008 
1009 SbModule* StarBASIC::MakeModule( const OUString& rName, const OUString& rSrc )
1010 {
1011     ModuleInfo aInfo;
1012     aInfo.ModuleType = ModuleType::NORMAL;
1013     return MakeModule(  rName, aInfo, rSrc );
1014 }
1015 SbModule* StarBASIC::MakeModule( const OUString& rName, const ModuleInfo& mInfo, const OUString& rSrc )
1016 {
1017 
1018     SAL_INFO(
1019         "basic",
1020         "create module " << rName  << " type mInfo " << mInfo.ModuleType);
1021     SbModule* p = nullptr;
1022     switch ( mInfo.ModuleType )
1023     {
1024     case ModuleType::DOCUMENT:
1025         // In theory we should be able to create Object modules
1026         // in ordinary basic ( in vba mode thought these are create
1027         // by the application/basic and not by the user )
1028         p = new SbObjModule( rName, mInfo, isVBAEnabled() );
1029         break;
1030     case ModuleType::CLASS:
1031         p = new SbModule( rName, isVBAEnabled() );
1032         p->SetModuleType( ModuleType::CLASS );
1033         break;
1034     case ModuleType::FORM:
1035         p = new SbUserFormModule( rName, mInfo, isVBAEnabled() );
1036         break;
1037     default:
1038         p = new SbModule( rName, isVBAEnabled() );
1039         break;
1040     }
1041     p->SetSource32( rSrc );
1042     p->SetParent( this );
1043     pModules.emplace_back(p);
1044     SetModified( true );
1045     return p;
1046 }
1047 
1048 void StarBASIC::Insert( SbxVariable* pVar )
1049 {
1050     if( auto pModule = dynamic_cast<SbModule*>(pVar) )
1051     {
1052         pModules.emplace_back(pModule);
1053         pVar->SetParent( this );
1054         StartListening(pVar->GetBroadcaster(), DuplicateHandling::Prevent);
1055     }
1056     else
1057     {
1058         bool bWasModified = IsModified();
1059         SbxObject::Insert( pVar );
1060         if( !bWasModified && pVar->IsSet( SbxFlagBits::DontStore ) )
1061         {
1062             SetModified( false );
1063         }
1064     }
1065 }
1066 
1067 void StarBASIC::Remove( SbxVariable* pVar )
1068 {
1069     SbModule* pModule = dynamic_cast<SbModule*>(pVar);
1070     if( pModule )
1071     {
1072         // #87540 Can be last reference!
1073         SbModuleRef xVar = pModule;
1074         pModules.erase(std::remove(pModules.begin(), pModules.end(), xVar));
1075         pVar->SetParent( nullptr );
1076         EndListening( pVar->GetBroadcaster() );
1077     }
1078     else
1079     {
1080         SbxObject::Remove( pVar );
1081     }
1082 }
1083 
1084 void StarBASIC::Clear()
1085 {
1086     pModules.clear();
1087 }
1088 
1089 SbModule* StarBASIC::FindModule( std::u16string_view rName )
1090 {
1091     for (const auto& pModule: pModules)
1092     {
1093         if( pModule->GetName().equalsIgnoreAsciiCase( rName ) )
1094         {
1095             return pModule.get();
1096         }
1097     }
1098     return nullptr;
1099 }
1100 
1101 
1102 struct ClassModuleRunInitItem
1103 {
1104     SbModule*       m_pModule;
1105     bool            m_bProcessing;
1106     bool            m_bRunInitDone;
1107 
1108     ClassModuleRunInitItem()
1109         : m_pModule( nullptr )
1110         , m_bProcessing( false )
1111         , m_bRunInitDone( false )
1112     {}
1113     explicit ClassModuleRunInitItem( SbModule* pModule )
1114         : m_pModule( pModule )
1115         , m_bProcessing( false )
1116         , m_bRunInitDone( false )
1117     {}
1118 };
1119 
1120 // Derive from unordered_map type instead of typedef
1121 // to allow forward declaration in sbmod.hxx
1122 class ModuleInitDependencyMap : public
1123     std::unordered_map< OUString, ClassModuleRunInitItem >
1124 {};
1125 
1126 void SbModule::implProcessModuleRunInit( ModuleInitDependencyMap& rMap, ClassModuleRunInitItem& rItem )
1127 {
1128     rItem.m_bProcessing = true;
1129 
1130     SbModule* pModule = rItem.m_pModule;
1131     if( pModule->pClassData != nullptr )
1132     {
1133         std::vector< OUString >& rReqTypes = pModule->pClassData->maRequiredTypes;
1134         for( const auto& rStr : rReqTypes )
1135         {
1136             // Is required type a class module?
1137             ModuleInitDependencyMap::iterator itFind = rMap.find( rStr );
1138             if( itFind != rMap.end() )
1139             {
1140                 ClassModuleRunInitItem& rParentItem = itFind->second;
1141                 if( rParentItem.m_bProcessing )
1142                 {
1143                     // TODO: raise error?
1144                     OSL_FAIL( "Cyclic module dependency detected" );
1145                     continue;
1146                 }
1147 
1148                 if( !rParentItem.m_bRunInitDone )
1149                 {
1150                     implProcessModuleRunInit( rMap, rParentItem );
1151                 }
1152             }
1153         }
1154     }
1155 
1156     pModule->RunInit();
1157     rItem.m_bRunInitDone = true;
1158     rItem.m_bProcessing = false;
1159 }
1160 
1161 // Run Init-Code of all modules (including inserted libraries)
1162 void StarBASIC::InitAllModules( StarBASIC const * pBasicNotToInit )
1163 {
1164     SolarMutexGuard guard;
1165 
1166     // Init own modules
1167     for (const auto& pModule: pModules)
1168     {
1169         pModule->Compile();
1170     }
1171     // compile modules first then RunInit ( otherwise there is
1172     // can be order dependency, e.g. classmodule A has a member
1173     // of type classmodule B and classmodule B hasn't been compiled yet )
1174 
1175     // Consider required types to init in right order. Class modules
1176     // that are required by other modules have to be initialized first.
1177     ModuleInitDependencyMap aMIDMap;
1178     for (const auto& pModule: pModules)
1179     {
1180         OUString aModuleName = pModule->GetName();
1181         if( pModule->isProxyModule() )
1182         {
1183             aMIDMap[aModuleName] = ClassModuleRunInitItem( pModule.get() );
1184         }
1185     }
1186 
1187     for (auto & elem : aMIDMap)
1188     {
1189         ClassModuleRunInitItem& rItem = elem.second;
1190         SbModule::implProcessModuleRunInit( aMIDMap, rItem );
1191     }
1192 
1193     // Call RunInit on standard modules
1194     for (const auto& pModule: pModules)
1195     {
1196         if( !pModule->isProxyModule() )
1197         {
1198             pModule->RunInit();
1199         }
1200     }
1201 
1202     // Check all objects if they are BASIC,
1203     // if yes initialize
1204     for (sal_uInt32 nObj = 0; nObj < pObjs->Count(); nObj++)
1205     {
1206         SbxVariable* pVar = pObjs->Get(nObj);
1207         StarBASIC* pBasic = dynamic_cast<StarBASIC*>( pVar );
1208         if( pBasic && pBasic != pBasicNotToInit )
1209         {
1210             pBasic->InitAllModules();
1211         }
1212     }
1213 }
1214 
1215 // #88329 Put modules back to not initialised state to
1216 // force reinitialisation at next start
1217 void StarBASIC::DeInitAllModules()
1218 {
1219     // Deinit own modules
1220     for (const auto& pModule: pModules)
1221     {
1222         if( pModule->pImage && !pModule->isProxyModule() && dynamic_cast<SbObjModule*>( pModule.get()) == nullptr )
1223         {
1224             pModule->pImage->bInit = false;
1225         }
1226     }
1227 
1228     for (sal_uInt32 nObj = 0; nObj < pObjs->Count(); nObj++)
1229     {
1230         SbxVariable* pVar = pObjs->Get(nObj);
1231         StarBASIC* pBasic = dynamic_cast<StarBASIC*>( pVar );
1232         if( pBasic )
1233         {
1234             pBasic->DeInitAllModules();
1235         }
1236     }
1237 }
1238 
1239 // This implementation at first searches within the runtime library,
1240 // then it looks for an element within one module. This module can be
1241 // a public var or an entrypoint. If it is not found and we look for a
1242 // method and a module with the given name is found the search continues
1243 // for entrypoint "Main".
1244 // If this fails again a conventional search over objects is performend.
1245 SbxVariable* StarBASIC::Find( const OUString& rName, SbxClassType t )
1246 {
1247     SbxVariable* pRes = nullptr;
1248     SbModule* pNamed = nullptr;
1249     // "Extended" search in Runtime Lib
1250     // but only if SbiRuntime has not set the flag
1251     if( !bNoRtl )
1252     {
1253         if( t == SbxClassType::DontCare || t == SbxClassType::Object )
1254         {
1255             if( rName.equalsIgnoreAsciiCase( SB_RTLNAME ) )
1256             {
1257                 pRes = pRtl.get();
1258             }
1259         }
1260         if( !pRes )
1261         {
1262             pRes = static_cast<SbiStdObject*>(pRtl.get())->Find( rName, t );
1263         }
1264         if( pRes )
1265         {
1266             pRes->SetFlag( SbxFlagBits::ExtFound );
1267         }
1268     }
1269     // Search module
1270     if( !pRes )
1271     {
1272         for (const auto& pModule: pModules)
1273         {
1274             if( pModule->IsVisible() )
1275             {
1276                 // Remember module for Main() call
1277                 // or is the name equal?!?
1278                 if( pModule->GetName().equalsIgnoreAsciiCase( rName ) )
1279                 {
1280                     if( t == SbxClassType::Object || t == SbxClassType::DontCare )
1281                     {
1282                         pRes = pModule.get(); break;
1283                     }
1284                     pNamed = pModule.get();
1285                 }
1286                 // Only variables qualified by the Module Name e.g. Sheet1.foo
1287                 // should work for Document && Class type Modules
1288                 sal_Int32 nType = pModule->GetModuleType();
1289                 if ( nType == ModuleType::DOCUMENT || nType == ModuleType::FORM )
1290                 {
1291                     continue;
1292                 }
1293                 // otherwise check if the element is available
1294                 // unset GBLSEARCH-Flag (due to recursion)
1295                 SbxFlagBits nGblFlag = pModule->GetFlags() & SbxFlagBits::GlobalSearch;
1296                 pModule->ResetFlag( SbxFlagBits::GlobalSearch );
1297                 pRes = pModule->Find( rName, t );
1298                 pModule->SetFlag( nGblFlag );
1299                 if( pRes )
1300                 {
1301                     break;
1302                 }
1303             }
1304         }
1305     }
1306     OUString aMainStr("Main");
1307     if( !pRes && pNamed && ( t == SbxClassType::Method || t == SbxClassType::DontCare ) &&
1308         !pNamed->GetName().equalsIgnoreAsciiCase( aMainStr ) )
1309     {
1310         pRes = pNamed->Find( aMainStr, SbxClassType::Method );
1311     }
1312     if( !pRes )
1313     {
1314         pRes = SbxObject::Find( rName, t );
1315     }
1316     return pRes;
1317 }
1318 
1319 bool StarBASIC::Call( const OUString& rName, SbxArray* pParam )
1320 {
1321     bool bRes = SbxObject::Call( rName, pParam );
1322     if( !bRes )
1323     {
1324         ErrCode eErr = SbxBase::GetError();
1325         SbxBase::ResetError();
1326         if( eErr != ERRCODE_NONE )
1327         {
1328             RTError( eErr, OUString(), 0, 0, 0 );
1329         }
1330     }
1331     return bRes;
1332 }
1333 
1334 // Find method via name (e.g. query via BASIC IDE)
1335 SbxBase* StarBASIC::FindSBXInCurrentScope( const OUString& rName )
1336 {
1337     if( !GetSbData()->pInst )
1338     {
1339         return nullptr;
1340     }
1341     if( !GetSbData()->pInst->pRun )
1342     {
1343         return nullptr;
1344     }
1345     return GetSbData()->pInst->pRun->FindElementExtern( rName );
1346 }
1347 
1348 void StarBASIC::QuitAndExitApplication()
1349 {
1350     Stop();
1351     bQuit = true;
1352 }
1353 
1354 void StarBASIC::Stop()
1355 {
1356     SbiInstance* p = GetSbData()->pInst;
1357     if( p )
1358         p->Stop();
1359 }
1360 
1361 bool StarBASIC::IsRunning()
1362 {
1363     return GetSbData()->pInst != nullptr;
1364 }
1365 
1366 SbMethod* StarBASIC::GetActiveMethod( sal_uInt16 nLevel )
1367 {
1368     if( GetSbData()->pInst )
1369     {
1370         return GetSbData()->pInst->GetCaller( nLevel );
1371     }
1372     else
1373     {
1374         return nullptr;
1375     }
1376 }
1377 
1378 SbModule* StarBASIC::GetActiveModule()
1379 {
1380     if( GetSbData()->pInst && !GetSbData()->bCompilerError )
1381     {
1382         return GetSbData()->pInst->GetActiveModule();
1383     }
1384     else
1385     {
1386         return GetSbData()->pCompMod;
1387     }
1388 }
1389 
1390 BasicDebugFlags StarBASIC::BreakPoint( sal_Int32 l, sal_Int32 c1, sal_Int32 c2 )
1391 {
1392     SetErrorData( ERRCODE_NONE, l, c1, c2 );
1393     bBreak = true;
1394     if( GetSbData()->aBreakHdl.IsSet() )
1395     {
1396         return GetSbData()->aBreakHdl.Call( this );
1397     }
1398     else
1399     {
1400         return BreakHdl();
1401     }
1402 }
1403 
1404 BasicDebugFlags StarBASIC::StepPoint( sal_Int32 l, sal_Int32 c1, sal_Int32 c2 )
1405 {
1406     SetErrorData( ERRCODE_NONE, l, c1, c2 );
1407     bBreak = false;
1408     if( GetSbData()->aBreakHdl.IsSet() )
1409     {
1410         return GetSbData()->aBreakHdl.Call( this );
1411     }
1412     else
1413     {
1414         return BreakHdl();
1415     }
1416 }
1417 
1418 BasicDebugFlags StarBASIC::BreakHdl()
1419 {
1420     return aBreakHdl.IsSet() ? aBreakHdl.Call( this ) : BasicDebugFlags::Continue;
1421 }
1422 
1423 // Calls for error handler and break handler
1424 sal_uInt16 StarBASIC::GetLine()     { return GetSbData()->nLine; }
1425 sal_uInt16 StarBASIC::GetCol1()     { return GetSbData()->nCol1; }
1426 sal_uInt16 StarBASIC::GetCol2()     { return GetSbData()->nCol2; }
1427 
1428 // Specific to error handler
1429 ErrCode const & StarBASIC::GetErrorCode() { return GetSbData()->nCode; }
1430 const OUString& StarBASIC::GetErrorText() { return GetSbData()->aErrMsg; }
1431 
1432 // From 1996-03-29:
1433 // The mapping between the old and the new error codes take place by searching
1434 // through the table SFX_VB_ErrorTab[]. This is indeed not with good performance,
1435 // but it consumes much less memory than corresponding switch blocks.
1436 // Because the conversion of error codes has not to be fast. There is no
1437 // binary search by VB Error -> Error SFX.
1438 
1439 // Map back new error codes to old, Sbx-compatible
1440 sal_uInt16 StarBASIC::GetVBErrorCode( ErrCode nError )
1441 {
1442     sal_uInt16 nRet = 0;
1443 
1444     if( SbiRuntime::isVBAEnabled() )
1445     {
1446         if ( nError == ERRCODE_BASIC_ARRAY_FIX )
1447             return 10;
1448         else if ( nError == ERRCODE_BASIC_STRING_OVERFLOW )
1449             return 14;
1450         else if ( nError == ERRCODE_BASIC_EXPR_TOO_COMPLEX )
1451             return 16;
1452         else if ( nError == ERRCODE_BASIC_OPER_NOT_PERFORM )
1453             return 17;
1454         else if ( nError == ERRCODE_BASIC_TOO_MANY_DLL )
1455             return 47;
1456         else if ( nError == ERRCODE_BASIC_LOOP_NOT_INIT )
1457             return 92;
1458         else
1459             nRet = 0;
1460     }
1461 
1462     // search loop
1463     const SFX_VB_ErrorItem* pErrItem;
1464     sal_uInt16 nIndex = 0;
1465     do
1466     {
1467         pErrItem = SFX_VB_ErrorTab + nIndex;
1468         if( pErrItem->nErrorSFX == nError )
1469         {
1470             nRet = pErrItem->nErrorVB;
1471             break;
1472         }
1473         nIndex++;
1474     }
1475     while( pErrItem->nErrorVB != 0xFFFF );      // up to end mark
1476     return nRet;
1477 }
1478 
1479 ErrCode StarBASIC::GetSfxFromVBError( sal_uInt16 nError )
1480 {
1481     ErrCode nRet = ERRCODE_NONE;
1482 
1483     if( SbiRuntime::isVBAEnabled() )
1484     {
1485         switch( nError )
1486         {
1487         case 1:
1488         case 2:
1489         case 4:
1490         case 8:
1491         case 12:
1492         case 73:
1493             return ERRCODE_NONE;
1494         case 10:
1495             return ERRCODE_BASIC_ARRAY_FIX;
1496         case 14:
1497             return ERRCODE_BASIC_STRING_OVERFLOW;
1498         case 16:
1499             return ERRCODE_BASIC_EXPR_TOO_COMPLEX;
1500         case 17:
1501             return ERRCODE_BASIC_OPER_NOT_PERFORM;
1502         case 47:
1503             return ERRCODE_BASIC_TOO_MANY_DLL;
1504         case 92:
1505             return ERRCODE_BASIC_LOOP_NOT_INIT;
1506         default:
1507             nRet = ERRCODE_NONE;
1508         }
1509     }
1510     const SFX_VB_ErrorItem* pErrItem;
1511     sal_uInt16 nIndex = 0;
1512     do
1513     {
1514         pErrItem = SFX_VB_ErrorTab + nIndex;
1515         if( pErrItem->nErrorVB == nError )
1516         {
1517             nRet = pErrItem->nErrorSFX;
1518             break;
1519         }
1520         else if( pErrItem->nErrorVB > nError )
1521         {
1522             break;              // couldn't found anymore
1523         }
1524         nIndex++;
1525     }
1526     while( pErrItem->nErrorVB != 0xFFFF );      // up to end mark
1527     return nRet;
1528 }
1529 
1530 // set Error- / Break-data
1531 void StarBASIC::SetErrorData( ErrCode nCode, sal_uInt16 nLine,
1532                               sal_uInt16 nCol1, sal_uInt16 nCol2 )
1533 {
1534     SbiGlobals& aGlobals = *GetSbData();
1535     aGlobals.nCode = nCode;
1536     aGlobals.nLine = nLine;
1537     aGlobals.nCol1 = nCol1;
1538     aGlobals.nCol2 = nCol2;
1539 }
1540 
1541 void StarBASIC::MakeErrorText( ErrCode nId, std::u16string_view aMsg )
1542 {
1543     SolarMutexGuard aSolarGuard;
1544     sal_uInt16 nOldID = GetVBErrorCode( nId );
1545 
1546     TranslateId pErrorMsg;
1547     for (std::pair<TranslateId, ErrCode> const *pItem = RID_BASIC_START; pItem->second; ++pItem)
1548     {
1549         if (nId == pItem->second)
1550         {
1551             pErrorMsg = pItem->first;
1552             break;
1553         }
1554     }
1555 
1556     if (pErrorMsg)
1557     {
1558         // merge message with additional text
1559         OUString sError = BasResId(pErrorMsg);
1560         OUStringBuffer aMsg1(sError);
1561         // replace argument placeholder with %s
1562         OUString aSrgStr( "$(ARG1)" );
1563         sal_Int32 nResult = sError.indexOf(aSrgStr);
1564 
1565         if( nResult >= 0 )
1566         {
1567             aMsg1.remove(nResult, aSrgStr.getLength());
1568             aMsg1.insert(nResult, aMsg);
1569         }
1570         GetSbData()->aErrMsg = aMsg1.makeStringAndClear();
1571     }
1572     else if( nOldID != 0 )
1573     {
1574         OUString aStdMsg = "Error " + OUString::number(nOldID) +
1575                            ": No error text available!";
1576         GetSbData()->aErrMsg = aStdMsg;
1577     }
1578     else
1579     {
1580         GetSbData()->aErrMsg.clear();
1581     }
1582 }
1583 
1584 bool StarBASIC::CError( ErrCode code, const OUString& rMsg,
1585                             sal_Int32 l, sal_Int32 c1, sal_Int32 c2 )
1586 {
1587     SolarMutexGuard aSolarGuard;
1588 
1589     // compiler error during runtime -> stop program
1590     if( IsRunning() )
1591     {
1592         // #109018 Check if running Basic is affected
1593         StarBASIC* pStartedBasic = GetSbData()->pInst->GetBasic();
1594         if( pStartedBasic != this )
1595         {
1596             return false;
1597         }
1598         Stop();
1599     }
1600 
1601     // set flag, so that GlobalRunInit notice the error
1602     GetSbData()->bGlobalInitErr = true;
1603 
1604     // tinker the error message
1605     MakeErrorText( code, rMsg );
1606 
1607     // Implementation of the code for the string transport to SFX-Error
1608     if( !rMsg.isEmpty() )
1609     {
1610         code = *new StringErrorInfo( code, rMsg );
1611     }
1612     SetErrorData( code, l, c1, c2 );
1613     GetSbData()->bCompilerError = true;
1614     bool bRet;
1615     if( GetSbData()->aErrHdl.IsSet() )
1616     {
1617         bRet = GetSbData()->aErrHdl.Call( this );
1618     }
1619     else
1620     {
1621         bRet = ErrorHdl();
1622     }
1623     GetSbData()->bCompilerError = false;     // only true for error handler
1624     return bRet;
1625 }
1626 
1627 bool StarBASIC::RTError( ErrCode code, const OUString& rMsg, sal_Int32 l, sal_Int32 c1, sal_Int32 c2 )
1628 {
1629     SolarMutexGuard aSolarGuard;
1630 
1631     ErrCode c = code;
1632     if( c.GetClass() == ErrCodeClass::Compiler )
1633     {
1634         c = ERRCODE_NONE;
1635     }
1636     MakeErrorText( c, rMsg );
1637 
1638     // Implementation of the code for the string transport to SFX-Error
1639     if( !rMsg.isEmpty() )
1640     {
1641         // very confusing, even though MakeErrorText sets up the error text
1642         // seems that this is not used ( if rMsg already has content )
1643         // In the case of VBA MakeErrorText also formats the error to be a little more
1644         // like vba ( adds an error number etc )
1645         if ( SbiRuntime::isVBAEnabled() && ( code == ERRCODE_BASIC_COMPAT ) )
1646         {
1647             OUString aTmp = "\'" + OUString::number(SbxErrObject::getUnoErrObject()->getNumber()) +
1648                             "\'\n" + (!GetSbData()->aErrMsg.isEmpty() ? GetSbData()->aErrMsg : rMsg);
1649             code = *new StringErrorInfo( code, aTmp );
1650         }
1651         else
1652         {
1653             code = *new StringErrorInfo( code, rMsg );
1654         }
1655     }
1656 
1657     SetErrorData( code, l, c1, c2 );
1658     if( GetSbData()->aErrHdl.IsSet() )
1659     {
1660         return GetSbData()->aErrHdl.Call( this );
1661     }
1662     else
1663     {
1664         return ErrorHdl();
1665     }
1666 }
1667 
1668 void StarBASIC::Error( ErrCode n )
1669 {
1670     Error( n, OUString() );
1671 }
1672 
1673 void StarBASIC::Error( ErrCode n, const OUString& rMsg )
1674 {
1675     if( GetSbData()->pInst )
1676     {
1677         GetSbData()->pInst->Error( n, rMsg );
1678     }
1679 }
1680 
1681 void StarBASIC::FatalError( ErrCode n )
1682 {
1683     if( GetSbData()->pInst )
1684     {
1685         GetSbData()->pInst->FatalError( n );
1686     }
1687 }
1688 
1689 void StarBASIC::FatalError( ErrCode _errCode, const OUString& _details )
1690 {
1691     if( GetSbData()->pInst )
1692     {
1693         GetSbData()->pInst->FatalError( _errCode, _details );
1694     }
1695 }
1696 
1697 ErrCode StarBASIC::GetErrBasic()
1698 {
1699     if( GetSbData()->pInst )
1700     {
1701         return GetSbData()->pInst->GetErr();
1702     }
1703     else
1704     {
1705         return ERRCODE_NONE;
1706     }
1707 }
1708 
1709 // make the additional message for the RTL function error accessible
1710 OUString StarBASIC::GetErrorMsg()
1711 {
1712     if( GetSbData()->pInst )
1713     {
1714         return GetSbData()->pInst->GetErrorMsg();
1715     }
1716     else
1717     {
1718         return OUString();
1719     }
1720 }
1721 
1722 sal_Int32 StarBASIC::GetErl()
1723 {
1724     if( GetSbData()->pInst )
1725     {
1726         return GetSbData()->pInst->GetErl();
1727     }
1728     else
1729     {
1730         return 0;
1731     }
1732 }
1733 
1734 bool StarBASIC::ErrorHdl()
1735 {
1736     return aErrorHdl.Call( this );
1737 }
1738 
1739 Link<StarBASIC*,bool> const & StarBASIC::GetGlobalErrorHdl()
1740 {
1741     return GetSbData()->aErrHdl;
1742 }
1743 
1744 void StarBASIC::SetGlobalErrorHdl( const Link<StarBASIC*,bool>& rLink )
1745 {
1746     GetSbData()->aErrHdl = rLink;
1747 }
1748 
1749 void StarBASIC::SetGlobalBreakHdl( const Link<StarBASIC*,BasicDebugFlags>& rLink )
1750 {
1751     GetSbData()->aBreakHdl = rLink;
1752 }
1753 
1754 SbxArrayRef const & StarBASIC::getUnoListeners()
1755 {
1756     if( !xUnoListeners.is() )
1757     {
1758         xUnoListeners = new SbxArray();
1759     }
1760     return xUnoListeners;
1761 }
1762 
1763 
1764 bool StarBASIC::LoadData( SvStream& r, sal_uInt16 nVer )
1765 {
1766     if( !SbxObject::LoadData( r, nVer ) )
1767     {
1768         return false;
1769     }
1770     // #95459 Delete dialogs, otherwise endless recursion
1771     // in SbxVariable::GetType() if dialogs are accessed
1772     sal_uInt32 nObjCount = pObjs->Count();
1773     std::unique_ptr<SbxVariable*[]> ppDeleteTab(new SbxVariable*[ nObjCount ]);
1774     sal_uInt32 nObj;
1775 
1776     for( nObj = 0 ; nObj < nObjCount ; nObj++ )
1777     {
1778         SbxVariable* pVar = pObjs->Get(nObj);
1779         StarBASIC* pBasic = dynamic_cast<StarBASIC*>( pVar  );
1780         ppDeleteTab[nObj] = pBasic ? nullptr : pVar;
1781     }
1782     for( nObj = 0 ; nObj < nObjCount ; nObj++ )
1783     {
1784         SbxVariable* pVar = ppDeleteTab[nObj];
1785         if( pVar )
1786         {
1787             pObjs->Remove( pVar );
1788         }
1789     }
1790     ppDeleteTab.reset();
1791 
1792     sal_uInt16 nMod(0);
1793     pModules.clear();
1794     r.ReadUInt16( nMod );
1795     const size_t nMinSbxSize(14);
1796     const size_t nMaxPossibleEntries = r.remainingSize() / nMinSbxSize;
1797     if (nMod > nMaxPossibleEntries)
1798     {
1799         nMod = nMaxPossibleEntries;
1800         SAL_WARN("basic", "Parsing error: " << nMaxPossibleEntries <<
1801                  " max possible entries, but " << nMod << " claimed, truncating");
1802     }
1803     for (sal_uInt16 i = 0; i < nMod; ++i)
1804     {
1805         SbxBaseRef pBase = SbxBase::Load( r );
1806         SbModule* pMod = dynamic_cast<SbModule*>(pBase.get());
1807         if( !pMod )
1808         {
1809             return false;
1810         }
1811         else if( dynamic_cast<const SbJScriptModule*>( pMod) != nullptr )
1812         {
1813             // assign Ref, so that pMod will be deleted
1814             SbModuleRef xDeleteRef = pMod;
1815         }
1816         else
1817         {
1818             pMod->SetParent( this );
1819             pModules.emplace_back(pMod );
1820         }
1821     }
1822     // HACK for SFX-Bullshit!
1823     SbxVariable* p = Find( "FALSE", SbxClassType::Property );
1824     if( p )
1825     {
1826         Remove( p );
1827     }
1828     p = Find( "TRUE", SbxClassType::Property );
1829     if( p )
1830     {
1831         Remove( p );
1832     }
1833     // End of the hacks!
1834     // Search via StarBASIC is at all times global
1835     DBG_ASSERT( IsSet( SbxFlagBits::GlobalSearch ), "Basic loaded without GBLSEARCH" );
1836     SetFlag( SbxFlagBits::GlobalSearch );
1837     return true;
1838 }
1839 
1840 bool StarBASIC::StoreData( SvStream& r ) const
1841 {
1842     if( !SbxObject::StoreData( r ) )
1843     {
1844         return false;
1845     }
1846     assert(pModules.size() < SAL_MAX_UINT16);
1847     r.WriteUInt16( static_cast<sal_uInt16>(pModules.size()));
1848     for( const auto& rpModule: pModules )
1849     {
1850         if( !rpModule->Store( r ) )
1851         {
1852             return false;
1853         }
1854     }
1855     return true;
1856 }
1857 
1858 bool StarBASIC::GetUNOConstant( const OUString& rName, css::uno::Any& aOut )
1859 {
1860     bool bRes = false;
1861     SbUnoObject* pGlobs = dynamic_cast<SbUnoObject*>( Find( rName, SbxClassType::DontCare ) );
1862     if ( pGlobs )
1863     {
1864         aOut = pGlobs->getUnoAny();
1865         bRes = true;
1866     }
1867     return bRes;
1868 }
1869 
1870 Reference< frame::XModel > StarBASIC::GetModelFromBasic( SbxObject* pBasic )
1871 {
1872     OSL_PRECOND( pBasic != nullptr, "getModelFromBasic: illegal call!" );
1873     if ( !pBasic )
1874     {
1875         return nullptr;
1876     }
1877     // look for the ThisComponent variable, first in the parent (which
1878     // might be the document's Basic), then in the parent's parent (which might be
1879     // the application Basic)
1880     static const OUStringLiteral sThisComponent( u"ThisComponent");
1881     SbxVariable* pThisComponent = nullptr;
1882 
1883     SbxObject* pLookup = pBasic->GetParent();
1884     while ( pLookup && !pThisComponent )
1885     {
1886         pThisComponent = pLookup->Find( sThisComponent, SbxClassType::Object );
1887         pLookup = pLookup->GetParent();
1888     }
1889     if ( !pThisComponent )
1890     {
1891         SAL_WARN("basic", "Failed to get ThisComponent");
1892             // the application Basic, at the latest, should have this variable
1893         return nullptr;
1894     }
1895 
1896     Any aThisComponentAny( sbxToUnoValue( pThisComponent ) );
1897     Reference< frame::XModel > xModel( aThisComponentAny, UNO_QUERY );
1898     if ( !xModel.is() )
1899     {
1900         // it's no XModel. Okay, ThisComponent nowadays is allowed to be a controller.
1901         Reference< frame::XController > xController( aThisComponentAny, UNO_QUERY );
1902         if ( xController.is() )
1903         {
1904             xModel = xController->getModel();
1905         }
1906     }
1907     if ( !xModel.is() )
1908     {
1909         return nullptr;
1910     }
1911 
1912     return xModel;
1913 }
1914 
1915 void StarBASIC::DetachAllDocBasicItems()
1916 {
1917     for (auto const& item : gaDocBasicItems)
1918     {
1919         DocBasicItemRef xItem = item.second;
1920         xItem->setDisposed(true);
1921     }
1922 }
1923 
1924 // #118116 Implementation Collection object
1925 
1926 
1927 constexpr OUStringLiteral pCountStr = u"Count";
1928 constexpr OUStringLiteral pAddStr = u"Add";
1929 constexpr OUStringLiteral pItemStr = u"Item";
1930 constexpr OUStringLiteral pRemoveStr = u"Remove";
1931 constexpr sal_uInt16 nCountHash = SbxVariable::MakeHashCode(pCountStr);
1932 constexpr sal_uInt16 nAddHash = SbxVariable::MakeHashCode(pAddStr);
1933 constexpr sal_uInt16 nItemHash = SbxVariable::MakeHashCode(pItemStr);
1934 constexpr sal_uInt16 nRemoveHash = SbxVariable::MakeHashCode(pRemoveStr);
1935 
1936 SbxInfoRef BasicCollection::xAddInfo;
1937 SbxInfoRef BasicCollection::xItemInfo;
1938 
1939 BasicCollection::BasicCollection( const OUString& rClass )
1940              : SbxObject( rClass )
1941 {
1942     Initialize();
1943 }
1944 
1945 BasicCollection::~BasicCollection()
1946 {}
1947 
1948 void BasicCollection::Clear()
1949 {
1950     SbxObject::Clear();
1951     Initialize();
1952 }
1953 
1954 void BasicCollection::Initialize()
1955 {
1956     xItemArray = new SbxArray();
1957     SetType( SbxOBJECT );
1958     SetFlag( SbxFlagBits::Fixed );
1959     ResetFlag( SbxFlagBits::Write );
1960     SbxVariable* p;
1961     p = Make( pCountStr, SbxClassType::Property, SbxINTEGER );
1962     p->ResetFlag( SbxFlagBits::Write );
1963     p->SetFlag( SbxFlagBits::DontStore );
1964     p = Make( pAddStr, SbxClassType::Method, SbxEMPTY );
1965     p->SetFlag( SbxFlagBits::DontStore );
1966     p = Make( pItemStr, SbxClassType::Method, SbxVARIANT );
1967     p->SetFlag( SbxFlagBits::DontStore );
1968     p = Make( pRemoveStr, SbxClassType::Method, SbxEMPTY );
1969     p->SetFlag( SbxFlagBits::DontStore );
1970     if ( !xAddInfo.is() )
1971     {
1972         xAddInfo = new SbxInfo;
1973         xAddInfo->AddParam(  "Item", SbxVARIANT );
1974         xAddInfo->AddParam(  "Key", SbxVARIANT, SbxFlagBits::Read | SbxFlagBits::Optional );
1975         xAddInfo->AddParam(  "Before", SbxVARIANT, SbxFlagBits::Read | SbxFlagBits::Optional );
1976         xAddInfo->AddParam(  "After", SbxVARIANT, SbxFlagBits::Read | SbxFlagBits::Optional );
1977     }
1978     if ( !xItemInfo.is() )
1979     {
1980         xItemInfo = new SbxInfo;
1981         xItemInfo->AddParam(  "Index", SbxVARIANT, SbxFlagBits::Read | SbxFlagBits::Optional);
1982     }
1983 }
1984 
1985 void BasicCollection::Notify( SfxBroadcaster& rCst, const SfxHint& rHint )
1986 {
1987     const SbxHint* p = dynamic_cast<const SbxHint*>(&rHint);
1988     if( p )
1989     {
1990         const SfxHintId nId = p->GetId();
1991         bool bRead  = nId == SfxHintId::BasicDataWanted;
1992         bool bWrite = nId == SfxHintId::BasicDataChanged;
1993         bool bRequestInfo = nId == SfxHintId::BasicInfoWanted;
1994         SbxVariable* pVar = p->GetVar();
1995         SbxArray* pArg = pVar->GetParameters();
1996         OUString aVarName( pVar->GetName() );
1997         if( bRead || bWrite )
1998         {
1999             if( pVar->GetHashCode() == nCountHash
2000                   && aVarName.equalsIgnoreAsciiCase( pCountStr ) )
2001             {
2002                 pVar->PutLong(xItemArray->Count());
2003             }
2004             else if( pVar->GetHashCode() == nAddHash
2005                   && aVarName.equalsIgnoreAsciiCase( pAddStr ) )
2006             {
2007                 CollAdd( pArg );
2008             }
2009             else if( pVar->GetHashCode() == nItemHash
2010                   && aVarName.equalsIgnoreAsciiCase( pItemStr ) )
2011             {
2012                 CollItem( pArg );
2013             }
2014             else if( pVar->GetHashCode() == nRemoveHash
2015                   && aVarName.equalsIgnoreAsciiCase( pRemoveStr ) )
2016             {
2017                 CollRemove( pArg );
2018             }
2019             else
2020             {
2021                 SbxObject::Notify( rCst, rHint );
2022             }
2023             return;
2024         }
2025         else if ( bRequestInfo )
2026         {
2027             if( pVar->GetHashCode() == nAddHash
2028                   && aVarName.equalsIgnoreAsciiCase( pAddStr ) )
2029             {
2030                 pVar->SetInfo( xAddInfo.get() );
2031             }
2032             else if( pVar->GetHashCode() == nItemHash
2033                   && aVarName.equalsIgnoreAsciiCase( pItemStr ) )
2034             {
2035                 pVar->SetInfo( xItemInfo.get() );
2036             }
2037         }
2038     }
2039     SbxObject::Notify( rCst, rHint );
2040 }
2041 
2042 sal_Int32 BasicCollection::implGetIndex( SbxVariable const * pIndexVar )
2043 {
2044     sal_Int32 nIndex = -1;
2045     if( pIndexVar->GetType() == SbxSTRING )
2046     {
2047         nIndex = implGetIndexForName( pIndexVar->GetOUString() );
2048     }
2049     else
2050     {
2051         nIndex = pIndexVar->GetLong() - 1;
2052     }
2053     return nIndex;
2054 }
2055 
2056 sal_Int32 BasicCollection::implGetIndexForName(std::u16string_view rName)
2057 {
2058     sal_Int32 nIndex = -1;
2059     sal_Int32 nCount = xItemArray->Count();
2060     sal_Int32 nNameHash = MakeHashCode( rName );
2061     for( sal_Int32 i = 0 ; i < nCount ; i++ )
2062     {
2063         SbxVariable* pVar = xItemArray->Get(i);
2064         if( pVar->GetHashCode() == nNameHash &&
2065             pVar->GetName().equalsIgnoreAsciiCase( rName ) )
2066         {
2067             nIndex = i;
2068             break;
2069         }
2070     }
2071     return nIndex;
2072 }
2073 
2074 void BasicCollection::CollAdd( SbxArray* pPar_ )
2075 {
2076     sal_uInt32 nCount = pPar_->Count();
2077     if( nCount < 2 || nCount > 5 )
2078     {
2079         SetError( ERRCODE_BASIC_WRONG_ARGS );
2080         return;
2081     }
2082 
2083     SbxVariable* pItem = pPar_->Get(1);
2084     if( pItem )
2085     {
2086         sal_uInt32 nNextIndex;
2087         if( nCount < 4 )
2088         {
2089             nNextIndex = xItemArray->Count();
2090         }
2091         else
2092         {
2093             SbxVariable* pBefore = pPar_->Get(3);
2094             if( nCount == 5 )
2095             {
2096                 if( !( pBefore->IsErr() || ( pBefore->GetType() == SbxEMPTY ) ) )
2097                 {
2098                     SetError( ERRCODE_BASIC_BAD_ARGUMENT );
2099                     return;
2100                 }
2101                 SbxVariable* pAfter = pPar_->Get(4);
2102                 sal_Int32 nAfterIndex = implGetIndex( pAfter );
2103                 if( nAfterIndex == -1 )
2104                 {
2105                     SetError( ERRCODE_BASIC_BAD_ARGUMENT );
2106                     return;
2107                 }
2108                 nNextIndex = sal::static_int_cast<sal_uInt32>(nAfterIndex + 1);
2109             }
2110             else // if( nCount == 4 )
2111             {
2112                 sal_Int32 nBeforeIndex = implGetIndex( pBefore );
2113                 if( nBeforeIndex == -1 )
2114                 {
2115                     SetError( ERRCODE_BASIC_BAD_ARGUMENT );
2116                     return;
2117                 }
2118                 nNextIndex = sal::static_int_cast<sal_uInt32>(nBeforeIndex);
2119             }
2120         }
2121 
2122         auto pNewItem = tools::make_ref<SbxVariable>( *pItem );
2123         if( nCount >= 3 )
2124         {
2125             SbxVariable* pKey = pPar_->Get(2);
2126             if( !( pKey->IsErr() || ( pKey->GetType() == SbxEMPTY ) ) )
2127             {
2128                 if( pKey->GetType() != SbxSTRING )
2129                 {
2130                     SetError( ERRCODE_BASIC_BAD_ARGUMENT );
2131                     return;
2132                 }
2133                 OUString aKey = pKey->GetOUString();
2134                 if( implGetIndexForName( aKey ) != -1 )
2135                 {
2136                     SetError( ERRCODE_BASIC_BAD_ARGUMENT );
2137                     return;
2138                 }
2139                 pNewItem->SetName( aKey );
2140             }
2141         }
2142         pNewItem->SetFlag( SbxFlagBits::ReadWrite );
2143         xItemArray->Insert(pNewItem.get(), nNextIndex);
2144     }
2145     else
2146     {
2147         SetError( ERRCODE_BASIC_BAD_ARGUMENT );
2148         return;
2149     }
2150 }
2151 
2152 void BasicCollection::CollItem( SbxArray* pPar_ )
2153 {
2154     if (pPar_->Count() != 2)
2155     {
2156         SetError( ERRCODE_BASIC_WRONG_ARGS );
2157         return;
2158     }
2159     SbxVariable* pRes = nullptr;
2160     SbxVariable* p = pPar_->Get(1);
2161     sal_Int32 nIndex = implGetIndex( p );
2162     if (nIndex >= 0 && nIndex < static_cast<sal_Int32>(xItemArray->Count()))
2163     {
2164         pRes = xItemArray->Get(nIndex);
2165     }
2166     if( !pRes )
2167     {
2168         SetError( ERRCODE_BASIC_BAD_ARGUMENT );
2169     }
2170     else
2171     {
2172         *(pPar_->Get(0)) = *pRes;
2173     }
2174 }
2175 
2176 void BasicCollection::CollRemove( SbxArray* pPar_ )
2177 {
2178     if (pPar_ == nullptr || pPar_->Count() != 2)
2179     {
2180         SetError( ERRCODE_BASIC_WRONG_ARGS );
2181         return;
2182     }
2183 
2184     SbxVariable* p = pPar_->Get(1);
2185     sal_Int32 nIndex = implGetIndex( p );
2186     if (nIndex >= 0 && nIndex < static_cast<sal_Int32>(xItemArray->Count()))
2187     {
2188         xItemArray->Remove( nIndex );
2189 
2190         // Correct for stack if necessary
2191         SbiInstance* pInst = GetSbData()->pInst;
2192         SbiRuntime* pRT = pInst ? pInst->pRun : nullptr;
2193         if( pRT )
2194         {
2195             SbiForStack* pStack = pRT->FindForStackItemForCollection( this );
2196             if( pStack != nullptr )
2197             {
2198                 if( pStack->nCurCollectionIndex >= nIndex )
2199                 {
2200                     --pStack->nCurCollectionIndex;
2201                 }
2202             }
2203         }
2204     }
2205     else
2206     {
2207         SetError( ERRCODE_BASIC_BAD_ARGUMENT );
2208     }
2209 }
2210 
2211 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
2212