[Libreoffice-commits] core.git: Branch 'libreoffice-4-2' - 2 commits - formula/source include/formula sc/inc sc/source

Kohei Yoshida kohei.yoshida at collabora.com
Tue Dec 31 07:41:13 PST 2013


 formula/source/core/api/FormulaCompiler.cxx |    4 
 include/formula/compiler.hrc                |    5 
 include/formula/opcode.hxx                  |    1 
 sc/inc/stringutil.hxx                       |    3 
 sc/source/core/inc/interpre.hxx             |    1 
 sc/source/core/tool/compiler.cxx            |   10 
 sc/source/core/tool/interpr4.cxx            |    1 
 sc/source/core/tool/interpr7.cxx            |   49 +++
 sc/source/core/tool/stringutil.cxx          |  150 +++++++++++
 sc/source/ui/docshell/datastream.cxx        |  370 +++++++++++++++-------------
 sc/source/ui/inc/datastream.hxx             |   44 ++-
 sc/source/ui/miscdlgs/datastreamdlg.cxx     |    5 
 12 files changed, 466 insertions(+), 177 deletions(-)

New commits:
commit fccf9404762b5c73af1dfa6523d6b8c2b3e91dd8
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Fri Dec 27 21:57:25 2013 -0500

    More backporting of experimental data stream feature.
    
    This change eliminates its "caller" thread in favor of using a timer.
    
    Change-Id: I30b85be938c4aec152c52b67cf69ff3afa53399d

diff --git a/sc/inc/stringutil.hxx b/sc/inc/stringutil.hxx
index 08e5c1e..d275007 100644
--- a/sc/inc/stringutil.hxx
+++ b/sc/inc/stringutil.hxx
@@ -126,6 +126,9 @@ public:
     static bool parseSimpleNumber(
         const OUString& rStr, sal_Unicode dsep, sal_Unicode gsep, double& rVal);
 
+    static bool parseSimpleNumber(
+        const char* p, size_t n, char dsep, char gsep, double& rVal);
+
     static sal_Int32 SC_DLLPUBLIC GetQuotedTokenCount(const OUString &rIn, const OUString& rQuotedPairs, sal_Unicode cTok = ';' );
     static OUString  SC_DLLPUBLIC GetQuotedToken(const OUString &rIn, sal_Int32 nToken, const OUString& rQuotedPairs,
                                         sal_Unicode cTok,  sal_Int32& rIndex );
diff --git a/sc/source/core/tool/stringutil.cxx b/sc/source/core/tool/stringutil.cxx
index e711bcb..5bdc2c2 100644
--- a/sc/source/core/tool/stringutil.cxx
+++ b/sc/source/core/tool/stringutil.cxx
@@ -18,11 +18,13 @@
  */
 
 #include "stringutil.hxx"
-#include "rtl/ustrbuf.hxx"
-#include "rtl/math.hxx"
 #include "global.hxx"
 #include "svl/zforlist.hxx"
 
+#include <rtl/ustrbuf.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/math.hxx>
+
 ScSetStringParam::ScSetStringParam() :
     mpNumFormatter(NULL),
     mbDetectNumberFormat(true),
@@ -194,6 +196,150 @@ bool ScStringUtil::parseSimpleNumber(
     return true;
 }
 
+bool ScStringUtil::parseSimpleNumber(
+    const char* p, size_t n, char dsep, char gsep, double& rVal)
+{
+    // Actually almost the entire pre-check is unnecessary and we could call
+    // rtl::math::stringToDouble() just after having exchanged ascii space with
+    // non-breaking space, if it wasn't for check of grouped digits. The NaN
+    // and Inf cases that are accepted by stringToDouble() could be detected
+    // using rtl::math::isFinite() on the result.
+
+    /* TODO: The grouped digits check isn't even valid for locales that do not
+     * group in thousands ... e.g. Indian locales. But that's something also
+     * the number scanner doesn't implement yet, only the formatter. */
+
+    OStringBuffer aBuf;
+
+    size_t i = 0;
+    const char* pLast = p + (n-1);
+    sal_Int32 nPosDSep = -1, nPosGSep = -1;
+    sal_uInt32 nDigitCount = 0;
+    sal_Int32 nPosExponent = -1;
+
+    // Skip preceding spaces.
+    for (i = 0; i < n; ++i, ++p)
+    {
+        char c = *p;
+        if (c != ' ')
+            // first non-space character.  Exit.
+            break;
+    }
+
+    if (i == n)
+        // the whole string is space.  Fail.
+        return false;
+
+    n -= i; // Subtract the length of the preceding spaces.
+
+    // Determine the last non-space character.
+    for (; p != pLast; --pLast, --n)
+    {
+        char c = *pLast;
+        if (c != ' ')
+            // Non space character. Exit.
+            break;
+    }
+
+    for (i = 0; i < n; ++i, ++p)
+    {
+        char c = *p;
+
+        if ('0' <= c && c <= '9')
+        {
+            // this is a digit.
+            aBuf.append(c);
+            ++nDigitCount;
+        }
+        else if (c == dsep)
+        {
+            // this is a decimal separator.
+
+            if (nPosDSep >= 0)
+                // a second decimal separator -> not a valid number.
+                return false;
+
+            if (nPosGSep >= 0 && i - nPosGSep != 4)
+                // the number has a group separator and the decimal sep is not
+                // positioned correctly.
+                return false;
+
+            nPosDSep = i;
+            nPosGSep = -1;
+            aBuf.append(c);
+            nDigitCount = 0;
+        }
+        else if (c == gsep)
+        {
+            // this is a group (thousand) separator.
+
+            if (i == 0)
+                // not allowed as the first character.
+                return false;
+
+            if (nPosDSep >= 0)
+                // not allowed after the decimal separator.
+                return false;
+
+            if (nPosGSep >= 0 && nDigitCount != 3)
+                // must be exactly 3 digits since the last group separator.
+                return false;
+
+            if (nPosExponent >= 0)
+                // not allowed in exponent.
+                return false;
+
+            nPosGSep = i;
+            nDigitCount = 0;
+        }
+        else if (c == '-' || c == '+')
+        {
+            // A sign must be the first character if it's given, or immediately
+            // follow the exponent character if present.
+            if (i == 0 || (nPosExponent >= 0 && i == static_cast<size_t>(nPosExponent+1)))
+                aBuf.append(c);
+            else
+                return false;
+        }
+        else if (c == 'E' || c == 'e')
+        {
+            // this is an exponent designator.
+
+            if (nPosExponent >= 0)
+                // Only one exponent allowed.
+                return false;
+
+            if (nPosGSep >= 0 && nDigitCount != 3)
+                // must be exactly 3 digits since the last group separator.
+                return false;
+
+            aBuf.append(c);
+            nPosExponent = i;
+            nPosDSep = -1;
+            nPosGSep = -1;
+            nDigitCount = 0;
+        }
+        else
+            return false;
+    }
+
+    // finished parsing the number.
+
+    if (nPosGSep >= 0 && nDigitCount != 3)
+        // must be exactly 3 digits since the last group separator.
+        return false;
+
+    rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
+    sal_Int32 nParseEnd = 0;
+    OString aString( aBuf.makeStringAndClear());
+    rVal = ::rtl::math::stringToDouble( aString, dsep, gsep, &eStatus, &nParseEnd);
+    if (eStatus != rtl_math_ConversionStatus_Ok || nParseEnd < aString.getLength())
+        // Not a valid number or not entire string consumed.
+        return false;
+
+    return true;
+}
+
 sal_Int32 ScStringUtil::GetQuotedTokenCount(const OUString &rIn, const OUString& rQuotedPairs, sal_Unicode cTok )
 {
     assert( !(rQuotedPairs.getLength()%2) );
diff --git a/sc/source/ui/docshell/datastream.cxx b/sc/source/ui/docshell/datastream.cxx
index c7f52e5..4c0deac 100644
--- a/sc/source/ui/docshell/datastream.cxx
+++ b/sc/source/ui/docshell/datastream.cxx
@@ -45,88 +45,161 @@ inline double getNow()
     return static_cast<double>(now.Seconds) + static_cast<double>(now.Nanosec) / 1000000000.0;
 }
 
-namespace datastreams {
+#if ENABLE_ORCUS
 
-class CallerThread : public salhelper::Thread
+class CSVHandler
 {
-    DataStream *mpDataStream;
+    DataStream::Line& mrLine;
+    size_t mnColCount;
+    size_t mnCols;
+    const char* mpLineHead;
+
 public:
-    osl::Condition maStart;
-    bool mbTerminate;
+    CSVHandler( DataStream::Line& rLine, size_t nColCount ) :
+        mrLine(rLine), mnColCount(nColCount), mnCols(0), mpLineHead(rLine.maLine.getStr()) {}
 
-    CallerThread(DataStream *pData):
-        Thread("CallerThread")
-        ,mpDataStream(pData)
-        ,mbTerminate(false)
-    {}
+    void begin_parse() {}
+    void end_parse() {}
+    void begin_row() {}
+    void end_row() {}
 
-private:
-    virtual void execute()
+    void cell(const char* p, size_t n)
     {
-        while (!mbTerminate)
+        if (mnCols >= mnColCount)
+            return;
+
+        DataStream::Cell aCell;
+        if (ScStringUtil::parseSimpleNumber(p, n, '.', ',', aCell.mfValue))
+        {
+            aCell.mbValue = true;
+        }
+        else
         {
-            // wait for a small amount of time, so that
-            // painting methods have a chance to be called.
-            // And also to make UI more responsive.
-            TimeValue const aTime = {0, 1000};
-            maStart.wait();
-            maStart.reset();
-            if (!mbTerminate)
-                while (mpDataStream->ImportData())
-                    wait(aTime);
-        };
+            aCell.mbValue = false;
+            aCell.maStr.Pos = std::distance(mpLineHead, p);
+            aCell.maStr.Size = n;
+        }
+        mrLine.maCells.push_back(aCell);
+
+        ++mnCols;
     }
 };
 
+#endif
+
+namespace datastreams {
+
+void emptyLineQueue( std::queue<DataStream::LinesType*>& rQueue )
+{
+    while (!rQueue.empty())
+    {
+        delete rQueue.front();
+        rQueue.pop();
+    }
+}
+
 class ReaderThread : public salhelper::Thread
 {
     SvStream *mpStream;
+    size_t mnColCount;
+    bool mbTerminate;
+    osl::Mutex maMtxTerminate;
+
+    std::queue<DataStream::LinesType*> maPendingLines;
+    std::queue<DataStream::LinesType*> maUsedLines;
+    osl::Mutex maMtxLines;
+
+    osl::Condition maCondReadStream;
+    osl::Condition maCondConsume;
+
+#if ENABLE_ORCUS
+    orcus::csv_parser_config maConfig;
+#endif
+
 public:
-    bool mbTerminateReading;
-    osl::Condition maProduceResume;
-    osl::Condition maConsumeResume;
-    osl::Mutex maLinesProtector;
-    std::queue<LinesList* > maPendingLines;
-    std::queue<LinesList* > maUsedLines;
-
-    ReaderThread(SvStream *pData):
-        Thread("ReaderThread")
-        ,mpStream(pData)
-        ,mbTerminateReading(false)
+
+    ReaderThread(SvStream *pData, size_t nColCount):
+        Thread("ReaderThread"),
+        mpStream(pData),
+        mnColCount(nColCount),
+        mbTerminate(false)
     {
+#if ENABLE_ORCUS
+        maConfig.delimiters.push_back(',');
+        maConfig.text_qualifier = '"';
+#endif
     }
 
     virtual ~ReaderThread()
     {
         delete mpStream;
-        while (!maPendingLines.empty())
-        {
-            delete maPendingLines.front();
-            maPendingLines.pop();
-        }
-        while (!maUsedLines.empty())
-        {
-            delete maUsedLines.front();
-            maUsedLines.pop();
-        }
+        emptyLineQueue(maPendingLines);
+        emptyLineQueue(maUsedLines);
+    }
+
+    bool isTerminateRequested()
+    {
+        osl::MutexGuard aGuard(maMtxTerminate);
+        return mbTerminate;
+    }
+
+    void requestTerminate()
+    {
+        osl::MutexGuard aGuard(maMtxTerminate);
+        mbTerminate = true;
     }
 
     void endThread()
     {
-        mbTerminateReading = true;
-        maProduceResume.set();
-        join();
+        requestTerminate();
+        maCondReadStream.set();
+    }
+
+    void waitForNewLines()
+    {
+        maCondConsume.wait();
+        maCondConsume.reset();
+    }
+
+    DataStream::LinesType* popNewLines()
+    {
+        DataStream::LinesType* pLines = maPendingLines.front();
+        maPendingLines.pop();
+        return pLines;
+    }
+
+    void resumeReadStream()
+    {
+        if (maPendingLines.size() <= 4)
+            maCondReadStream.set(); // start producer again
+    }
+
+    bool hasNewLines()
+    {
+        return !maPendingLines.empty();
+    }
+
+    void pushUsedLines( DataStream::LinesType* pLines )
+    {
+        maUsedLines.push(pLines);
+    }
+
+    osl::Mutex& getLinesMutex()
+    {
+        return maMtxLines;
     }
 
 private:
     virtual void execute() SAL_OVERRIDE
     {
-        while (!mbTerminateReading)
+        while (!isTerminateRequested())
         {
-            LinesList *pLines = 0;
-            osl::ResettableMutexGuard aGuard(maLinesProtector);
+            DataStream::LinesType* pLines = NULL;
+            osl::ResettableMutexGuard aGuard(maMtxLines);
+
             if (!maUsedLines.empty())
             {
+                // Re-use lines from previous runs.
                 pLines = maUsedLines.front();
                 maUsedLines.pop();
                 aGuard.clear(); // unlock
@@ -134,28 +207,54 @@ private:
             else
             {
                 aGuard.clear(); // unlock
-                pLines = new LinesList(10);
+                pLines = new DataStream::LinesType(10);
+            }
+
+            // Read & store new lines from stream.
+            for (size_t i = 0, n = pLines->size(); i < n; ++i)
+            {
+                DataStream::Line& rLine = (*pLines)[i];
+                rLine.maCells.clear();
+                mpStream->ReadLine(rLine.maLine);
+#if ENABLE_ORCUS
+                CSVHandler aHdl(rLine, mnColCount);
+                orcus::csv_parser<CSVHandler> parser(rLine.maLine.getStr(), rLine.maLine.getLength(), aHdl, maConfig);
+                parser.parse();
+#endif
             }
-            for (size_t i = 0; i < pLines->size(); ++i)
-                mpStream->ReadLine( pLines->at(i) );
+
             aGuard.reset(); // lock
-            while (!mbTerminateReading && maPendingLines.size() >= 8)
-            { // pause reading for a bit
+            while (!isTerminateRequested() && maPendingLines.size() >= 8)
+            {
+                // pause reading for a bit
                 aGuard.clear(); // unlock
-                maProduceResume.wait();
-                maProduceResume.reset();
+                maCondReadStream.wait();
+                maCondReadStream.reset();
                 aGuard.reset(); // lock
             }
             maPendingLines.push(pLines);
-            maConsumeResume.set();
+            maCondConsume.set();
             if (!mpStream->good())
-                mbTerminateReading = true;
+                requestTerminate();
         }
     }
 };
 
 }
 
+DataStream::Cell::Cell() : mfValue(0.0), mbValue(true) {}
+
+DataStream::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;
+    }
+}
+
 void DataStream::MakeToolbarVisible()
 {
     css::uno::Reference< css::frame::XFrame > xFrame =
@@ -219,8 +318,8 @@ DataStream::DataStream(ScDocShell *pShell, const OUString& rURL, const ScRange&
     mfLastRefreshTime(0.0),
     mnCurRow(0)
 {
-    mxThread = new datastreams::CallerThread( this );
-    mxThread->launch();
+    maImportTimer.SetTimeout(0);
+    maImportTimer.SetTimeoutHdl( LINK(this, DataStream, ImportTimerHdl) );
 
     Decode(rURL, rRange, nLimit, eMove, nSettings);
 }
@@ -229,35 +328,36 @@ DataStream::~DataStream()
 {
     if (mbRunning)
         StopImport();
-    mxThread->mbTerminate = true;
-    mxThread->maStart.set();
-    mxThread->join();
+
     if (mxReaderThread.is())
+    {
         mxReaderThread->endThread();
+        mxReaderThread->join();
+    }
     delete mpLines;
 }
 
-OString DataStream::ConsumeLine()
+DataStream::Line DataStream::ConsumeLine()
 {
     if (!mpLines || mnLinesCount >= mpLines->size())
     {
         mnLinesCount = 0;
-        if (mxReaderThread->mbTerminateReading)
-            return OString();
-        osl::ResettableMutexGuard aGuard(mxReaderThread->maLinesProtector);
+        if (mxReaderThread->isTerminateRequested())
+            return Line();
+
+        osl::ResettableMutexGuard aGuard(mxReaderThread->getLinesMutex());
         if (mpLines)
-            mxReaderThread->maUsedLines.push(mpLines);
-        while (mxReaderThread->maPendingLines.empty())
+            mxReaderThread->pushUsedLines(mpLines);
+
+        while (!mxReaderThread->hasNewLines())
         {
             aGuard.clear(); // unlock
-            mxReaderThread->maConsumeResume.wait();
-            mxReaderThread->maConsumeResume.reset();
+            mxReaderThread->waitForNewLines();
             aGuard.reset(); // lock
         }
-        mpLines = mxReaderThread->maPendingLines.front();
-        mxReaderThread->maPendingLines.pop();
-        if (mxReaderThread->maPendingLines.size() <= 4)
-            mxReaderThread->maProduceResume.set(); // start producer again
+
+        mpLines = mxReaderThread->popNewLines();
+        mxReaderThread->resumeReadStream();
     }
     return mpLines->at(mnLinesCount++);
 }
@@ -327,12 +427,13 @@ void DataStream::StartImport()
             pStream = new SvScriptStream(msURL);
         else
             pStream = new SvFileStream(msURL, STREAM_READ);
-        mxReaderThread = new datastreams::ReaderThread( pStream );
+        mxReaderThread = new datastreams::ReaderThread(pStream, maStartRange.aEnd.Col() - maStartRange.aStart.Col() + 1);
         mxReaderThread->launch();
     }
     mbRunning = true;
     maDocAccess.reset();
-    mxThread->maStart.set();
+
+    maImportTimer.Start();
 }
 
 void DataStream::StopImport()
@@ -342,6 +443,7 @@ void DataStream::StopImport()
 
     mbRunning = false;
     Refresh();
+    maImportTimer.Stop();
 }
 
 void DataStream::SetRefreshOnEmptyLine( bool bVal )
@@ -397,82 +499,10 @@ void DataStream::MoveData()
 
 #if ENABLE_ORCUS
 
-namespace {
-
-struct StrVal
-{
-    ScAddress maPos;
-    OUString maStr;
-
-    StrVal( const ScAddress& rPos, const OUString& rStr ) : maPos(rPos), maStr(rStr) {}
-};
-
-struct NumVal
-{
-    ScAddress maPos;
-    double mfVal;
-
-    NumVal( const ScAddress& rPos, double fVal ) : maPos(rPos), mfVal(fVal) {}
-};
-
-typedef std::vector<StrVal> StrValArray;
-typedef std::vector<NumVal> NumValArray;
-
-/**
- * This handler handles a single line CSV input.
- */
-class CSVHandler
-{
-    ScAddress maPos;
-    SCROW mnRow;
-    SCCOL mnCol;
-    SCCOL mnEndCol;
-    SCTAB mnTab;
-
-    StrValArray maStrs;
-    NumValArray maNums;
-
-public:
-    CSVHandler( const ScAddress& rPos, SCCOL nEndCol ) : maPos(rPos), mnEndCol(nEndCol) {}
-
-    void begin_parse() {}
-    void end_parse() {}
-    void begin_row() {}
-    void end_row() {}
-
-    void cell(const char* p, size_t n)
-    {
-        if (maPos.Col() <= mnEndCol)
-        {
-            OUString aStr(p, n, RTL_TEXTENCODING_UTF8);
-            double fVal;
-            if (ScStringUtil::parseSimpleNumber(aStr, '.', ',', fVal))
-                maNums.push_back(NumVal(maPos, fVal));
-            else
-                maStrs.push_back(StrVal(maPos, aStr));
-        }
-        maPos.IncCol();
-    }
-
-    const StrValArray& getStrs() const { return maStrs; }
-    const NumValArray& getNums() const { return maNums; }
-};
-
-}
-
 void DataStream::Text2Doc()
 {
-    OString aLine = ConsumeLine();
-    orcus::csv_parser_config aConfig;
-    aConfig.delimiters.push_back(',');
-    aConfig.text_qualifier = '"';
-    CSVHandler aHdl(ScAddress(maStartRange.aStart.Col(), mnCurRow, maStartRange.aStart.Tab()), maStartRange.aEnd.Col());
-    orcus::csv_parser<CSVHandler> parser(aLine.getStr(), aLine.getLength(), aHdl, aConfig);
-    parser.parse();
-
-    const StrValArray& rStrs = aHdl.getStrs();
-    const NumValArray& rNums = aHdl.getNums();
-    if (rStrs.empty() && rNums.empty() && mbRefreshOnEmptyLine)
+    Line aLine = ConsumeLine();
+    if (aLine.maCells.empty() && mbRefreshOnEmptyLine)
     {
         // Empty line detected.  Trigger refresh and discard it.
         Refresh();
@@ -481,15 +511,24 @@ void DataStream::Text2Doc()
 
     MoveData();
     {
-        StrValArray::const_iterator it = rStrs.begin(), itEnd = rStrs.end();
-        for (; it != itEnd; ++it)
-            maDocAccess.setStringCell(it->maPos, it->maStr);
-    }
-
-    {
-        NumValArray::const_iterator it = rNums.begin(), itEnd = rNums.end();
-        for (; it != itEnd; ++it)
-            maDocAccess.setNumericCell(it->maPos, it->mfVal);
+        std::vector<Cell>::const_iterator it = aLine.maCells.begin(), itEnd = aLine.maCells.end();
+        SCCOL nCol = maStartRange.aStart.Col();
+        const char* pLineHead = aLine.maLine.getStr();
+        for (; it != itEnd; ++it, ++nCol)
+        {
+            const Cell& rCell = *it;
+            if (rCell.mbValue)
+            {
+                maDocAccess.setNumericCell(
+                    ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()), rCell.mfValue);
+            }
+            else
+            {
+                maDocAccess.setStringCell(
+                    ScAddress(nCol, mnCurRow, maStartRange.aStart.Tab()),
+                    OUString(pLineHead+rCell.maStr.Pos, rCell.maStr.Size, RTL_TEXTENCODING_UTF8));
+            }
+        }
     }
 
     if (meMove == NO_MOVE)
@@ -514,7 +553,6 @@ void DataStream::Text2Doc() {}
 
 bool DataStream::ImportData()
 {
-    SolarMutexGuard aGuard;
     if (!mbValuesInLine)
         // We no longer support this mode. To be deleted later.
         return false;
@@ -526,6 +564,14 @@ bool DataStream::ImportData()
     return mbRunning;
 }
 
+IMPL_LINK_NOARG(DataStream, ImportTimerHdl)
+{
+    if (ImportData())
+        maImportTimer.Start();
+
+    return 0;
+}
+
 } // namespace sc
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/sc/source/ui/inc/datastream.hxx b/sc/source/ui/inc/datastream.hxx
index 5a4d8cd..5600a09 100644
--- a/sc/source/ui/inc/datastream.hxx
+++ b/sc/source/ui/inc/datastream.hxx
@@ -14,6 +14,7 @@
 
 #include <rtl/ref.hxx>
 #include <rtl/ustring.hxx>
+#include <vcl/timer.hxx>
 #include <address.hxx>
 
 #include <boost/noncopyable.hpp>
@@ -33,15 +34,37 @@ namespace datastreams {
     class ReaderThread;
 }
 
-typedef std::vector<OString> LinesList;
 
 class DataStream : boost::noncopyable
 {
-    OString ConsumeLine();
-    void MoveData();
-    void Text2Doc();
-
 public:
+    struct Cell
+    {
+        struct Str
+        {
+            size_t Pos;
+            size_t Size;
+        };
+
+        union
+        {
+            Str maStr;
+            double mfValue;
+        };
+
+        bool mbValue;
+
+        Cell();
+        Cell( const Cell& r );
+    };
+
+    struct Line
+    {
+        OString maLine;
+        std::vector<Cell> maCells;
+    };
+    typedef std::vector<Line> LinesType;
+
     enum MoveType { NO_MOVE, RANGE_DOWN, MOVE_DOWN, MOVE_UP };
     enum { SCRIPT_STREAM = 1, VALUES_IN_LINE = 2 };
 
@@ -75,8 +98,13 @@ public:
     void SetRefreshOnEmptyLine( bool bVal );
 
 private:
+    Line ConsumeLine();
+    void MoveData();
+    void Text2Doc();
     void Refresh();
 
+    DECL_LINK( ImportTimerHdl, void* );
+
 private:
     ScDocShell* mpDocShell;
     ScDocument* mpDoc;
@@ -89,14 +117,16 @@ private:
     bool mbRunning;
     bool mbValuesInLine;
     bool mbRefreshOnEmptyLine;
-    LinesList* mpLines;
+    LinesType* mpLines;
     size_t mnLinesCount;
     size_t mnLinesSinceRefresh;
     double mfLastRefreshTime;
     SCROW mnCurRow;
     ScRange maStartRange;
     ScRange maEndRange;
-    rtl::Reference<datastreams::CallerThread> mxThread;
+
+    Timer maImportTimer;
+
     rtl::Reference<datastreams::ReaderThread> mxReaderThread;
 };
 
diff --git a/sc/source/ui/miscdlgs/datastreamdlg.cxx b/sc/source/ui/miscdlgs/datastreamdlg.cxx
index 8a3bee5..b0cc2b0 100644
--- a/sc/source/ui/miscdlgs/datastreamdlg.cxx
+++ b/sc/source/ui/miscdlgs/datastreamdlg.cxx
@@ -100,7 +100,7 @@ ScRange DataStreamDlg::GetStartRange()
     OUString aStr = m_pEdRange->GetText();
     ScDocument* pDoc = mpDocShell->GetDocument();
     ScRange aRange;
-    sal_uInt16 nRes = aRange.Parse(aStr, pDoc);
+    sal_uInt16 nRes = aRange.Parse(aStr, pDoc, pDoc->GetAddressConvention());
     if ((nRes & SCA_VALID) != SCA_VALID || !aRange.IsValid())
     {
         // Invalid range.
@@ -118,11 +118,12 @@ ScRange DataStreamDlg::GetStartRange()
 void DataStreamDlg::Init( const DataStream& rStrm )
 {
     m_pCbUrl->SetText(rStrm.GetURL());
+    ScDocument* pDoc = mpDocShell->GetDocument();
 
     ScRange aRange = rStrm.GetRange();
     ScRange aTopRange = aRange;
     aTopRange.aEnd.SetRow(aTopRange.aStart.Row());
-    OUString aStr = aTopRange.Format(SCA_VALID);
+    OUString aStr = aTopRange.Format(SCR_ABS_3D, pDoc, pDoc->GetAddressConvention());
     m_pEdRange->SetText(aStr);
     SCROW nRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
 
commit c1ed8bb33219a3c2f6b35e37f56febc022698121
Author: Kohei Yoshida <kohei.yoshida at collabora.com>
Date:   Fri Dec 20 19:57:28 2013 -0500

    Add internal cell function __DEBUG_VAR to sniff arbitrary internal state.
    
    Useful for debugging in a more flashy way.  But never ever document this
    for end users.  If you are an end user reading this, use this at your
    own risk.  You have been warned.

diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index e8c40aa..1051b73 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -39,7 +39,7 @@ namespace formula
 {
     using namespace ::com::sun::star;
 
-    static const sal_Char* pInternal[ 1 ] = { "TTT" };
+    static const sal_Char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
 
 namespace {
 
@@ -794,6 +794,8 @@ bool FormulaCompiler::IsOpCodeVolatile( OpCode eOp )
         case ocIndirectXL:
             // ocOffset results in indirect references.
         case ocOffset:
+            // ocDebugVar shows internal value that may change as the internal state changes.
+        case ocDebugVar:
             bRet = true;
             break;
         default:
diff --git a/include/formula/compiler.hrc b/include/formula/compiler.hrc
index 9bda945..e11f825 100644
--- a/include/formula/compiler.hrc
+++ b/include/formula/compiler.hrc
@@ -442,10 +442,11 @@
 /*** Internal ***/
 #define SC_OPCODE_INTERNAL_BEGIN   9999
 #define SC_OPCODE_TTT              9999
-#define SC_OPCODE_INTERNAL_END     9999
+#define SC_OPCODE_DEBUG_VAR       10000
+#define SC_OPCODE_INTERNAL_END    10000
 
 /*** from here on ExtraData contained ***/
-#define SC_OPCODE_DATA_TOKEN_1    10000
+#define SC_OPCODE_DATA_TOKEN_1    10001
 
 #define SC_OPCODE_NONE           0xFFFF
 
diff --git a/include/formula/opcode.hxx b/include/formula/opcode.hxx
index 58f3d41..c86b3a3 100644
--- a/include/formula/opcode.hxx
+++ b/include/formula/opcode.hxx
@@ -432,6 +432,7 @@ enum OpCodeEnum
     // internal stuff
         ocInternalBegin     = SC_OPCODE_INTERNAL_BEGIN,
         ocTTT               = SC_OPCODE_TTT,
+        ocDebugVar          = SC_OPCODE_DEBUG_VAR,
         ocInternalEnd       = SC_OPCODE_INTERNAL_END,
     // from here on ExtraData
         ocDataToken1        = SC_OPCODE_DATA_TOKEN_1,
diff --git a/sc/source/core/inc/interpre.hxx b/sc/source/core/inc/interpre.hxx
index f8fc0e9..cc67580 100644
--- a/sc/source/core/inc/interpre.hxx
+++ b/sc/source/core/inc/interpre.hxx
@@ -563,6 +563,7 @@ void ScBitXor();
 void ScBitRshift();
 void ScBitLshift();
 void ScTTT();
+void ScDebugVar();
 
 /** Obtain the date serial number for a given date.
     @param bStrict
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 496c151..dcb676e 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -40,6 +40,7 @@
 #include <tools/urlobj.hxx>
 #include <rtl/math.hxx>
 #include <rtl/ustring.hxx>
+#include <svtools/miscopt.hxx>
 #include <ctype.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -89,7 +90,7 @@ enum ScanState
     ssStop
 };
 
-static const sal_Char* pInternal[ 1 ] = { "TTT" };
+static const sal_Char* pInternal[2] = { "TTT", "__DEBUG_VAR" };
 
 using namespace ::com::sun::star::i18n;
 
@@ -3441,6 +3442,13 @@ bool ScCompiler::NextNewToken( bool bInArray )
     if ( cSymbol[0] < 128 )
     {
         bMayBeFuncName = rtl::isAsciiAlpha( cSymbol[0] );
+        if (!bMayBeFuncName)
+        {
+            SvtMiscOptions aOpt;
+            if (aOpt.IsExperimentalMode())
+                bMayBeFuncName = (cSymbol[0] == '_' && cSymbol[1] == '_');
+        }
+
         bAsciiNonAlnum = !bMayBeFuncName && !rtl::isAsciiDigit( cSymbol[0] );
     }
     else
diff --git a/sc/source/core/tool/interpr4.cxx b/sc/source/core/tool/interpr4.cxx
index 6983d3e..c5ebbd3 100644
--- a/sc/source/core/tool/interpr4.cxx
+++ b/sc/source/core/tool/interpr4.cxx
@@ -4219,6 +4219,7 @@ StackVar ScInterpreter::Interpret()
                 case ocBitRshift        : ScBitRshift();                break;
                 case ocBitLshift        : ScBitLshift();                break;
                 case ocTTT              : ScTTT();                      break;
+                case ocDebugVar         : ScDebugVar();                 break;
                 case ocNone : nFuncFmtType = NUMBERFORMAT_UNDEFINED;    break;
                 default : PushError( errUnknownOpCode);                 break;
             }
diff --git a/sc/source/core/tool/interpr7.cxx b/sc/source/core/tool/interpr7.cxx
index bb647c1..aa07090 100644
--- a/sc/source/core/tool/interpr7.cxx
+++ b/sc/source/core/tool/interpr7.cxx
@@ -9,12 +9,16 @@
 
 #include "interpre.hxx"
 #include <rtl/strbuf.hxx>
+#include <formula/errorcodes.hxx>
+#include <svtools/miscopt.hxx>
 
 #include <com/sun/star/ucb/XSimpleFileAccess3.hpp>
 #include <com/sun/star/ucb/SimpleFileAccess.hpp>
 #include <com/sun/star/io/XInputStream.hpp>
 
 #include "libxml/xpath.h"
+#include <dpobject.hxx>
+#include <document.hxx>
 
 #include <boost/shared_ptr.hpp>
 #include <cstring>
@@ -205,4 +209,49 @@ void ScInterpreter::ScWebservice()
     }
 }
 
+void ScInterpreter::ScDebugVar()
+{
+    // This is to be used by developers only!  Never document this for end
+    // users.  This is a convenient way to extract arbitrary internal state to
+    // a cell for easier debugging.
+
+    SvtMiscOptions aMiscOptions;
+    if (!aMiscOptions.IsExperimentalMode())
+    {
+        PushError(ScErrorCodes::errNoName);
+        return;
+    }
+
+    if (!MustHaveParamCount(GetByte(), 1))
+    {
+        PushIllegalParameter();
+        return;
+    }
+
+    rtl_uString* p = GetString().getDataIgnoreCase();
+    if (!p)
+    {
+        PushIllegalParameter();
+        return;
+    }
+
+    OUString aStrUpper(p);
+
+    if (aStrUpper == "PIVOTCOUNT")
+    {
+        // Set the number of pivot tables in the document.
+
+        double fVal = 0.0;
+        if (pDok->HasPivotTable())
+        {
+            const ScDPCollection* pDPs = pDok->GetDPCollection();
+            fVal = pDPs->GetCount();
+        }
+        PushDouble(fVal);
+        return;
+    }
+
+    PushIllegalParameter();
+}
+
 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */


More information about the Libreoffice-commits mailing list