1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
10 #include <datastream.hxx>
11 #include <datastreamgettime.hxx>
13 #include <com/sun/star/frame/XLayoutManager.hpp>
14 #include <osl/conditn.hxx>
16 #include <salhelper/thread.hxx>
17 #include <sfx2/viewfrm.hxx>
18 #include <tools/stream.hxx>
19 #include <vcl/svapp.hxx>
21 #include <tabvwsh.hxx>
22 #include <viewdata.hxx>
23 #include <stringutil.hxx>
24 #include <documentlinkmgr.hxx>
25 #include <o3tl/enumarray.hxx>
27 #include <officecfg/Office/Calc.hxx>
29 #include <orcus/csv_parser.hpp>
34 namespace com::sun::star::ui
{ class XUIElement
; }
38 static o3tl::enumarray
<DebugTime
, double> fTimes
{ 0.0, 0.0, 0.0 };
40 double datastream_get_time(DebugTime nIdx
)
42 return fTimes
[ nIdx
];
50 osl_getSystemTime(&now
);
51 return static_cast<double>(now
.Seconds
) + static_cast<double>(now
.Nanosec
) / 1000000000.0;
56 DataStream::Line
& mrLine
;
59 const char* mpLineHead
;
62 CSVHandler( DataStream::Line
& rLine
, size_t nColCount
) :
63 mrLine(rLine
), mnColCount(nColCount
), mnCols(0), mpLineHead(rLine
.maLine
.getStr()) {}
65 static void begin_parse() {}
66 static void end_parse() {}
67 static void begin_row() {}
68 static void end_row() {}
70 void cell(std::string_view s
, bool /*transient*/)
72 if (mnCols
>= mnColCount
)
75 DataStream::Cell aCell
;
76 if (ScStringUtil::parseSimpleNumber(s
.data(), s
.size(), '.', ',', aCell
.mfValue
))
82 aCell
.mbValue
= false;
83 aCell
.maStr
.Pos
= std::distance(mpLineHead
, s
.data());
84 aCell
.maStr
.Size
= s
.size();
86 mrLine
.maCells
.push_back(aCell
);
94 namespace datastreams
{
96 class ReaderThread
: public salhelper::Thread
98 std::unique_ptr
<SvStream
> mpStream
;
100 std::atomic
<bool> mbTerminate
;
102 std::queue
<DataStream::LinesType
> maPendingLines
;
103 std::queue
<DataStream::LinesType
> maUsedLines
;
104 std::mutex maMtxLines
;
106 osl::Condition maCondReadStream
;
107 osl::Condition maCondConsume
;
109 orcus::csv::parser_config maConfig
;
113 ReaderThread(std::unique_ptr
<SvStream
> pData
, size_t nColCount
):
114 Thread("ReaderThread"),
115 mpStream(std::move(pData
)),
116 mnColCount(nColCount
),
119 maConfig
.delimiters
.push_back(',');
120 maConfig
.text_qualifier
= '"';
123 bool isTerminateRequested()
125 return mbTerminate
.load();
128 void requestTerminate()
130 mbTerminate
.store(true);
136 maCondReadStream
.set();
139 void waitForNewLines()
141 maCondConsume
.wait();
142 maCondConsume
.reset();
145 DataStream::LinesType
popNewLines()
147 auto pLines
= std::move(maPendingLines
.front());
148 maPendingLines
.pop();
152 void resumeReadStream()
154 if (maPendingLines
.size() <= 4)
155 maCondReadStream
.set(); // start producer again
158 bool hasNewLines() const
160 return !maPendingLines
.empty();
163 void pushUsedLines( DataStream::LinesType pLines
)
165 maUsedLines
.push(std::move(pLines
));
168 std::mutex
& getLinesMutex()
174 virtual void execute() override
176 while (!isTerminateRequested())
178 std::optional
<DataStream::LinesType
> oLines
;
179 std::unique_lock
aGuard(getLinesMutex());
181 if (!maUsedLines
.empty())
183 // Re-use lines from previous runs.
184 oLines
= std::move(maUsedLines
.front());
186 aGuard
.unlock(); // unlock
190 aGuard
.unlock(); // unlock
194 // Read & store new lines from stream.
195 for (DataStream::Line
& rLine
: *oLines
)
197 rLine
.maCells
.clear();
198 mpStream
->ReadLine(rLine
.maLine
);
199 CSVHandler
aHdl(rLine
, mnColCount
);
200 orcus::csv_parser
<CSVHandler
> parser(rLine
.maLine
, aHdl
, maConfig
);
204 aGuard
.lock(); // lock
205 while (!isTerminateRequested() && maPendingLines
.size() >= 8)
207 // pause reading for a bit
208 aGuard
.unlock(); // unlock
209 maCondReadStream
.wait();
210 maCondReadStream
.reset();
211 aGuard
.lock(); // lock
213 maPendingLines
.push(std::move(*oLines
));
215 if (!mpStream
->good())
223 DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
225 DataStream::Cell::Cell( const Cell
& r
) : mbValue(r
.mbValue
)
231 maStr
.Pos
= r
.maStr
.Pos
;
232 maStr
.Size
= r
.maStr
.Size
;
236 void DataStream::MakeToolbarVisible()
238 ScViewData
* pViewData
= ScDocShell::GetViewData();
242 css::uno::Reference
< css::frame::XFrame
> xFrame
=
243 pViewData
->GetViewShell()->GetViewFrame().GetFrame().GetFrameInterface();
247 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
251 css::uno::Reference
< css::frame::XLayoutManager
> xLayoutManager
;
252 xPropSet
->getPropertyValue(u
"LayoutManager"_ustr
) >>= xLayoutManager
;
253 if (!xLayoutManager
.is())
256 static constexpr OUString
sResourceURL( u
"private:resource/toolbar/datastreams"_ustr
);
257 css::uno::Reference
< css::ui::XUIElement
> xUIElement
= xLayoutManager
->getElement(sResourceURL
);
258 if (!xUIElement
.is())
260 xLayoutManager
->createElement( sResourceURL
);
261 xLayoutManager
->showElement( sResourceURL
);
265 DataStream
* DataStream::Set(
266 ScDocShell
*pShell
, const OUString
& rURL
, const ScRange
& rRange
,
267 sal_Int32 nLimit
, MoveType eMove
)
269 DataStream
* pLink
= new DataStream(pShell
, rURL
, rRange
, nLimit
, eMove
);
270 sc::DocumentLinkManager
& rMgr
= pShell
->GetDocument().GetDocLinkManager();
271 rMgr
.setDataStream(pLink
);
275 DataStream::DataStream(ScDocShell
*pShell
, const OUString
& rURL
, const ScRange
& rRange
,
276 sal_Int32 nLimit
, MoveType eMove
) :
278 maDocAccess(mpDocShell
->GetDocument()),
282 mbValuesInLine(false),
283 mbRefreshOnEmptyLine(false),
285 mnLinesSinceRefresh(0),
286 mfLastRefreshTime(0.0),
288 maImportTimer("sc DataStream maImportTimer"),
292 maImportTimer
.SetTimeout(0);
293 maImportTimer
.SetInvokeHandler( LINK(this, DataStream
, ImportTimerHdl
) );
295 Decode(rURL
, rRange
, nLimit
, eMove
);
298 DataStream::~DataStream()
303 if (mxReaderThread
.is())
305 mxReaderThread
->endThread();
306 mxReaderThread
->join();
311 DataStream::Line
DataStream::ConsumeLine()
313 if (!moLines
|| mnLinesCount
>= moLines
->size())
316 if (mxReaderThread
->isTerminateRequested())
319 std::unique_lock
aGuard(mxReaderThread
->getLinesMutex());
322 mxReaderThread
->pushUsedLines(std::move(*moLines
));
326 while (!mxReaderThread
->hasNewLines())
328 aGuard
.unlock(); // unlock
329 mxReaderThread
->waitForNewLines();
330 aGuard
.lock(); // lock
333 moLines
= mxReaderThread
->popNewLines();
334 mxReaderThread
->resumeReadStream();
336 return moLines
->at(mnLinesCount
++);
339 ScRange
DataStream::GetRange() const
341 ScRange aRange
= maStartRange
;
342 aRange
.aEnd
= maEndRange
.aEnd
;
346 void DataStream::Decode(const OUString
& rURL
, const ScRange
& rRange
,
347 sal_Int32 nLimit
, MoveType eMove
)
353 mbValuesInLine
= true; // always true.
355 mnCurRow
= rRange
.aStart
.Row();
357 ScRange aRange
= rRange
;
358 if (aRange
.aStart
.Row() != aRange
.aEnd
.Row())
359 // We only allow this range to be one row tall.
360 aRange
.aEnd
.SetRow(aRange
.aStart
.Row());
362 maStartRange
= aRange
;
364 const auto & rDoc
= mpDocShell
->GetDocument();
368 maEndRange
.aStart
.SetRow(rDoc
.MaxRow());
373 maEndRange
.aStart
.IncRow(nLimit
-1);
374 if (maEndRange
.aStart
.Row() > rDoc
.MaxRow())
375 maEndRange
.aStart
.SetRow(rDoc
.MaxRow());
378 maEndRange
.aEnd
.SetRow(maEndRange
.aStart
.Row());
381 void DataStream::StartImport()
386 if (!mxReaderThread
.is())
388 std::unique_ptr
<SvStream
> pStream(new SvFileStream(msURL
, StreamMode::READ
));
389 mxReaderThread
= new datastreams::ReaderThread(std::move(pStream
), maStartRange
.aEnd
.Col() - maStartRange
.aStart
.Col() + 1);
390 mxReaderThread
->launch();
395 maImportTimer
.Start();
398 void DataStream::StopImport()
405 maImportTimer
.Stop();
408 void DataStream::SetRefreshOnEmptyLine( bool bVal
)
410 mbRefreshOnEmptyLine
= bVal
;
413 void DataStream::Refresh()
415 Application::Yield();
417 double fStart
= getNow();
419 // Hard recalc will repaint the grid area.
420 mpDocShell
->DoHardRecalc();
421 mpDocShell
->SetDocumentModified();
423 fTimes
[ DebugTime::Recalc
] = getNow() - fStart
;
425 mfLastRefreshTime
= getNow();
426 mnLinesSinceRefresh
= 0;
429 void DataStream::MoveData()
435 if (mnCurRow
== maEndRange
.aStart
.Row())
442 // Remove the top row and shift the remaining rows upward. Then
443 // insert a new row at the end row position.
444 ScRange aRange
= maStartRange
;
445 aRange
.aEnd
= maEndRange
.aEnd
;
446 maDocAccess
.shiftRangeUp(aRange
);
452 // Remove the end row and shift the remaining rows downward by
453 // inserting a new row at the top row.
454 ScRange aRange
= maStartRange
;
455 aRange
.aEnd
= maEndRange
.aEnd
;
456 maDocAccess
.shiftRangeDown(aRange
);
463 if(mbIsFirst
&& mbIsUpdate
)
465 sal_Int32 nStreamTimeout
= officecfg::Office::Calc::DataStream::UpdateTimeout::get();
466 maImportTimer
.SetTimeout(nStreamTimeout
);
471 void DataStream::Text2Doc()
473 Line aLine
= ConsumeLine();
474 if (aLine
.maCells
.empty() && mbRefreshOnEmptyLine
)
476 // Empty line detected. Trigger refresh and discard it.
481 double fStart
= getNow();
485 SCCOL nCol
= maStartRange
.aStart
.Col();
486 const char* pLineHead
= aLine
.maLine
.getStr();
487 for (const Cell
& rCell
: aLine
.maCells
)
491 maDocAccess
.setNumericCell(
492 ScAddress(nCol
, mnCurRow
, maStartRange
.aStart
.Tab()), rCell
.mfValue
);
496 maDocAccess
.setStringCell(
497 ScAddress(nCol
, mnCurRow
, maStartRange
.aStart
.Tab()),
498 OUString(pLineHead
+rCell
.maStr
.Pos
, rCell
.maStr
.Size
, RTL_TEXTENCODING_UTF8
));
504 fTimes
[ DebugTime::Import
] = getNow() - fStart
;
506 if (meMove
== NO_MOVE
)
509 if (meMove
== RANGE_DOWN
)
512 // mpDocShell->GetViewData().GetView()->AlignToCursor(
513 // maStartRange.aStart.Col(), mnCurRow, SC_FOLLOW_JUMP);
516 if (getNow() - mfLastRefreshTime
> 0.1 && mnLinesSinceRefresh
> 200)
517 // Refresh no more frequently than every 0.1 second, and wait until at
518 // least we have processed 200 lines.
521 ++mnLinesSinceRefresh
;
524 bool DataStream::ImportData()
527 // We no longer support this mode. To be deleted later.
530 ScViewData
* pViewData
= ScDocShell::GetViewData();
534 if (pViewData
->GetViewShell()->NeedsRepaint())
541 IMPL_LINK_NOARG(DataStream
, ImportTimerHdl
, Timer
*, void)
544 maImportTimer
.Start();
549 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */