Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/sc/source/ui/dataprovider/csvdataprovider.cxx
Line
Count
Source
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
10
#include <dataprovider.hxx>
11
#include <datatransformation.hxx>
12
#include <datamapper.hxx>
13
#include <exception>
14
#include <stringutil.hxx>
15
16
#include <tools/stream.hxx>
17
#include <utility>
18
#include <vcl/svapp.hxx>
19
#include <docsh.hxx>
20
#include <orcus/csv_parser.hpp>
21
22
namespace {
23
24
class CSVHandler
25
{
26
    ScDocument* mpDoc;
27
    SCCOL mnCol;
28
    SCROW mnRow;
29
30
public:
31
    CSVHandler(ScDocument* pDoc) :
32
0
        mpDoc(pDoc), mnCol(0), mnRow(0)
33
0
    {
34
0
    }
35
36
0
    static void begin_parse() {}
37
0
    static void end_parse() {}
38
0
    static void begin_row() {}
39
    void end_row()
40
0
    {
41
0
        ++mnRow;
42
0
        mnCol = 0;
43
0
    }
44
45
    void cell(std::string_view s, bool /*transient*/)
46
0
    {
47
0
        if (mnCol > mpDoc->MaxCol())
48
0
            return;
49
50
0
        double mfValue = 0.0;
51
0
        if (ScStringUtil::parseSimpleNumber(s.data(), s.size(), '.', ',', mfValue))
52
0
        {
53
0
            mpDoc->SetValue(mnCol, mnRow, 0, mfValue);
54
0
        }
55
0
        else
56
0
        {
57
0
            mpDoc->SetString(mnCol, mnRow, 0, OStringToOUString(s, RTL_TEXTENCODING_UTF8));
58
0
        }
59
60
0
        ++mnCol;
61
0
    }
62
};
63
64
}
65
66
namespace sc {
67
CSVFetchThread::CSVFetchThread(
68
    ScDocument& rDoc, OUString aURL, std::function<void()> aImportFinishedHdl,
69
    std::vector<std::shared_ptr<sc::DataTransformation>>&& rDataTransformations)
70
0
    : Thread("CSV Fetch Thread")
71
0
    , mrDocument(rDoc)
72
0
    , maURL(std::move(aURL))
73
0
    , mbTerminate(false)
74
0
    , maDataTransformations(std::move(rDataTransformations))
75
0
    , maImportFinishedHdl(std::move(aImportFinishedHdl))
76
0
    , mbIsParseError(false)
77
0
{
78
0
    maConfig.delimiters.push_back(',');
79
0
    maConfig.text_qualifier = '"';
80
0
}
81
82
CSVFetchThread::~CSVFetchThread()
83
0
{
84
0
}
85
86
bool CSVFetchThread::IsRequestedTerminate()
87
0
{
88
0
    return mbTerminate.load();
89
0
}
90
91
void CSVFetchThread::RequestTerminate()
92
0
{
93
0
    mbTerminate.store(true);
94
0
}
95
96
void CSVFetchThread::EndThread()
97
0
{
98
0
    RequestTerminate();
99
0
}
100
101
void CSVFetchThread::execute()
102
0
{
103
0
    OStringBuffer aBuffer(64000);
104
0
    DataProvider::FetchStreamFromURL(maURL, aBuffer);
105
0
    if (mbTerminate)
106
0
        return;
107
108
0
    CSVHandler aHdl(&mrDocument);
109
0
    orcus::csv_parser<CSVHandler> parser(aBuffer, aHdl, maConfig);
110
111
0
    try
112
0
    {
113
0
        parser.parse();
114
0
    }
115
0
    catch(const orcus::parse_error&)
116
0
    {
117
0
        mbIsParseError = true;
118
0
        mpLastException = std::current_exception();
119
0
        RequestTerminate();
120
0
        return;
121
0
    }
122
123
0
    for (const auto& itr : maDataTransformations)
124
0
    {
125
0
        itr->Transform(mrDocument);
126
0
    }
127
128
0
    SolarMutexGuard aGuard;
129
0
    maImportFinishedHdl();
130
0
}
131
132
CSVDataProvider::CSVDataProvider(ScDocument* pDoc, sc::ExternalDataSource& rDataSource):
133
0
    DataProvider(rDataSource),
134
0
    mpDocument(pDoc)
135
0
{
136
0
}
137
138
CSVDataProvider::~CSVDataProvider()
139
0
{
140
0
    if (mxCSVFetchThread.is())
141
0
    {
142
0
        SolarMutexReleaser aReleaser;
143
0
        mxCSVFetchThread->join();
144
0
    }
145
0
}
146
147
void CSVDataProvider::Import()
148
0
{
149
    // already importing data
150
0
    if (mpDoc)
151
0
        return;
152
153
0
    mpDoc.reset(new ScDocument(SCDOCMODE_CLIP));
154
0
    mpDoc->ResetClip(mpDocument, SCTAB(0));
155
0
    mxCSVFetchThread = new CSVFetchThread(*mpDoc, mrDataSource.getURL(),
156
0
        [this]() { this->ImportFinished(); }, std::vector(mrDataSource.getDataTransformation()));
157
158
0
    if (mbDeterministic)
159
0
    {
160
0
        SolarMutexReleaser aReleaser;
161
0
        mxCSVFetchThread->execute();
162
163
        // tdf#165658 An exception may have happened during the parsing of the file.
164
        // CSVFetchThread::execute catches the exception and stores it in case it is running in a
165
        // separate thread. Here we need to check if something wrong happened and then rethrow the
166
        // exception
167
0
        if (mxCSVFetchThread->IsParseError())
168
0
        {
169
0
            std::rethrow_exception(mxCSVFetchThread->GetLastException());
170
0
        }
171
0
    }
172
0
    else
173
0
    {
174
0
        mxCSVFetchThread->launch();
175
0
    }
176
0
}
177
178
void CSVDataProvider::ImportFinished()
179
0
{
180
0
    mrDataSource.getDBManager()->WriteToDoc(*mpDoc);
181
0
    mpDoc.reset();
182
0
    Refresh();
183
0
}
184
185
void CSVDataProvider::Refresh()
186
0
{
187
0
    ScDocShell* pDocShell = mpDocument->GetDocumentShell();
188
0
    if (pDocShell)
189
0
        pDocShell->SetDocumentModified();
190
0
}
191
192
const OUString& CSVDataProvider::GetURL() const
193
0
{
194
0
    return mrDataSource.getURL();
195
0
}
196
197
}
198
199
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */