Quellcodebibliothek Statistik Leitseite products/sources/formale Sprachen/C/LibreOffice/sc/source/ui/docshell/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 13 kB image not shown  

Quelle  datastream.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */


#include <datastream.hxx>
#include <datastreamgettime.hxx>

#include <com/sun/star/frame/XLayoutManager.hpp>
#include <osl/conditn.hxx>
#include <osl/time.h>
#include <salhelper/thread.hxx>
#include <sfx2/viewfrm.hxx>
#include <tools/stream.hxx>
#include <vcl/svapp.hxx>
#include <docsh.hxx>
#include <tabvwsh.hxx>
#include <viewdata.hxx>
#include <stringutil.hxx>
#include <documentlinkmgr.hxx>
#include <o3tl/enumarray.hxx>

#include <officecfg/Office/Calc.hxx>

#include <orcus/csv_parser.hpp>

#include <atomic>
#include <queue>

namespace com::sun::star::ui { class XUIElement; }

namespace sc {

static o3tl::enumarray<DebugTime, double> fTimes { 0.0, 0.0, 0.0 };

double datastream_get_time(DebugTime nIdx)
{
    return fTimes[ nIdx ];
}

namespace {

double getNow()
{
    TimeValue now;
    osl_getSystemTime(&now);
    return static_cast<double>(now.Seconds) + static_cast<double>(now.Nanosec) / 1000000000.0;
}

class CSVHandler
{
    DataStream::Line& mrLine;
    size_t mnColCount;
    size_t mnCols;
    const char* mpLineHead;

public:
    CSVHandler( DataStream::Line& rLine, size_t nColCount ) :
        mrLine(rLine), mnColCount(nColCount), mnCols(0), mpLineHead(rLine.maLine.getStr()) {}

    static void begin_parse() {}
    static void end_parse() {}
    static void begin_row() {}
    static void end_row() {}

    void cell(std::string_view s, bool /*transient*/)
    {
        if (mnCols >= mnColCount)
            return;

        DataStream::Cell aCell;
        if (ScStringUtil::parseSimpleNumber(s.data(), s.size(), '.'',', aCell.mfValue))
        {
            aCell.mbValue = true;
        }
        else
        {
            aCell.mbValue = false;
            aCell.maStr.Pos = std::distance(mpLineHead, s.data());
            aCell.maStr.Size = s.size();
        }
        mrLine.maCells.push_back(aCell);

        ++mnCols;
    }
};

}

namespace datastreams {

class ReaderThread : public salhelper::Thread
{
    std::unique_ptr<SvStream> mpStream;
    size_t mnColCount;
    std::atomic<bool> mbTerminate;

    std::queue<DataStream::LinesType> maPendingLines;
    std::queue<DataStream::LinesType> maUsedLines;
    std::mutex maMtxLines;

    osl::Condition maCondReadStream;
    osl::Condition maCondConsume;

    orcus::csv::parser_config maConfig;

public:

    ReaderThread(std::unique_ptr<SvStream> pData, size_t nColCount):
        Thread("ReaderThread"),
        mpStream(std::move(pData)),
        mnColCount(nColCount),
        mbTerminate(false)
    {
        maConfig.delimiters.push_back(',');
        maConfig.text_qualifier = '"';
    }

    bool isTerminateRequested()
    {
        return mbTerminate.load();
    }

    void requestTerminate()
    {
        mbTerminate.store(true);
    }

    void endThread()
    {
        requestTerminate();
        maCondReadStream.set();
    }

    void waitForNewLines()
    {
        maCondConsume.wait();
        maCondConsume.reset();
    }

    DataStream::LinesType popNewLines()
    {
        auto pLines = std::move(maPendingLines.front());
        maPendingLines.pop();
        return pLines;
    }

    void resumeReadStream()
    {
        if (maPendingLines.size() <= 4)
            maCondReadStream.set(); // start producer again
    }

    bool hasNewLines() const
    {
        return !maPendingLines.empty();
    }

    void pushUsedLines( DataStream::LinesType pLines )
    {
        maUsedLines.push(std::move(pLines));
    }

    std::mutex& getLinesMutex()
    {
        return maMtxLines;
    }

private:
    virtual void execute() override
    {
        while (!isTerminateRequested())
        {
            std::optional<DataStream::LinesType> oLines;
            std::unique_lock aGuard(getLinesMutex());

            if (!maUsedLines.empty())
            {
                // Re-use lines from previous runs.
                oLines = std::move(maUsedLines.front());
                maUsedLines.pop();
                aGuard.unlock(); // unlock
            }
            else
            {
                aGuard.unlock(); // unlock
                oLines.emplace(10);
            }

            // Read & store new lines from stream.
            OStringBuffer aBuffer;
            for (DataStream::Line & rLine : *oLines)
            {
                rLine.maCells.clear();
                mpStream->ReadLine(aBuffer);
                rLine.maLine = aBuffer.toString(); // produces minimal string, and keeps buffer
                CSVHandler aHdl(rLine, mnColCount);
                orcus::csv_parser<CSVHandler> parser(rLine.maLine, aHdl, maConfig);
                parser.parse();
            }

            aGuard.lock(); // lock
            while (!isTerminateRequested() && maPendingLines.size() >= 8)
            {
                // pause reading for a bit
                aGuard.unlock(); // unlock
                maCondReadStream.wait();
                maCondReadStream.reset();
                aGuard.lock(); // lock
            }
            maPendingLines.push(std::move(*oLines));
            maCondConsume.set();
            if (!mpStream->good())
                requestTerminate();
        }
    }
};

}

DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}

DataStream::Cell::Cell( const Cell& r ) : mbValue(r.mbValue)
{
    if (r.mbValue)
        mfValue = r.mfValue;
    else
    {
        maStr.Pos = r.maStr.Pos;
        maStr.Size = r.maStr.Size;
    }
}

void DataStream::MakeToolbarVisible()
{
    ScViewData* pViewData = ScDocShell::GetViewData();
    if (!pViewData)
        return;

    css::uno::Reference< css::frame::XFrame > xFrame =
        pViewData->GetViewShell()->GetViewFrame().GetFrame().GetFrameInterface();
    if (!xFrame.is())
        return;

    css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
    if (!xPropSet.is())
        return;

    css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
    xPropSet->getPropertyValue(u"LayoutManager"_ustr) >>= xLayoutManager;
    if (!xLayoutManager.is())
        return;

    static constexpr OUString sResourceURL( u"private:resource/toolbar/datastreams"_ustr );
    css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL);
    if (!xUIElement.is())
    {
        xLayoutManager->createElement( sResourceURL );
        xLayoutManager->showElement( sResourceURL );
    }
}

DataStream* DataStream::Set(
    ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
    sal_Int32 nLimit, MoveType eMove)
{
    DataStream* pLink = new DataStream(pShell, rURL, rRange, nLimit, eMove);
    sc::DocumentLinkManager& rMgr = pShell->GetDocument().GetDocLinkManager();
    rMgr.setDataStream(pLink);
    return pLink;
}

DataStream::DataStream(ScDocShell *pShell, const OUString& rURL, const ScRange& ;rRange,
        sal_Int32 nLimit, MoveType eMove) :
    mpDocShell(pShell),
    maDocAccess(mpDocShell->GetDocument()),
    meOrigMove(NO_MOVE),
    meMove(NO_MOVE),
    mbRunning(false),
    mbValuesInLine(false),
    mbRefreshOnEmptyLine(false),
    mnLinesCount(0),
    mnLinesSinceRefresh(0),
    mfLastRefreshTime(0.0),
    mnCurRow(0),
    maImportTimer("sc DataStream maImportTimer"),
    mbIsFirst(true),
    mbIsUpdate(false)
{
    maImportTimer.SetTimeout(0);
    maImportTimer.SetInvokeHandler( LINK(this, DataStream, ImportTimerHdl) );

    Decode(rURL, rRange, nLimit, eMove);
}

DataStream::~DataStream()
{
    if (mbRunning)
        StopImport();

    if (mxReaderThread.is())
    {
        mxReaderThread->endThread();
        mxReaderThread->join();
    }
    moLines.reset();
}

DataStream::Line DataStream::ConsumeLine()
{
    if (!moLines || mnLinesCount >= moLines->size())
    {
        mnLinesCount = 0;
        if (mxReaderThread->isTerminateRequested())
            return Line();

        std::unique_lock aGuard(mxReaderThread->getLinesMutex());
        if (moLines)
        {
            mxReaderThread->pushUsedLines(std::move(*moLines));
            moLines.reset();
        }

        while (!mxReaderThread->hasNewLines())
        {
            aGuard.unlock(); // unlock
            mxReaderThread->waitForNewLines();
            aGuard.lock(); // lock
        }

        moLines = mxReaderThread->popNewLines();
        mxReaderThread->resumeReadStream();
    }
    return moLines->at(mnLinesCount++);
}

ScRange DataStream::GetRange() const
{
    ScRange aRange = maStartRange;
    aRange.aEnd = maEndRange.aEnd;
    return aRange;
}

void DataStream::Decode(const OUString& rURL, const ScRange& rRange,
        sal_Int32 nLimit, MoveType eMove)
{
    msURL = rURL;
    meMove = eMove;
    meOrigMove = eMove;

    mbValuesInLine = true// always true.

    mnCurRow = rRange.aStart.Row();

    ScRange aRange = rRange;
    if (aRange.aStart.Row() != aRange.aEnd.Row())
        // We only allow this range to be one row tall.
        aRange.aEnd.SetRow(aRange.aStart.Row());

    maStartRange = aRange;
    maEndRange = aRange;
    const auto & rDoc = mpDocShell->GetDocument();
    if (nLimit == 0)
    {
        // Unlimited
        maEndRange.aStart.SetRow(rDoc.MaxRow());
    }
    else if (nLimit > 0)
    {
        // Limited.
        maEndRange.aStart.IncRow(nLimit-1);
        if (maEndRange.aStart.Row() > rDoc.MaxRow())
            maEndRange.aStart.SetRow(rDoc.MaxRow());
    }

    maEndRange.aEnd.SetRow(maEndRange.aStart.Row());
}

void DataStream::StartImport()
{
    if (mbRunning)
        return;

    if (!mxReaderThread.is())
    {
        std::unique_ptr<SvStream> pStream(new SvFileStream(msURL, StreamMode::READ));
        mxReaderThread = new datastreams::ReaderThread(std::move(pStream), maStartRange.aEnd.Col() - maStartRange.aStart.Col() + 1);
        mxReaderThread->launch();
    }
    mbRunning = true;
    maDocAccess.reset();

    maImportTimer.Start();
}

void DataStream::StopImport()
{
    if (!mbRunning)
        return;

    mbRunning = false;
    Refresh();
    maImportTimer.Stop();
}

void DataStream::SetRefreshOnEmptyLine( bool bVal )
{
    mbRefreshOnEmptyLine = bVal;
}

void DataStream::Refresh()
{
    Application::Yield();

    double fStart = getNow();

    // Hard recalc will repaint the grid area.
    mpDocShell->DoHardRecalc();
    mpDocShell->SetDocumentModified();

    fTimes[ DebugTime::Recalc ] = getNow() - fStart;

    mfLastRefreshTime = getNow();
    mnLinesSinceRefresh = 0;
}

void DataStream::MoveData()
{
    switch (meMove)
    {
        case RANGE_DOWN:
        {
            if (mnCurRow == maEndRange.aStart.Row())
                meMove = MOVE_UP;
        }
        break;
        case MOVE_UP:
        {
            mbIsUpdate = true;
            // Remove the top row and shift the remaining rows upward. Then
            // insert a new row at the end row position.
            ScRange aRange = maStartRange;
            aRange.aEnd = maEndRange.aEnd;
            maDocAccess.shiftRangeUp(aRange);
        }
        break;
        case MOVE_DOWN:
        {
            mbIsUpdate = true;
            // Remove the end row and shift the remaining rows downward by
            // inserting a new row at the top row.
            ScRange aRange = maStartRange;
            aRange.aEnd = maEndRange.aEnd;
            maDocAccess.shiftRangeDown(aRange);
        }
        break;
        case NO_MOVE:
        default:
            ;
    }
    if(mbIsFirst && mbIsUpdate)
    {
        sal_Int32 nStreamTimeout = officecfg::Office::Calc::DataStream::UpdateTimeout::get();
        maImportTimer.SetTimeout(nStreamTimeout);
        mbIsFirst = false;
    }
}

void DataStream::Text2Doc()
{
    Line aLine = ConsumeLine();
    if (aLine.maCells.empty() && mbRefreshOnEmptyLine)
    {
        // Empty line detected.  Trigger refresh and discard it.
        Refresh();
        return;
    }

    double fStart = getNow();

    MoveData();
    {
        SCCOL nCol = maStartRange.aStart.Col();
        const char* pLineHead = aLine.maLine.getStr();
        for (const Cell& rCell : aLine.maCells)
        {
            if (rCell.mbValue)
            {
                maDocAccess.setNumericCell(
                    ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()), rCell.mfValue);
            }
            else
            {
                maDocAccess.setStringCell(
                    ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()),
                    OUString(pLineHead+rCell.maStr.Pos, rCell.maStr.Size, RTL_TEXTENCODING_UTF8));
            }
            ++nCol;
        }
    }

    fTimes[ DebugTime::Import ] = getNow() - fStart;

    if (meMove == NO_MOVE)
        return;

    if (meMove == RANGE_DOWN)
    {
        ++mnCurRow;
//      mpDocShell->GetViewData().GetView()->AlignToCursor(
//              maStartRange.aStart.Col(), mnCurRow, SC_FOLLOW_JUMP);
    }

    if (getNow() - mfLastRefreshTime > 0.1 && mnLinesSinceRefresh > 200)
        // Refresh no more frequently than every 0.1 second, and wait until at
        // least we have processed 200 lines.
        Refresh();

    ++mnLinesSinceRefresh;
}

bool DataStream::ImportData()
{
    if (!mbValuesInLine)
        // We no longer support this mode. To be deleted later.
        return false;

    ScViewData* pViewData = ScDocShell::GetViewData();
    if (!pViewData)
        return false;

    if (pViewData->GetViewShell()->NeedsRepaint())
        return mbRunning;

    Text2Doc();
    return mbRunning;
}

IMPL_LINK_NOARG(DataStream, ImportTimerHdl, Timer *, void)
{
    if (ImportData())
        maImportTimer.Start();
}

// namespace sc

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=94 H=93 G=93

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.