/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: */ |