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