tdf#130307 - Support for each loop for objects exposing XIndexAccess

Change-Id: Ib94c642e6d2a52ac7c60a8f7ae3c79d611b41614
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/123072
Tested-by: Jenkins
Reviewed-by: Andreas Heinisch <andreas.heinisch@yahoo.de>
diff --git a/basic/source/inc/runtime.hxx b/basic/source/inc/runtime.hxx
index 73e5683..d09db07 100644
--- a/basic/source/inc/runtime.hxx
+++ b/basic/source/inc/runtime.hxx
@@ -34,6 +34,7 @@
#include <memory>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/container/XEnumeration.hpp>
#include <com/sun/star/container/XIndexAccess.hpp>
#include <unotools/localedatawrapper.hxx>
#include <o3tl/deleter.hxx>
#include <o3tl/typed_flags_set.hxx>
@@ -55,6 +56,7 @@ enum class ForType {
    EachArray,
    EachCollection,
    EachXEnumeration,
    EachXIndexAccess,
    Error,
};

@@ -74,6 +76,7 @@ struct SbiForStack {                // for/next stack:
    std::unique_ptr<sal_Int32[]>
                        pArrayUpperBounds;
    css::uno::Reference< css::container::XEnumeration > xEnumeration;
    css::uno::Reference<css::container::XIndexAccess> xIndexAccess;

    SbiForStack()
        : pNext(nullptr)
diff --git a/basic/source/runtime/runtime.cxx b/basic/source/runtime/runtime.cxx
index 9a2a79e..5c27b84 100644
--- a/basic/source/runtime/runtime.cxx
+++ b/basic/source/runtime/runtime.cxx
@@ -1196,14 +1196,22 @@ void SbiRuntime::PushForEach()
    }
    else if (SbUnoObject* pUnoObj = dynamic_cast<SbUnoObject*>(pObj))
    {
        // XEnumerationAccess?
        // XEnumerationAccess or XIndexAccess?
        Any aAny = pUnoObj->getUnoAny();
        Reference<XIndexAccess> xIndexAccess;
        Reference< XEnumerationAccess > xEnumerationAccess;
        if( aAny >>= xEnumerationAccess )
        {
            p->xEnumeration = xEnumerationAccess->createEnumeration();
            p->eForType = ForType::EachXEnumeration;
        }
        // tdf#130307 - support for each loop for objects exposing XIndexAccess
        else if (aAny >>= xIndexAccess)
        {
            p->eForType = ForType::EachXIndexAccess;
            p->xIndexAccess = xIndexAccess;
            p->nCurCollectionIndex = 0;
        }
        else if ( isVBAEnabled() && pUnoObj->isNativeCOMObject() )
        {
            uno::Reference< script::XInvocation > xInvocation;
@@ -3204,6 +3212,29 @@ void SbiRuntime::StepTESTFOR( sal_uInt32 nOp1 )
            }
            break;
        }
        // tdf#130307 - support for each loop for objects exposing XIndexAccess
        case ForType::EachXIndexAccess:
        {
            SbiForStack* p = pForStk;
            if (!p->xIndexAccess)
            {
                SbxBase::SetError(ERRCODE_BASIC_CONVERSION);
                pForStk->eForType = ForType::Error; // terminate loop at the next iteration
            }
            else if (pForStk->nCurCollectionIndex < p->xIndexAccess->getCount())
            {
                Any aElem = p->xIndexAccess->getByIndex(pForStk->nCurCollectionIndex);
                pForStk->nCurCollectionIndex++;
                SbxVariableRef xVar = new SbxVariable(SbxVARIANT);
                unoToSbxValue(xVar.get(), aElem);
                (*pForStk->refVar) = *xVar;
            }
            else
            {
                bEndLoop = true;
            }
            break;
        }
        case ForType::Error:
        {
            // We are in Resume Next mode after failed loop initialization
diff --git a/sc/qa/extras/macros-test.cxx b/sc/qa/extras/macros-test.cxx
index 6c4c9e1..759f416 100644
--- a/sc/qa/extras/macros-test.cxx
+++ b/sc/qa/extras/macros-test.cxx
@@ -68,6 +68,7 @@ public:
    void testTdf90278();
    void testTdf143582();
    void testTdf144085();
    void testTdf130307();
    void testMacroButtonFormControlXlsxExport();

    CPPUNIT_TEST_SUITE(ScMacrosTest);
@@ -97,6 +98,7 @@ public:
    CPPUNIT_TEST(testTdf90278);
    CPPUNIT_TEST(testTdf143582);
    CPPUNIT_TEST(testTdf144085);
    CPPUNIT_TEST(testTdf130307);
    CPPUNIT_TEST(testMacroButtonFormControlXlsxExport);

    CPPUNIT_TEST_SUITE_END();
@@ -1057,6 +1059,32 @@ void ScMacrosTest::testTdf144085()
    xCloseable->close(true);
}

void ScMacrosTest::testTdf130307()
{
    OUString aFileName;
    createFileURL(u"tdf130307.ods", aFileName);
    auto xComponent = loadFromDesktop(aFileName, "com.sun.star.sheet.SpreadsheetDocument");

    css::uno::Any aRet;
    css::uno::Sequence<css::uno::Any> aParams;
    css::uno::Sequence<css::uno::Any> aOutParam;
    css::uno::Sequence<sal_Int16> aOutParamIndex;

    SfxObjectShell::CallXScript(
        xComponent,
        "vnd.sun.Star.script:Standard.Module1.ForEachSheets?language=Basic&location=document",
        aParams, aRet, aOutParamIndex, aOutParam);

    OUString aReturnValue;
    aRet >>= aReturnValue;

    // Without the fix in place, this test would have crashed here
    CPPUNIT_ASSERT_EQUAL(OUString("Sheet1Sheet2"), aReturnValue);

    css::uno::Reference<css::util::XCloseable> xCloseable(xComponent, css::uno::UNO_QUERY_THROW);
    xCloseable->close(true);
}

void ScMacrosTest::testTdf144970()
{
    OUString aFileName;
diff --git a/sc/qa/extras/testdocuments/tdf130307.ods b/sc/qa/extras/testdocuments/tdf130307.ods
new file mode 100644
index 0000000..fc354cf
--- /dev/null
+++ b/sc/qa/extras/testdocuments/tdf130307.ods
Binary files differ