/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <dataprovider.hxx> #include <com/sun/star/ucb/XSimpleFileAccess3.hpp> #include <com/sun/star/ucb/SimpleFileAccess.hpp> #include <com/sun/star/io/XInputStream.hpp> #include "officecfg/Office/Calc.hxx" #include <stringutil.hxx> #include <rtl/strbuf.hxx> #if defined(_WIN32) #if !defined __ORCUS_STATIC_LIB // avoid -Werror,-Wunused-macros #define __ORCUS_STATIC_LIB #endif #endif #include <orcus/csv_parser.hpp> using namespace com::sun::star; namespace sc { namespace { std::unique_ptr<SvStream> FetchStreamFromURL(const OUString& rURL, OStringBuffer& rBuffer) { uno::Reference< ucb::XSimpleFileAccess3 > xFileAccess( ucb::SimpleFileAccess::create( comphelper::getProcessComponentContext() ), uno::UNO_QUERY ); uno::Reference< io::XInputStream > xStream; xStream = xFileAccess->openFileRead( rURL ); const sal_Int32 BUF_LEN = 8000; uno::Sequence< sal_Int8 > buffer( BUF_LEN ); sal_Int32 nRead = 0; while ( ( nRead = xStream->readBytes( buffer, BUF_LEN ) ) == BUF_LEN ) { rBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead ); } if ( nRead > 0 ) { rBuffer.append( reinterpret_cast< const char* >( buffer.getConstArray() ), nRead ); } xStream->closeInput(); SvStream* pStream = new SvMemoryStream(const_cast<char*>(rBuffer.getStr()), rBuffer.getLength(), StreamMode::READ); return std::unique_ptr<SvStream>(pStream); } } ExternalDataMapper::ExternalDataMapper(ScDocShell* pDocShell, const OUString& rURL, const OUString& rName, SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool& bSuccess): maRange (ScRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab)), mpDocShell(pDocShell), mpDataProvider (new CSVDataProvider(mpDocShell, rURL, maRange)), mpDBCollection (pDocShell->GetDocument().GetDBCollection()) { bSuccess = true; ScDBCollection::NamedDBs& rNamedDBS = mpDBCollection->getNamedDBs(); if(!rNamedDBS.insert (new ScDBData (rName, nTab, nCol1, nRow1, nCol2, nRow2))) bSuccess = false; } ExternalDataMapper::~ExternalDataMapper() { } void ExternalDataMapper::StartImport() { mpDataProvider->StartImport(); } DataProvider::~DataProvider() { } Cell::Cell() : mfValue(0.0), mbValue(true) {} Cell::Cell(const Cell& r) : mbValue(r.mbValue) { if (r.mbValue) mfValue = r.mfValue; else { maStr.Pos = r.maStr.Pos; maStr.Size = r.maStr.Size; } } class CSVHandler { Line& mrLine; size_t mnColCount; size_t mnCols; const char* mpLineHead; public: CSVHandler( Line& rLine, size_t nColCount ) : mrLine(rLine), mnColCount(nColCount), mnCols(0), mpLineHead(rLine.maLine.getStr()) {} static void begin_parse() {} static void end_parse() {} static void begin_row() {} static void end_row() {} void cell(const char* p, size_t n) { if (mnCols >= mnColCount) return; Cell aCell; if (ScStringUtil::parseSimpleNumber(p, n, '.', ',', aCell.mfValue)) { aCell.mbValue = true; } else { aCell.mbValue = false; aCell.maStr.Pos = std::distance(mpLineHead, p); aCell.maStr.Size = n; } mrLine.maCells.push_back(aCell); ++mnCols; } }; CSVFetchThread::CSVFetchThread(ScDocument& rDoc, const OUString& mrURL, size_t nColCount): Thread("ReaderThread"), mpStream(nullptr), mrDocument(rDoc), maURL (mrURL), mnColCount(nColCount), mbTerminate(false) { maConfig.delimiters.push_back(','); maConfig.text_qualifier = '"'; mrDocument.InsertTab(0, "blah"); } CSVFetchThread::~CSVFetchThread() { } bool CSVFetchThread::IsRequestedTerminate() { osl::MutexGuard aGuard(maMtxTerminate); return mbTerminate; } void CSVFetchThread::RequestTerminate() { osl::MutexGuard aGuard(maMtxTerminate); mbTerminate = true; } void CSVFetchThread::EndThread() { RequestTerminate(); } void CSVFetchThread::execute() { OStringBuffer aBuffer(64000); mpStream = FetchStreamFromURL(maURL, aBuffer); if (mpStream->good()) { LinesType aLines(10); SCROW nCurRow = 0; for (Line & rLine : aLines) { rLine.maCells.clear(); mpStream->ReadLine(rLine.maLine); CSVHandler aHdl(rLine, mnColCount); orcus::csv_parser<CSVHandler> parser(rLine.maLine.getStr(), rLine.maLine.getLength(), aHdl, maConfig); parser.parse(); if (rLine.maCells.empty()) { return; } SCCOL nCol = 0; const char* pLineHead = rLine.maLine.getStr(); for (auto& rCell : rLine.maCells) { if (rCell.mbValue) { mrDocument.SetValue(ScAddress(nCol, nCurRow, 0 /* Tab */), rCell.mfValue); } else { mrDocument.SetString(nCol, nCurRow, 0 /* Tab */, OUString(pLineHead+rCell.maStr.Pos, rCell.maStr.Size, RTL_TEXTENCODING_UTF8)); } ++nCol; } nCurRow++; } } } osl::Mutex& CSVFetchThread::GetLinesMutex() { return maMtxLines; } bool CSVFetchThread::HasNewLines() { return !maPendingLines.empty(); } void CSVFetchThread::WaitForNewLines() { maCondConsume.wait(); maCondConsume.reset(); } LinesType* CSVFetchThread::GetNewLines() { LinesType* pLines = maPendingLines.front(); maPendingLines.pop(); return pLines; } void CSVFetchThread::ResumeFetchStream() { maCondReadStream.set(); } CSVDataProvider::CSVDataProvider(ScDocShell* pDocShell, const OUString& rURL, const ScRange& rRange): maURL(rURL), mrRange(rRange), mpDocShell(pDocShell), mpDocument(&pDocShell->GetDocument()), mpLines(nullptr), mnLineCount(0), mbImportUnderway(false) { } CSVDataProvider::~CSVDataProvider() { } void CSVDataProvider::StartImport() { if (mbImportUnderway) return; if (!mxCSVFetchThread.is()) { ScDocument aDoc; mxCSVFetchThread = new CSVFetchThread(aDoc, maURL, mrRange.aEnd.Col() - mrRange.aStart.Col() + 1); mxCSVFetchThread->launch(); if (mxCSVFetchThread.is()) { mxCSVFetchThread->EndThread(); mxCSVFetchThread->join(); } WriteToDoc(aDoc); } Refresh(); } void CSVDataProvider::Refresh() { mpDocShell->DoHardRecalc(); mpDocShell->SetDocumentModified(); } Line CSVDataProvider::GetLine() { if (!mpLines || mnLineCount >= mpLines->size()) { if (mxCSVFetchThread->IsRequestedTerminate()) return Line(); osl::ResettableMutexGuard aGuard(mxCSVFetchThread->GetLinesMutex()); while (!mxCSVFetchThread->HasNewLines() && !mxCSVFetchThread->IsRequestedTerminate()) { aGuard.clear(); mxCSVFetchThread->WaitForNewLines(); aGuard.reset(); } mpLines = mxCSVFetchThread->GetNewLines(); mxCSVFetchThread->ResumeFetchStream(); } return mpLines->at(mnLineCount++); } void CSVDataProvider::WriteToDoc(ScDocument& rDoc) { double* pfValue; for (int nRow = mrRange.aStart.Row(); nRow < mrRange.aEnd.Row(); ++nRow) { for (int nCol = mrRange.aStart.Col(); nCol < mrRange.aEnd.Col(); ++nCol) { ScAddress aAddr = ScAddress(nCol, nRow, mrRange.aStart.Tab()); pfValue = rDoc.GetValueCell(aAddr); if (pfValue == nullptr) { OUString aString = rDoc.GetString(nCol, nRow, mrRange.aStart.Tab()); mpDocument->SetString(nCol, nRow, mrRange.aStart.Tab(), aString); } else { mpDocument->SetValue(nCol, nRow, mrRange.aStart.Tab(), *pfValue); } } } } } /* vim:set shiftwidth=4 softtabstop=4 expandtab: */