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 <com/sun/star/ui/XUIElement.hpp>
15 #include <officecfg/Office/Common.hxx>
16 #include <osl/conditn.hxx>
18 #include <rtl/strbuf.hxx>
19 #include <salhelper/thread.hxx>
20 #include <sfx2/viewfrm.hxx>
21 #include <datastreamdlg.hxx>
23 #include <rangelst.hxx>
24 #include <tabvwsh.hxx>
25 #include <viewdata.hxx>
26 #include <stringutil.hxx>
27 #include <documentlinkmgr.hxx>
29 #include <config_orcus.h>
30 #include "officecfg/Office/Calc.hxx"
35 #define __ORCUS_STATIC_LIB
37 #include <orcus/csv_parser.hpp>
51 static double fTimes
[DEBUG_TIME_MAX
] = { 0.0, 0.0, 0.0 };
53 double datastream_get_time(int nIdx
)
55 if( nIdx
< 0 || nIdx
>= (int)SAL_N_ELEMENTS( fTimes
) )
57 return fTimes
[ nIdx
];
62 inline double getNow()
65 osl_getSystemTime(&now
);
66 return static_cast<double>(now
.Seconds
) + static_cast<double>(now
.Nanosec
) / 1000000000.0;
73 DataStream::Line
& mrLine
;
76 const char* mpLineHead
;
79 CSVHandler( DataStream::Line
& rLine
, size_t nColCount
) :
80 mrLine(rLine
), mnColCount(nColCount
), mnCols(0), mpLineHead(rLine
.maLine
.getStr()) {}
82 static void begin_parse() {}
83 static void end_parse() {}
84 static void begin_row() {}
85 static void end_row() {}
87 void cell(const char* p
, size_t n
)
89 if (mnCols
>= mnColCount
)
92 DataStream::Cell aCell
;
93 if (ScStringUtil::parseSimpleNumber(p
, n
, '.', ',', aCell
.mfValue
))
99 aCell
.mbValue
= false;
100 aCell
.maStr
.Pos
= std::distance(mpLineHead
, p
);
101 aCell
.maStr
.Size
= n
;
103 mrLine
.maCells
.push_back(aCell
);
113 namespace datastreams
{
115 void emptyLineQueue( std::queue
<DataStream::LinesType
*>& rQueue
)
117 while (!rQueue
.empty())
119 delete rQueue
.front();
124 class ReaderThread
: public salhelper::Thread
129 osl::Mutex maMtxTerminate
;
131 std::queue
<DataStream::LinesType
*> maPendingLines
;
132 std::queue
<DataStream::LinesType
*> maUsedLines
;
133 osl::Mutex maMtxLines
;
135 osl::Condition maCondReadStream
;
136 osl::Condition maCondConsume
;
139 orcus::csv::parser_config maConfig
;
144 ReaderThread(SvStream
*pData
, size_t nColCount
):
145 Thread("ReaderThread"),
147 mnColCount(nColCount
),
151 maConfig
.delimiters
.push_back(',');
152 maConfig
.text_qualifier
= '"';
156 virtual ~ReaderThread()
159 emptyLineQueue(maPendingLines
);
160 emptyLineQueue(maUsedLines
);
163 bool isTerminateRequested()
165 osl::MutexGuard
aGuard(maMtxTerminate
);
169 void requestTerminate()
171 osl::MutexGuard
aGuard(maMtxTerminate
);
178 maCondReadStream
.set();
181 void waitForNewLines()
183 maCondConsume
.wait();
184 maCondConsume
.reset();
187 DataStream::LinesType
* popNewLines()
189 DataStream::LinesType
* pLines
= maPendingLines
.front();
190 maPendingLines
.pop();
194 void resumeReadStream()
196 if (maPendingLines
.size() <= 4)
197 maCondReadStream
.set(); // start producer again
202 return !maPendingLines
.empty();
205 void pushUsedLines( DataStream::LinesType
* pLines
)
207 maUsedLines
.push(pLines
);
210 osl::Mutex
& getLinesMutex()
216 virtual void execute() SAL_OVERRIDE
218 while (!isTerminateRequested())
220 DataStream::LinesType
* pLines
= NULL
;
221 osl::ResettableMutexGuard
aGuard(maMtxLines
);
223 if (!maUsedLines
.empty())
225 // Re-use lines from previous runs.
226 pLines
= maUsedLines
.front();
228 aGuard
.clear(); // unlock
232 aGuard
.clear(); // unlock
233 pLines
= new DataStream::LinesType(10);
236 // Read & store new lines from stream.
237 for (size_t i
= 0, n
= pLines
->size(); i
< n
; ++i
)
239 DataStream::Line
& rLine
= (*pLines
)[i
];
240 rLine
.maCells
.clear();
241 mpStream
->ReadLine(rLine
.maLine
);
243 CSVHandler
aHdl(rLine
, mnColCount
);
244 orcus::csv_parser
<CSVHandler
> parser(rLine
.maLine
.getStr(), rLine
.maLine
.getLength(), aHdl
, maConfig
);
249 aGuard
.reset(); // lock
250 while (!isTerminateRequested() && maPendingLines
.size() >= 8)
252 // pause reading for a bit
253 aGuard
.clear(); // unlock
254 maCondReadStream
.wait();
255 maCondReadStream
.reset();
256 aGuard
.reset(); // lock
258 maPendingLines
.push(pLines
);
260 if (!mpStream
->good())
268 DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
270 DataStream::Cell::Cell( const Cell
& r
) : mbValue(r
.mbValue
)
276 maStr
.Pos
= r
.maStr
.Pos
;
277 maStr
.Size
= r
.maStr
.Size
;
281 void DataStream::MakeToolbarVisible()
283 css::uno::Reference
< css::frame::XFrame
> xFrame
=
284 ScDocShell::GetViewData()->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
288 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
292 css::uno::Reference
< css::frame::XLayoutManager
> xLayoutManager
;
293 xPropSet
->getPropertyValue("LayoutManager") >>= xLayoutManager
;
294 if (!xLayoutManager
.is())
297 const OUString
sResourceURL( "private:resource/toolbar/datastreams" );
298 css::uno::Reference
< css::ui::XUIElement
> xUIElement
= xLayoutManager
->getElement(sResourceURL
);
299 if (!xUIElement
.is())
301 xLayoutManager
->createElement( sResourceURL
);
302 xLayoutManager
->showElement( sResourceURL
);
306 DataStream
* DataStream::Set(
307 ScDocShell
*pShell
, const OUString
& rURL
, const ScRange
& rRange
,
308 sal_Int32 nLimit
, MoveType eMove
, sal_uInt32 nSettings
)
310 DataStream
* pLink
= new DataStream(pShell
, rURL
, rRange
, nLimit
, eMove
, nSettings
);
311 sc::DocumentLinkManager
& rMgr
= pShell
->GetDocument().GetDocLinkManager();
312 rMgr
.setDataStream(pLink
);
316 DataStream::DataStream(ScDocShell
*pShell
, const OUString
& rURL
, const ScRange
& rRange
,
317 sal_Int32 nLimit
, MoveType eMove
, sal_uInt32 nSettings
) :
319 maDocAccess(mpDocShell
->GetDocument()),
323 mbValuesInLine(false),
324 mbRefreshOnEmptyLine(false),
327 mnLinesSinceRefresh(0),
328 mfLastRefreshTime(0.0),
333 maImportTimer
.SetTimeout(0);
334 maImportTimer
.SetTimeoutHdl( LINK(this, DataStream
, ImportTimerHdl
) );
336 Decode(rURL
, rRange
, nLimit
, eMove
, nSettings
);
339 DataStream::~DataStream()
344 if (mxReaderThread
.is())
346 mxReaderThread
->endThread();
347 mxReaderThread
->join();
352 DataStream::Line
DataStream::ConsumeLine()
354 if (!mpLines
|| mnLinesCount
>= mpLines
->size())
357 if (mxReaderThread
->isTerminateRequested())
360 osl::ResettableMutexGuard
aGuard(mxReaderThread
->getLinesMutex());
362 mxReaderThread
->pushUsedLines(mpLines
);
364 while (!mxReaderThread
->hasNewLines())
366 aGuard
.clear(); // unlock
367 mxReaderThread
->waitForNewLines();
368 aGuard
.reset(); // lock
371 mpLines
= mxReaderThread
->popNewLines();
372 mxReaderThread
->resumeReadStream();
374 return mpLines
->at(mnLinesCount
++);
377 ScRange
DataStream::GetRange() const
379 ScRange aRange
= maStartRange
;
380 aRange
.aEnd
= maEndRange
.aEnd
;
384 void DataStream::Decode(const OUString
& rURL
, const ScRange
& rRange
,
385 sal_Int32 nLimit
, MoveType eMove
, const sal_uInt32 nSettings
)
391 mnSettings
= nSettings
;
393 mbValuesInLine
= true; // always true.
395 mnCurRow
= rRange
.aStart
.Row();
397 ScRange aRange
= rRange
;
398 if (aRange
.aStart
.Row() != aRange
.aEnd
.Row())
399 // We only allow this range to be one row tall.
400 aRange
.aEnd
.SetRow(aRange
.aStart
.Row());
402 maStartRange
= aRange
;
407 maEndRange
.aStart
.SetRow(MAXROW
);
412 maEndRange
.aStart
.IncRow(nLimit
-1);
413 if (maEndRange
.aStart
.Row() > MAXROW
)
414 maEndRange
.aStart
.SetRow(MAXROW
);
417 maEndRange
.aEnd
.SetRow(maEndRange
.aStart
.Row());
420 void DataStream::StartImport()
425 if (!mxReaderThread
.is())
427 SvStream
*pStream
= 0;
428 if (mnSettings
& SCRIPT_STREAM
)
429 pStream
= new SvScriptStream(msURL
);
431 pStream
= new SvFileStream(msURL
, StreamMode::READ
);
432 mxReaderThread
= new datastreams::ReaderThread(pStream
, maStartRange
.aEnd
.Col() - maStartRange
.aStart
.Col() + 1);
433 mxReaderThread
->launch();
438 maImportTimer
.Start();
441 void DataStream::StopImport()
448 maImportTimer
.Stop();
451 void DataStream::SetRefreshOnEmptyLine( bool bVal
)
453 mbRefreshOnEmptyLine
= bVal
;
456 void DataStream::Refresh()
458 Application::Yield();
460 double fStart
= getNow();
462 // Hard recalc will repaint the grid area.
463 mpDocShell
->DoHardRecalc(true);
464 mpDocShell
->SetDocumentModified(true);
466 fTimes
[ DEBUG_TIME_RECALC
] = getNow() - fStart
;
468 mfLastRefreshTime
= getNow();
469 mnLinesSinceRefresh
= 0;
472 void DataStream::MoveData()
478 if (mnCurRow
== maEndRange
.aStart
.Row())
485 // Remove the top row and shift the remaining rows upward. Then
486 // insert a new row at the end row position.
487 ScRange aRange
= maStartRange
;
488 aRange
.aEnd
= maEndRange
.aEnd
;
489 maDocAccess
.shiftRangeUp(aRange
);
495 // Remove the end row and shift the remaining rows downward by
496 // inserting a new row at the top row.
497 ScRange aRange
= maStartRange
;
498 aRange
.aEnd
= maEndRange
.aEnd
;
499 maDocAccess
.shiftRangeDown(aRange
);
506 if(mbIsFirst
&& mbIsUpdate
)
508 sal_Int32 nStreamTimeout
= officecfg::Office::Calc::DataStream::UpdateTimeout::get();
509 maImportTimer
.SetTimeout(nStreamTimeout
);
516 void DataStream::Text2Doc()
518 Line aLine
= ConsumeLine();
519 if (aLine
.maCells
.empty() && mbRefreshOnEmptyLine
)
521 // Empty line detected. Trigger refresh and discard it.
526 double fStart
= getNow();
530 std::vector
<Cell
>::const_iterator it
= aLine
.maCells
.begin(), itEnd
= aLine
.maCells
.end();
531 SCCOL nCol
= maStartRange
.aStart
.Col();
532 const char* pLineHead
= aLine
.maLine
.getStr();
533 for (; it
!= itEnd
; ++it
, ++nCol
)
535 const Cell
& rCell
= *it
;
538 maDocAccess
.setNumericCell(
539 ScAddress(nCol
, mnCurRow
, maStartRange
.aStart
.Tab()), rCell
.mfValue
);
543 maDocAccess
.setStringCell(
544 ScAddress(nCol
, mnCurRow
, maStartRange
.aStart
.Tab()),
545 OUString(pLineHead
+rCell
.maStr
.Pos
, rCell
.maStr
.Size
, RTL_TEXTENCODING_UTF8
));
550 fTimes
[ DEBUG_TIME_IMPORT
] = getNow() - fStart
;
552 if (meMove
== NO_MOVE
)
555 if (meMove
== RANGE_DOWN
)
558 // mpDocShell->GetViewData().GetView()->AlignToCursor(
559 // maStartRange.aStart.Col(), mnCurRow, SC_FOLLOW_JUMP);
562 if (getNow() - mfLastRefreshTime
> 0.1 && mnLinesSinceRefresh
> 200)
563 // Refresh no more frequently than every 0.1 second, and wait until at
564 // least we have processed 200 lines.
567 ++mnLinesSinceRefresh
;
572 void DataStream::Text2Doc() {}
576 bool DataStream::ImportData()
579 // We no longer support this mode. To be deleted later.
582 if (ScDocShell::GetViewData()->GetViewShell()->NeedsRepaint())
589 IMPL_LINK_NOARG_TYPED(DataStream
, ImportTimerHdl
, Timer
*, void)
592 maImportTimer
.Start();
597 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */