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>
33 #define __ORCUS_STATIC_LIB
35 #include <orcus/csv_parser.hpp>
49 static double fTimes
[DEBUG_TIME_MAX
] = { 0.0, 0.0, 0.0 };
51 double datastream_get_time(int nIdx
)
53 if( nIdx
< 0 || nIdx
>= (int)SAL_N_ELEMENTS( fTimes
) )
55 return fTimes
[ nIdx
];
60 inline double getNow()
63 osl_getSystemTime(&now
);
64 return static_cast<double>(now
.Seconds
) + static_cast<double>(now
.Nanosec
) / 1000000000.0;
71 DataStream::Line
& mrLine
;
74 const char* mpLineHead
;
77 CSVHandler( DataStream::Line
& rLine
, size_t nColCount
) :
78 mrLine(rLine
), mnColCount(nColCount
), mnCols(0), mpLineHead(rLine
.maLine
.getStr()) {}
85 void cell(const char* p
, size_t n
)
87 if (mnCols
>= mnColCount
)
90 DataStream::Cell aCell
;
91 if (ScStringUtil::parseSimpleNumber(p
, n
, '.', ',', aCell
.mfValue
))
97 aCell
.mbValue
= false;
98 aCell
.maStr
.Pos
= std::distance(mpLineHead
, p
);
101 mrLine
.maCells
.push_back(aCell
);
111 namespace datastreams
{
113 void emptyLineQueue( std::queue
<DataStream::LinesType
*>& rQueue
)
115 while (!rQueue
.empty())
117 delete rQueue
.front();
122 class ReaderThread
: public salhelper::Thread
127 osl::Mutex maMtxTerminate
;
129 std::queue
<DataStream::LinesType
*> maPendingLines
;
130 std::queue
<DataStream::LinesType
*> maUsedLines
;
131 osl::Mutex maMtxLines
;
133 osl::Condition maCondReadStream
;
134 osl::Condition maCondConsume
;
137 orcus::csv::parser_config maConfig
;
142 ReaderThread(SvStream
*pData
, size_t nColCount
):
143 Thread("ReaderThread"),
145 mnColCount(nColCount
),
149 maConfig
.delimiters
.push_back(',');
150 maConfig
.text_qualifier
= '"';
154 virtual ~ReaderThread()
157 emptyLineQueue(maPendingLines
);
158 emptyLineQueue(maUsedLines
);
161 bool isTerminateRequested()
163 osl::MutexGuard
aGuard(maMtxTerminate
);
167 void requestTerminate()
169 osl::MutexGuard
aGuard(maMtxTerminate
);
176 maCondReadStream
.set();
179 void waitForNewLines()
181 maCondConsume
.wait();
182 maCondConsume
.reset();
185 DataStream::LinesType
* popNewLines()
187 DataStream::LinesType
* pLines
= maPendingLines
.front();
188 maPendingLines
.pop();
192 void resumeReadStream()
194 if (maPendingLines
.size() <= 4)
195 maCondReadStream
.set(); // start producer again
200 return !maPendingLines
.empty();
203 void pushUsedLines( DataStream::LinesType
* pLines
)
205 maUsedLines
.push(pLines
);
208 osl::Mutex
& getLinesMutex()
214 virtual void execute() SAL_OVERRIDE
216 while (!isTerminateRequested())
218 DataStream::LinesType
* pLines
= NULL
;
219 osl::ResettableMutexGuard
aGuard(maMtxLines
);
221 if (!maUsedLines
.empty())
223 // Re-use lines from previous runs.
224 pLines
= maUsedLines
.front();
226 aGuard
.clear(); // unlock
230 aGuard
.clear(); // unlock
231 pLines
= new DataStream::LinesType(10);
234 // Read & store new lines from stream.
235 for (size_t i
= 0, n
= pLines
->size(); i
< n
; ++i
)
237 DataStream::Line
& rLine
= (*pLines
)[i
];
238 rLine
.maCells
.clear();
239 mpStream
->ReadLine(rLine
.maLine
);
241 CSVHandler
aHdl(rLine
, mnColCount
);
242 orcus::csv_parser
<CSVHandler
> parser(rLine
.maLine
.getStr(), rLine
.maLine
.getLength(), aHdl
, maConfig
);
247 aGuard
.reset(); // lock
248 while (!isTerminateRequested() && maPendingLines
.size() >= 8)
250 // pause reading for a bit
251 aGuard
.clear(); // unlock
252 maCondReadStream
.wait();
253 maCondReadStream
.reset();
254 aGuard
.reset(); // lock
256 maPendingLines
.push(pLines
);
258 if (!mpStream
->good())
266 DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
268 DataStream::Cell::Cell( const Cell
& r
) : mbValue(r
.mbValue
)
274 maStr
.Pos
= r
.maStr
.Pos
;
275 maStr
.Size
= r
.maStr
.Size
;
279 void DataStream::MakeToolbarVisible()
281 css::uno::Reference
< css::frame::XFrame
> xFrame
=
282 ScDocShell::GetViewData()->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
286 css::uno::Reference
< css::beans::XPropertySet
> xPropSet(xFrame
, css::uno::UNO_QUERY
);
290 css::uno::Reference
< css::frame::XLayoutManager
> xLayoutManager
;
291 xPropSet
->getPropertyValue("LayoutManager") >>= xLayoutManager
;
292 if (!xLayoutManager
.is())
295 const OUString
sResourceURL( "private:resource/toolbar/datastreams" );
296 css::uno::Reference
< css::ui::XUIElement
> xUIElement
= xLayoutManager
->getElement(sResourceURL
);
297 if (!xUIElement
.is())
299 xLayoutManager
->createElement( sResourceURL
);
300 xLayoutManager
->showElement( sResourceURL
);
304 DataStream
* DataStream::Set(
305 ScDocShell
*pShell
, const OUString
& rURL
, const ScRange
& rRange
,
306 sal_Int32 nLimit
, MoveType eMove
, sal_uInt32 nSettings
)
308 DataStream
* pLink
= new DataStream(pShell
, rURL
, rRange
, nLimit
, eMove
, nSettings
);
309 sc::DocumentLinkManager
& rMgr
= pShell
->GetDocument()->GetDocLinkManager();
310 rMgr
.setDataStream(pLink
);
314 DataStream::DataStream(ScDocShell
*pShell
, const OUString
& rURL
, const ScRange
& rRange
,
315 sal_Int32 nLimit
, MoveType eMove
, sal_uInt32 nSettings
) :
317 mpDoc(mpDocShell
->GetDocument()),
322 mbValuesInLine(false),
323 mbRefreshOnEmptyLine(false),
326 mnLinesSinceRefresh(0),
327 mfLastRefreshTime(0.0),
330 maImportTimer
.SetTimeout(0);
331 maImportTimer
.SetTimeoutHdl( LINK(this, DataStream
, ImportTimerHdl
) );
333 Decode(rURL
, rRange
, nLimit
, eMove
, nSettings
);
336 DataStream::~DataStream()
341 if (mxReaderThread
.is())
343 mxReaderThread
->endThread();
344 mxReaderThread
->join();
349 DataStream::Line
DataStream::ConsumeLine()
351 if (!mpLines
|| mnLinesCount
>= mpLines
->size())
354 if (mxReaderThread
->isTerminateRequested())
357 osl::ResettableMutexGuard
aGuard(mxReaderThread
->getLinesMutex());
359 mxReaderThread
->pushUsedLines(mpLines
);
361 while (!mxReaderThread
->hasNewLines())
363 aGuard
.clear(); // unlock
364 mxReaderThread
->waitForNewLines();
365 aGuard
.reset(); // lock
368 mpLines
= mxReaderThread
->popNewLines();
369 mxReaderThread
->resumeReadStream();
371 return mpLines
->at(mnLinesCount
++);
374 ScRange
DataStream::GetRange() const
376 ScRange aRange
= maStartRange
;
377 aRange
.aEnd
= maEndRange
.aEnd
;
381 bool DataStream::IsRefreshOnEmptyLine() const
383 return mbRefreshOnEmptyLine
;
386 DataStream::MoveType
DataStream::GetMove() const
391 void DataStream::Decode(const OUString
& rURL
, const ScRange
& rRange
,
392 sal_Int32 nLimit
, MoveType eMove
, const sal_uInt32 nSettings
)
398 mnSettings
= nSettings
;
400 mbValuesInLine
= true; // always true.
402 mnCurRow
= rRange
.aStart
.Row();
404 ScRange aRange
= rRange
;
405 if (aRange
.aStart
.Row() != aRange
.aEnd
.Row())
406 // We only allow this range to be one row tall.
407 aRange
.aEnd
.SetRow(aRange
.aStart
.Row());
409 maStartRange
= aRange
;
414 maEndRange
.aStart
.SetRow(MAXROW
);
419 maEndRange
.aStart
.IncRow(nLimit
-1);
420 if (maEndRange
.aStart
.Row() > MAXROW
)
421 maEndRange
.aStart
.SetRow(MAXROW
);
424 maEndRange
.aEnd
.SetRow(maEndRange
.aStart
.Row());
427 void DataStream::StartImport()
432 if (!mxReaderThread
.is())
434 SvStream
*pStream
= 0;
435 if (mnSettings
& SCRIPT_STREAM
)
436 pStream
= new SvScriptStream(msURL
);
438 pStream
= new SvFileStream(msURL
, STREAM_READ
);
439 mxReaderThread
= new datastreams::ReaderThread(pStream
, maStartRange
.aEnd
.Col() - maStartRange
.aStart
.Col() + 1);
440 mxReaderThread
->launch();
445 maImportTimer
.Start();
448 void DataStream::StopImport()
455 maImportTimer
.Stop();
458 void DataStream::SetRefreshOnEmptyLine( bool bVal
)
460 mbRefreshOnEmptyLine
= bVal
;
463 void DataStream::Refresh()
465 Application::Yield();
467 double fStart
= getNow();
469 // Hard recalc will repaint the grid area.
470 mpDocShell
->DoHardRecalc(true);
471 mpDocShell
->SetDocumentModified(true);
473 fTimes
[ DEBUG_TIME_RECALC
] = getNow() - fStart
;
475 mfLastRefreshTime
= getNow();
476 mnLinesSinceRefresh
= 0;
479 void DataStream::MoveData()
485 if (mnCurRow
== maEndRange
.aStart
.Row())
491 // Remove the top row and shift the remaining rows upward. Then
492 // insert a new row at the end row position.
493 ScRange aRange
= maStartRange
;
494 aRange
.aEnd
= maEndRange
.aEnd
;
495 maDocAccess
.shiftRangeUp(aRange
);
500 // Remove the end row and shift the remaining rows downward by
501 // inserting a new row at the top row.
502 ScRange aRange
= maStartRange
;
503 aRange
.aEnd
= maEndRange
.aEnd
;
504 maDocAccess
.shiftRangeDown(aRange
);
515 void DataStream::Text2Doc()
517 Line aLine
= ConsumeLine();
518 if (aLine
.maCells
.empty() && mbRefreshOnEmptyLine
)
520 // Empty line detected. Trigger refresh and discard it.
525 double fStart
= getNow();
529 std::vector
<Cell
>::const_iterator it
= aLine
.maCells
.begin(), itEnd
= aLine
.maCells
.end();
530 SCCOL nCol
= maStartRange
.aStart
.Col();
531 const char* pLineHead
= aLine
.maLine
.getStr();
532 for (; it
!= itEnd
; ++it
, ++nCol
)
534 const Cell
& rCell
= *it
;
537 maDocAccess
.setNumericCell(
538 ScAddress(nCol
, mnCurRow
, maStartRange
.aStart
.Tab()), rCell
.mfValue
);
542 maDocAccess
.setStringCell(
543 ScAddress(nCol
, mnCurRow
, maStartRange
.aStart
.Tab()),
544 OUString(pLineHead
+rCell
.maStr
.Pos
, rCell
.maStr
.Size
, RTL_TEXTENCODING_UTF8
));
549 fTimes
[ DEBUG_TIME_IMPORT
] = getNow() - fStart
;
551 if (meMove
== NO_MOVE
)
554 if (meMove
== RANGE_DOWN
)
557 // mpDocShell->GetViewData()->GetView()->AlignToCursor(
558 // maStartRange.aStart.Col(), mnCurRow, SC_FOLLOW_JUMP);
561 if (getNow() - mfLastRefreshTime
> 0.1 && mnLinesSinceRefresh
> 200)
562 // Refresh no more frequently than every 0.1 second, and wait until at
563 // least we have processed 200 lines.
566 ++mnLinesSinceRefresh
;
571 void DataStream::Text2Doc() {}
575 bool DataStream::ImportData()
578 // We no longer support this mode. To be deleted later.
581 if (ScDocShell::GetViewData()->GetViewShell()->NeedsRepaint())
588 IMPL_LINK_NOARG(DataStream
, ImportTimerHdl
)
591 maImportTimer
.Start();
598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */