Line data Source code
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 <datastream.hxx>
11 : #include <datastreamgettime.hxx>
12 :
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>
17 : #include <osl/time.h>
18 : #include <rtl/strbuf.hxx>
19 : #include <salhelper/thread.hxx>
20 : #include <sfx2/viewfrm.hxx>
21 : #include <datastreamdlg.hxx>
22 : #include <docsh.hxx>
23 : #include <rangelst.hxx>
24 : #include <tabvwsh.hxx>
25 : #include <viewdata.hxx>
26 : #include <stringutil.hxx>
27 : #include <documentlinkmgr.hxx>
28 :
29 : #include <config_orcus.h>
30 : #include "officecfg/Office/Calc.hxx"
31 :
32 :
33 : #if ENABLE_ORCUS
34 : #if defined WNT
35 : #define __ORCUS_STATIC_LIB
36 : #endif
37 : #include <orcus/csv_parser.hpp>
38 : #endif
39 :
40 : #include <queue>
41 :
42 : namespace sc {
43 :
44 : enum {
45 : DEBUG_TIME_IMPORT,
46 : DEBUG_TIME_RECALC,
47 : DEBUG_TIME_RENDER,
48 : DEBUG_TIME_MAX
49 : };
50 :
51 : static double fTimes[DEBUG_TIME_MAX] = { 0.0, 0.0, 0.0 };
52 :
53 0 : double datastream_get_time(int nIdx)
54 : {
55 0 : if( nIdx < 0 || nIdx >= (int)SAL_N_ELEMENTS( fTimes ) )
56 0 : return -1;
57 0 : return fTimes[ nIdx ];
58 : }
59 :
60 : namespace {
61 :
62 0 : inline double getNow()
63 : {
64 : TimeValue now;
65 0 : osl_getSystemTime(&now);
66 0 : return static_cast<double>(now.Seconds) + static_cast<double>(now.Nanosec) / 1000000000.0;
67 : }
68 :
69 : #if ENABLE_ORCUS
70 :
71 : class CSVHandler
72 : {
73 : DataStream::Line& mrLine;
74 : size_t mnColCount;
75 : size_t mnCols;
76 : const char* mpLineHead;
77 :
78 : public:
79 0 : CSVHandler( DataStream::Line& rLine, size_t nColCount ) :
80 0 : mrLine(rLine), mnColCount(nColCount), mnCols(0), mpLineHead(rLine.maLine.getStr()) {}
81 :
82 0 : static void begin_parse() {}
83 0 : static void end_parse() {}
84 0 : static void begin_row() {}
85 0 : static void end_row() {}
86 :
87 0 : void cell(const char* p, size_t n)
88 : {
89 0 : if (mnCols >= mnColCount)
90 0 : return;
91 :
92 0 : DataStream::Cell aCell;
93 0 : if (ScStringUtil::parseSimpleNumber(p, n, '.', ',', aCell.mfValue))
94 : {
95 0 : aCell.mbValue = true;
96 : }
97 : else
98 : {
99 0 : aCell.mbValue = false;
100 0 : aCell.maStr.Pos = std::distance(mpLineHead, p);
101 0 : aCell.maStr.Size = n;
102 : }
103 0 : mrLine.maCells.push_back(aCell);
104 :
105 0 : ++mnCols;
106 : }
107 : };
108 :
109 : #endif
110 :
111 : }
112 :
113 : namespace datastreams {
114 :
115 0 : void emptyLineQueue( std::queue<DataStream::LinesType*>& rQueue )
116 : {
117 0 : while (!rQueue.empty())
118 : {
119 0 : delete rQueue.front();
120 0 : rQueue.pop();
121 : }
122 0 : }
123 :
124 : class ReaderThread : public salhelper::Thread
125 : {
126 : SvStream *mpStream;
127 : size_t mnColCount;
128 : bool mbTerminate;
129 : osl::Mutex maMtxTerminate;
130 :
131 : std::queue<DataStream::LinesType*> maPendingLines;
132 : std::queue<DataStream::LinesType*> maUsedLines;
133 : osl::Mutex maMtxLines;
134 :
135 : osl::Condition maCondReadStream;
136 : osl::Condition maCondConsume;
137 :
138 : #if ENABLE_ORCUS
139 : orcus::csv::parser_config maConfig;
140 : #endif
141 :
142 : public:
143 :
144 0 : ReaderThread(SvStream *pData, size_t nColCount):
145 : Thread("ReaderThread"),
146 : mpStream(pData),
147 : mnColCount(nColCount),
148 0 : mbTerminate(false)
149 : {
150 : #if ENABLE_ORCUS
151 0 : maConfig.delimiters.push_back(',');
152 0 : maConfig.text_qualifier = '"';
153 : #endif
154 0 : }
155 :
156 0 : virtual ~ReaderThread()
157 0 : {
158 0 : delete mpStream;
159 0 : emptyLineQueue(maPendingLines);
160 0 : emptyLineQueue(maUsedLines);
161 0 : }
162 :
163 0 : bool isTerminateRequested()
164 : {
165 0 : osl::MutexGuard aGuard(maMtxTerminate);
166 0 : return mbTerminate;
167 : }
168 :
169 0 : void requestTerminate()
170 : {
171 0 : osl::MutexGuard aGuard(maMtxTerminate);
172 0 : mbTerminate = true;
173 0 : }
174 :
175 0 : void endThread()
176 : {
177 0 : requestTerminate();
178 0 : maCondReadStream.set();
179 0 : }
180 :
181 0 : void waitForNewLines()
182 : {
183 0 : maCondConsume.wait();
184 0 : maCondConsume.reset();
185 0 : }
186 :
187 0 : DataStream::LinesType* popNewLines()
188 : {
189 0 : DataStream::LinesType* pLines = maPendingLines.front();
190 0 : maPendingLines.pop();
191 0 : return pLines;
192 : }
193 :
194 0 : void resumeReadStream()
195 : {
196 0 : if (maPendingLines.size() <= 4)
197 0 : maCondReadStream.set(); // start producer again
198 0 : }
199 :
200 0 : bool hasNewLines()
201 : {
202 0 : return !maPendingLines.empty();
203 : }
204 :
205 0 : void pushUsedLines( DataStream::LinesType* pLines )
206 : {
207 0 : maUsedLines.push(pLines);
208 0 : }
209 :
210 0 : osl::Mutex& getLinesMutex()
211 : {
212 0 : return maMtxLines;
213 : }
214 :
215 : private:
216 0 : virtual void execute() SAL_OVERRIDE
217 : {
218 0 : while (!isTerminateRequested())
219 : {
220 0 : DataStream::LinesType* pLines = NULL;
221 0 : osl::ResettableMutexGuard aGuard(maMtxLines);
222 :
223 0 : if (!maUsedLines.empty())
224 : {
225 : // Re-use lines from previous runs.
226 0 : pLines = maUsedLines.front();
227 0 : maUsedLines.pop();
228 0 : aGuard.clear(); // unlock
229 : }
230 : else
231 : {
232 0 : aGuard.clear(); // unlock
233 0 : pLines = new DataStream::LinesType(10);
234 : }
235 :
236 : // Read & store new lines from stream.
237 0 : for (size_t i = 0, n = pLines->size(); i < n; ++i)
238 : {
239 0 : DataStream::Line& rLine = (*pLines)[i];
240 0 : rLine.maCells.clear();
241 0 : mpStream->ReadLine(rLine.maLine);
242 : #if ENABLE_ORCUS
243 0 : CSVHandler aHdl(rLine, mnColCount);
244 0 : orcus::csv_parser<CSVHandler> parser(rLine.maLine.getStr(), rLine.maLine.getLength(), aHdl, maConfig);
245 0 : parser.parse();
246 : #endif
247 0 : }
248 :
249 0 : aGuard.reset(); // lock
250 0 : while (!isTerminateRequested() && maPendingLines.size() >= 8)
251 : {
252 : // pause reading for a bit
253 0 : aGuard.clear(); // unlock
254 0 : maCondReadStream.wait();
255 0 : maCondReadStream.reset();
256 0 : aGuard.reset(); // lock
257 : }
258 0 : maPendingLines.push(pLines);
259 0 : maCondConsume.set();
260 0 : if (!mpStream->good())
261 0 : requestTerminate();
262 0 : }
263 0 : }
264 : };
265 :
266 : }
267 :
268 0 : DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
269 :
270 0 : DataStream::Cell::Cell( const Cell& r ) : mbValue(r.mbValue)
271 : {
272 0 : if (r.mbValue)
273 0 : mfValue = r.mfValue;
274 : else
275 : {
276 0 : maStr.Pos = r.maStr.Pos;
277 0 : maStr.Size = r.maStr.Size;
278 : }
279 0 : }
280 :
281 0 : void DataStream::MakeToolbarVisible()
282 : {
283 : css::uno::Reference< css::frame::XFrame > xFrame =
284 0 : ScDocShell::GetViewData()->GetViewShell()->GetViewFrame()->GetFrame().GetFrameInterface();
285 0 : if (!xFrame.is())
286 0 : return;
287 :
288 0 : css::uno::Reference< css::beans::XPropertySet > xPropSet(xFrame, css::uno::UNO_QUERY);
289 0 : if (!xPropSet.is())
290 0 : return;
291 :
292 0 : css::uno::Reference< css::frame::XLayoutManager > xLayoutManager;
293 0 : xPropSet->getPropertyValue("LayoutManager") >>= xLayoutManager;
294 0 : if (!xLayoutManager.is())
295 0 : return;
296 :
297 0 : const OUString sResourceURL( "private:resource/toolbar/datastreams" );
298 0 : css::uno::Reference< css::ui::XUIElement > xUIElement = xLayoutManager->getElement(sResourceURL);
299 0 : if (!xUIElement.is())
300 : {
301 0 : xLayoutManager->createElement( sResourceURL );
302 0 : xLayoutManager->showElement( sResourceURL );
303 0 : }
304 : }
305 :
306 0 : DataStream* DataStream::Set(
307 : ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
308 : sal_Int32 nLimit, MoveType eMove, sal_uInt32 nSettings)
309 : {
310 0 : DataStream* pLink = new DataStream(pShell, rURL, rRange, nLimit, eMove, nSettings);
311 0 : sc::DocumentLinkManager& rMgr = pShell->GetDocument().GetDocLinkManager();
312 0 : rMgr.setDataStream(pLink);
313 0 : return pLink;
314 : }
315 :
316 0 : DataStream::DataStream(ScDocShell *pShell, const OUString& rURL, const ScRange& rRange,
317 : sal_Int32 nLimit, MoveType eMove, sal_uInt32 nSettings) :
318 : mpDocShell(pShell),
319 0 : maDocAccess(mpDocShell->GetDocument()),
320 : meOrigMove(NO_MOVE),
321 : meMove(NO_MOVE),
322 : mbRunning(false),
323 : mbValuesInLine(false),
324 : mbRefreshOnEmptyLine(false),
325 : mpLines(0),
326 : mnLinesCount(0),
327 : mnLinesSinceRefresh(0),
328 : mfLastRefreshTime(0.0),
329 : mnCurRow(0),
330 : mbIsFirst(true),
331 0 : mbIsUpdate(false)
332 : {
333 0 : maImportTimer.SetTimeout(0);
334 0 : maImportTimer.SetTimeoutHdl( LINK(this, DataStream, ImportTimerHdl) );
335 :
336 0 : Decode(rURL, rRange, nLimit, eMove, nSettings);
337 0 : }
338 :
339 0 : DataStream::~DataStream()
340 : {
341 0 : if (mbRunning)
342 0 : StopImport();
343 :
344 0 : if (mxReaderThread.is())
345 : {
346 0 : mxReaderThread->endThread();
347 0 : mxReaderThread->join();
348 : }
349 0 : delete mpLines;
350 0 : }
351 :
352 0 : DataStream::Line DataStream::ConsumeLine()
353 : {
354 0 : if (!mpLines || mnLinesCount >= mpLines->size())
355 : {
356 0 : mnLinesCount = 0;
357 0 : if (mxReaderThread->isTerminateRequested())
358 0 : return Line();
359 :
360 0 : osl::ResettableMutexGuard aGuard(mxReaderThread->getLinesMutex());
361 0 : if (mpLines)
362 0 : mxReaderThread->pushUsedLines(mpLines);
363 :
364 0 : while (!mxReaderThread->hasNewLines())
365 : {
366 0 : aGuard.clear(); // unlock
367 0 : mxReaderThread->waitForNewLines();
368 0 : aGuard.reset(); // lock
369 : }
370 :
371 0 : mpLines = mxReaderThread->popNewLines();
372 0 : mxReaderThread->resumeReadStream();
373 : }
374 0 : return mpLines->at(mnLinesCount++);
375 : }
376 :
377 0 : ScRange DataStream::GetRange() const
378 : {
379 0 : ScRange aRange = maStartRange;
380 0 : aRange.aEnd = maEndRange.aEnd;
381 0 : return aRange;
382 : }
383 :
384 0 : void DataStream::Decode(const OUString& rURL, const ScRange& rRange,
385 : sal_Int32 nLimit, MoveType eMove, const sal_uInt32 nSettings)
386 : {
387 0 : msURL = rURL;
388 0 : mnLimit = nLimit;
389 0 : meMove = eMove;
390 0 : meOrigMove = eMove;
391 0 : mnSettings = nSettings;
392 :
393 0 : mbValuesInLine = true; // always true.
394 :
395 0 : mnCurRow = rRange.aStart.Row();
396 :
397 0 : ScRange aRange = rRange;
398 0 : if (aRange.aStart.Row() != aRange.aEnd.Row())
399 : // We only allow this range to be one row tall.
400 0 : aRange.aEnd.SetRow(aRange.aStart.Row());
401 :
402 0 : maStartRange = aRange;
403 0 : maEndRange = aRange;
404 0 : if (nLimit == 0)
405 : {
406 : // Unlimited
407 0 : maEndRange.aStart.SetRow(MAXROW);
408 : }
409 0 : else if (nLimit > 0)
410 : {
411 : // Limited.
412 0 : maEndRange.aStart.IncRow(nLimit-1);
413 0 : if (maEndRange.aStart.Row() > MAXROW)
414 0 : maEndRange.aStart.SetRow(MAXROW);
415 : }
416 :
417 0 : maEndRange.aEnd.SetRow(maEndRange.aStart.Row());
418 0 : }
419 :
420 0 : void DataStream::StartImport()
421 : {
422 0 : if (mbRunning)
423 0 : return;
424 :
425 0 : if (!mxReaderThread.is())
426 : {
427 0 : SvStream *pStream = 0;
428 0 : if (mnSettings & SCRIPT_STREAM)
429 0 : pStream = new SvScriptStream(msURL);
430 : else
431 0 : pStream = new SvFileStream(msURL, StreamMode::READ);
432 0 : mxReaderThread = new datastreams::ReaderThread(pStream, maStartRange.aEnd.Col() - maStartRange.aStart.Col() + 1);
433 0 : mxReaderThread->launch();
434 : }
435 0 : mbRunning = true;
436 0 : maDocAccess.reset();
437 :
438 0 : maImportTimer.Start();
439 : }
440 :
441 0 : void DataStream::StopImport()
442 : {
443 0 : if (!mbRunning)
444 0 : return;
445 :
446 0 : mbRunning = false;
447 0 : Refresh();
448 0 : maImportTimer.Stop();
449 : }
450 :
451 0 : void DataStream::SetRefreshOnEmptyLine( bool bVal )
452 : {
453 0 : mbRefreshOnEmptyLine = bVal;
454 0 : }
455 :
456 0 : void DataStream::Refresh()
457 : {
458 0 : Application::Yield();
459 :
460 0 : double fStart = getNow();
461 :
462 : // Hard recalc will repaint the grid area.
463 0 : mpDocShell->DoHardRecalc(true);
464 0 : mpDocShell->SetDocumentModified(true);
465 :
466 0 : fTimes[ DEBUG_TIME_RECALC ] = getNow() - fStart;
467 :
468 0 : mfLastRefreshTime = getNow();
469 0 : mnLinesSinceRefresh = 0;
470 0 : }
471 :
472 0 : void DataStream::MoveData()
473 : {
474 0 : switch (meMove)
475 : {
476 : case RANGE_DOWN:
477 : {
478 0 : if (mnCurRow == maEndRange.aStart.Row())
479 0 : meMove = MOVE_UP;
480 : }
481 0 : break;
482 : case MOVE_UP:
483 : {
484 0 : mbIsUpdate = true;
485 : // Remove the top row and shift the remaining rows upward. Then
486 : // insert a new row at the end row position.
487 0 : ScRange aRange = maStartRange;
488 0 : aRange.aEnd = maEndRange.aEnd;
489 0 : maDocAccess.shiftRangeUp(aRange);
490 : }
491 0 : break;
492 : case MOVE_DOWN:
493 : {
494 0 : mbIsUpdate = true;
495 : // Remove the end row and shift the remaining rows downward by
496 : // inserting a new row at the top row.
497 0 : ScRange aRange = maStartRange;
498 0 : aRange.aEnd = maEndRange.aEnd;
499 0 : maDocAccess.shiftRangeDown(aRange);
500 : }
501 0 : break;
502 : case NO_MOVE:
503 : default:
504 : ;
505 : }
506 0 : if(mbIsFirst && mbIsUpdate)
507 : {
508 0 : sal_Int32 nStreamTimeout = officecfg::Office::Calc::DataStream::UpdateTimeout::get();
509 0 : maImportTimer.SetTimeout(nStreamTimeout);
510 0 : mbIsFirst = false;
511 : }
512 0 : }
513 :
514 : #if ENABLE_ORCUS
515 :
516 0 : void DataStream::Text2Doc()
517 : {
518 0 : Line aLine = ConsumeLine();
519 0 : if (aLine.maCells.empty() && mbRefreshOnEmptyLine)
520 : {
521 : // Empty line detected. Trigger refresh and discard it.
522 0 : Refresh();
523 0 : return;
524 : }
525 :
526 0 : double fStart = getNow();
527 :
528 0 : MoveData();
529 : {
530 0 : std::vector<Cell>::const_iterator it = aLine.maCells.begin(), itEnd = aLine.maCells.end();
531 0 : SCCOL nCol = maStartRange.aStart.Col();
532 0 : const char* pLineHead = aLine.maLine.getStr();
533 0 : for (; it != itEnd; ++it, ++nCol)
534 : {
535 0 : const Cell& rCell = *it;
536 0 : if (rCell.mbValue)
537 : {
538 : maDocAccess.setNumericCell(
539 0 : ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()), rCell.mfValue);
540 : }
541 : else
542 : {
543 : maDocAccess.setStringCell(
544 0 : ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()),
545 0 : OUString(pLineHead+rCell.maStr.Pos, rCell.maStr.Size, RTL_TEXTENCODING_UTF8));
546 : }
547 : }
548 : }
549 :
550 0 : fTimes[ DEBUG_TIME_IMPORT ] = getNow() - fStart;
551 :
552 0 : if (meMove == NO_MOVE)
553 0 : return;
554 :
555 0 : if (meMove == RANGE_DOWN)
556 : {
557 0 : ++mnCurRow;
558 : // mpDocShell->GetViewData().GetView()->AlignToCursor(
559 : // maStartRange.aStart.Col(), mnCurRow, SC_FOLLOW_JUMP);
560 : }
561 :
562 0 : 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.
565 0 : Refresh();
566 :
567 0 : ++mnLinesSinceRefresh;
568 : }
569 :
570 : #else
571 :
572 : void DataStream::Text2Doc() {}
573 :
574 : #endif
575 :
576 0 : bool DataStream::ImportData()
577 : {
578 0 : if (!mbValuesInLine)
579 : // We no longer support this mode. To be deleted later.
580 0 : return false;
581 :
582 0 : if (ScDocShell::GetViewData()->GetViewShell()->NeedsRepaint())
583 0 : return mbRunning;
584 :
585 0 : Text2Doc();
586 0 : return mbRunning;
587 : }
588 :
589 0 : IMPL_LINK_NOARG_TYPED(DataStream, ImportTimerHdl, Timer *, void)
590 : {
591 0 : if (ImportData())
592 0 : maImportTimer.Start();
593 0 : }
594 :
595 156 : } // namespace sc
596 :
597 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|