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>
33 namespace com::sun::star::ui
{ class XUIElement
; }
37 static o3tl::enumarray
<DebugTime
, double> fTimes
{ 0.0, 0.0, 0.0 };
39 double datastream_get_time(DebugTime nIdx
)
41 return fTimes
[ nIdx
];
49 osl_getSystemTime(&now
);
50 return static_cast<double>(now
.Seconds
) + static_cast<double>(now
.Nanosec
) / 1000000000.0;
55 DataStream::Line
& mrLine
;
58 const char* mpLineHead
;
61 CSVHandler( DataStream::Line
& rLine
, size_t nColCount
) :
62 mrLine(rLine
), mnColCount(nColCount
), mnCols(0), mpLineHead(rLine
.maLine
.getStr()) {}
64 static void begin_parse() {}
65 static void end_parse() {}
66 static void begin_row() {}
67 static void end_row() {}
69 void cell(const char* p
, size_t n
, bool /*transient*/)
71 if (mnCols
>= mnColCount
)
74 DataStream::Cell aCell
;
75 if (ScStringUtil::parseSimpleNumber(p
, n
, '.', ',', aCell
.mfValue
))
81 aCell
.mbValue
= false;
82 aCell
.maStr
.Pos
= std::distance(mpLineHead
, p
);
85 mrLine
.maCells
.push_back(aCell
);
93 namespace datastreams
{
95 class ReaderThread
: public salhelper::Thread
97 std::unique_ptr
<SvStream
> mpStream
;
100 osl::Mutex maMtxTerminate
;
102 std::queue
<std::unique_ptr
<DataStream::LinesType
>> maPendingLines
;
103 std::queue
<std::unique_ptr
<DataStream::LinesType
>> maUsedLines
;
104 osl::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 osl::MutexGuard
aGuard(maMtxTerminate
);
129 void requestTerminate()
131 osl::MutexGuard
aGuard(maMtxTerminate
);
138 maCondReadStream
.set();
141 void waitForNewLines()
143 maCondConsume
.wait();
144 maCondConsume
.reset();
147 std::unique_ptr
<DataStream::LinesType
> popNewLines()
149 auto pLines
= std::move(maPendingLines
.front());
150 maPendingLines
.pop();
154 void resumeReadStream()
156 if (maPendingLines
.size() <= 4)
157 maCondReadStream
.set(); // start producer again
160 bool hasNewLines() const
162 return !maPendingLines
.empty();
165 void pushUsedLines( std::unique_ptr
<DataStream::LinesType
> pLines
)
167 maUsedLines
.push(std::move(pLines
));
170 osl::Mutex
& getLinesMutex()
176 virtual void execute() override
178 while (!isTerminateRequested())
180 std::unique_ptr
<DataStream::LinesType
> pLines
;
181 osl::ResettableMutexGuard
aGuard(maMtxLines
);
183 if (!maUsedLines
.empty())
185 // Re-use lines from previous runs.
186 pLines
= std::move(maUsedLines
.front());
188 aGuard
.clear(); // unlock
192 aGuard
.clear(); // unlock
193 pLines
.reset(new DataStream::LinesType(10));
196 // Read & store new lines from stream.
197 for (DataStream::Line
& rLine
: *pLines
)
199 rLine
.maCells
.clear();
200 mpStream
->ReadLine(rLine
.maLine
);
201 CSVHandler
aHdl(rLine
, mnColCount
);
202 orcus::csv_parser
<CSVHandler
> parser(rLine
.maLine
.getStr(), rLine
.maLine
.getLength(), aHdl
, maConfig
);
206 aGuard
.reset(); // lock
207 while (!isTerminateRequested() && maPendingLines
.size() >= 8)
209 // pause reading for a bit
210 aGuard
.clear(); // unlock
211 maCondReadStream
.wait();
212 maCondReadStream
.reset();
213 aGuard
.reset(); // lock
215 maPendingLines
.push(std::move(pLines
));
217 if (!mpStream
->good())
225 DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
227 DataStream::Cell::Cell( const Cell
& r
) : mbValue(r
.mbValue
)
233 maStr
.Pos
= r
.maStr
.Pos
;
234 maStr
.Size
= r
.maStr
.Size
;
238 void DataStream::MakeToolbarVisible()
240 ScViewData
* pViewData
= ScDocShell::GetViewData();
244 css::uno::Reference
< css::frame::XFrame
> xFrame
=
245 pViewData
->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
249 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
253 css::uno::Reference
< css::frame::XLayoutManager
> xLayoutManager
;
254 xPropSet
->getPropertyValue("LayoutManager") >>= xLayoutManager
;
255 if (!xLayoutManager
.is())
258 static const OUStringLiteral
sResourceURL( u
"private:resource/toolbar/datastreams" );
259 css::uno::Reference
< css::ui::XUIElement
> xUIElement
= xLayoutManager
->getElement(sResourceURL
);
260 if (!xUIElement
.is())
262 xLayoutManager
->createElement( sResourceURL
);
263 xLayoutManager
->showElement( sResourceURL
);
267 DataStream
* DataStream::Set(
268 ScDocShell
*pShell
, const OUString
& rURL
, const ScRange
& rRange
,
269 sal_Int32 nLimit
, MoveType eMove
, sal_uInt32 nSettings
)
271 DataStream
* pLink
= new DataStream(pShell
, rURL
, rRange
, nLimit
, eMove
, nSettings
);
272 sc::DocumentLinkManager
& rMgr
= pShell
->GetDocument().GetDocLinkManager();
273 rMgr
.setDataStream(pLink
);
277 DataStream::DataStream(ScDocShell
*pShell
, const OUString
& rURL
, const ScRange
& rRange
,
278 sal_Int32 nLimit
, MoveType eMove
, sal_uInt32 nSettings
) :
280 maDocAccess(mpDocShell
->GetDocument()),
284 mbValuesInLine(false),
285 mbRefreshOnEmptyLine(false),
287 mnLinesSinceRefresh(0),
288 mfLastRefreshTime(0.0),
290 maImportTimer("sc DataStream maImportTimer"),
294 maImportTimer
.SetTimeout(0);
295 maImportTimer
.SetInvokeHandler( LINK(this, DataStream
, ImportTimerHdl
) );
297 Decode(rURL
, rRange
, nLimit
, eMove
, nSettings
);
300 DataStream::~DataStream()
305 if (mxReaderThread
.is())
307 mxReaderThread
->endThread();
308 mxReaderThread
->join();
313 DataStream::Line
DataStream::ConsumeLine()
315 if (!mpLines
|| mnLinesCount
>= mpLines
->size())
318 if (mxReaderThread
->isTerminateRequested())
321 osl::ResettableMutexGuard
aGuard(mxReaderThread
->getLinesMutex());
323 mxReaderThread
->pushUsedLines(std::move(mpLines
));
325 while (!mxReaderThread
->hasNewLines())
327 aGuard
.clear(); // unlock
328 mxReaderThread
->waitForNewLines();
329 aGuard
.reset(); // lock
332 mpLines
= mxReaderThread
->popNewLines();
333 mxReaderThread
->resumeReadStream();
335 return mpLines
->at(mnLinesCount
++);
338 ScRange
DataStream::GetRange() const
340 ScRange aRange
= maStartRange
;
341 aRange
.aEnd
= maEndRange
.aEnd
;
345 void DataStream::Decode(const OUString
& rURL
, const ScRange
& rRange
,
346 sal_Int32 nLimit
, MoveType eMove
, const sal_uInt32 nSettings
)
351 mnSettings
= nSettings
;
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: */