[Libreoffice-commits] .: formula/inc formula/source sc/inc sc/source

Eike Rathke erack at kemper.freedesktop.org
Sat Aug 13 15:13:10 PDT 2011


 formula/inc/formula/FormulaCompiler.hxx     |    6 
 formula/source/core/api/FormulaCompiler.cxx |   75 +++++++++
 sc/inc/compiler.hxx                         |    4 
 sc/source/core/tool/compiler.cxx            |  211 +++++++++++++++++++---------
 sc/source/core/tool/interpr1.cxx            |    2 
 sc/source/core/tool/token.cxx               |   11 +
 6 files changed, 243 insertions(+), 66 deletions(-)

New commits:
commit baca3632869b2b0b81e5a7dd83b189d3c8367652
Author: Eike Rathke <erack at erack.de>
Date:   Fri Aug 12 17:48:37 2011 +0200

    fdo#37391 write and read [#REF!] in ODFF for reference errors
    
    * write [#REF!] to ODFF when any part of the reference is invalid
    * read [#REF!] as reference error
    * display #REF! in UI
    + parse #REF! in UI
    + implemented error constants defined in ODFF as error tokens
    + parse error constants from ODFF and in UI
    * fixed SUM, AVERAGE, SUMSQ, PRODUCT to propagate error

diff --git a/formula/inc/formula/FormulaCompiler.hxx b/formula/inc/formula/FormulaCompiler.hxx
index 9ff456b..e053c57 100644
--- a/formula/inc/formula/FormulaCompiler.hxx
+++ b/formula/inc/formula/FormulaCompiler.hxx
@@ -211,6 +211,9 @@ public:
             const ::com::sun::star::sheet::FormulaOpCodeMapEntry > & rMapping,
             bool bEnglish );
 
+    /** Get current OpCodeMap in effect. */
+    inline OpCodeMapPtr GetCurrentOpCodeMap() const { return mxSymbols; }
+
     /** Get OpCode for English symbol.
         Used in XFunctionAccess to create token array.
         @param rName
@@ -272,6 +275,9 @@ protected:
     virtual void LocalizeString( String& rName );   // modify rName - input: exact name
     virtual sal_Bool IsImportingXML() const;
 
+    sal_uInt16 GetErrorConstant( const String& rName );
+    void AppendErrorConstant( rtl::OUStringBuffer& rBuffer, sal_uInt16 nError );
+
     sal_Bool   GetToken();
     OpCode NextToken();
     void PutCode( FormulaTokenRef& );
diff --git a/formula/source/core/api/FormulaCompiler.cxx b/formula/source/core/api/FormulaCompiler.cxx
index 8fd6512..c1b4e5f 100644
--- a/formula/source/core/api/FormulaCompiler.cxx
+++ b/formula/source/core/api/FormulaCompiler.cxx
@@ -825,6 +825,78 @@ void FormulaCompiler::OpCodeMap::copyFrom( const OpCodeMap& r )
     // TODO: maybe copy the external maps too?
 }
 // -----------------------------------------------------------------------------
+
+sal_uInt16 FormulaCompiler::GetErrorConstant( const String& rName )
+{
+    sal_uInt16 nError = 0;
+    OpCodeHashMap::const_iterator iLook( mxSymbols->getHashMap()->find( rName));
+    if (iLook != mxSymbols->getHashMap()->end())
+    {
+        switch ((*iLook).second)
+        {
+            // Not all may make sense in a formula, but these we know as 
+            // opcodes.
+            case ocErrNull:
+                nError = errNoCode;
+                break;
+            case ocErrDivZero:
+                nError = errDivisionByZero;
+                break;
+            case ocErrValue:
+                nError = errNoValue;
+                break;
+            case ocErrRef:
+                nError = errNoRef;
+                break;
+            case ocErrName:
+                nError = errNoName;
+                break;
+            case ocErrNum:
+                nError = errIllegalFPOperation;
+                break;
+            case ocErrNA:
+                nError = NOTAVAILABLE;
+                break;
+            default:
+                ;   // nothing
+        }
+    }
+    return nError;
+}
+
+
+void FormulaCompiler::AppendErrorConstant( rtl::OUStringBuffer& rBuffer, sal_uInt16 nError )
+{
+    OpCode eOp;
+    switch (nError)
+    {
+        default:
+        case errNoCode:
+            eOp = ocErrNull;
+            break;
+        case errDivisionByZero:
+            eOp = ocErrDivZero;
+            break;
+        case errNoValue:
+            eOp = ocErrValue;
+            break;
+        case errNoRef:
+            eOp = ocErrRef;
+            break;
+        case errNoName:
+            eOp = ocErrName;
+            break;
+        case errIllegalFPOperation:
+            eOp = ocErrNum;
+            break;
+        case NOTAVAILABLE:
+            eOp = ocErrNA;
+            break;
+    }
+    rBuffer.append( mxSymbols->getSymbol( eOp));
+}
+
+// -----------------------------------------------------------------------------
 sal_Int32 FormulaCompiler::OpCodeMap::getOpCodeUnknown()
 {
     static const sal_Int32 kOpCodeUnknown = -1;
@@ -1646,6 +1718,9 @@ FormulaToken* FormulaCompiler::CreateStringFromToken( rtl::OUStringBuffer& rBuff
                     rBuffer.append(aAddIn);
                 }
             break;
+            case svError:
+                AppendErrorConstant( rBuffer, t->GetError());
+            break;
             case svByte:
             case svJump:
             case svFAP:
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx
index 2148b00..8d54b87 100644
--- a/sc/inc/compiler.hxx
+++ b/sc/inc/compiler.hxx
@@ -87,6 +87,7 @@
 #define SC_COMPILER_C_ODF_NAME_MARKER 0x00200000  // ODF '$$' marker that starts a defined (range) name
 #define SC_COMPILER_C_CHAR_NAME       0x00400000  // start character of a defined name
 #define SC_COMPILER_C_NAME            0x00800000  // continuation character of a defined name
+#define SC_COMPILER_C_CHAR_ERRCONST   0x01000000  // start character of an error constant ('#')
 
 #define SC_COMPILER_FILE_TAB_SEP      '#'         // 'Doc'#Tab
 
@@ -159,6 +160,7 @@ public:
             sal_uInt16  nIndex;
         } name;
         ScMatrix*    pMat;
+        sal_uInt16   nError;
         sal_Unicode  cStr[ MAXSTRLEN+1 ];   // string (up to 255 characters + 0)
         short        nJump[MAXJUMPCOUNT+1]; // If/Chose token
     };
@@ -184,6 +186,7 @@ public:
     void SetSingleReference( const ScSingleRefData& rRef );
     void SetDoubleReference( const ScComplexRefData& rRef );
     void SetDouble( double fVal );
+    void SetErrorConstant( sal_uInt16 nErr );
 
     // These methods are ok to use, reference count not cleared.
     void SetName(bool bGlobal, sal_uInt16 nIndex);
@@ -349,6 +352,7 @@ private:
     bool IsDBRange( const String& );
     sal_Bool IsColRowName( const String& );
     bool IsBoolean( const String& );
+    bool IsErrorConstant( const String& );
     void AutoCorrectParsedSymbol();
 
     void SetRelNameReference();
diff --git a/sc/source/core/tool/compiler.cxx b/sc/source/core/tool/compiler.cxx
index 55c8229..e17b44c 100644
--- a/sc/source/core/tool/compiler.cxx
+++ b/sc/source/core/tool/compiler.cxx
@@ -107,6 +107,8 @@ const char* dbg_sc_dump( const sal_Unicode c )
 }
 #endif
 
+
+
 CharClass*                          ScCompiler::pCharClassEnglish = NULL;
 const ScCompiler::Convention*       ScCompiler::pConventions[ ]   = { NULL, NULL, NULL, NULL, NULL, NULL };
 
@@ -120,6 +122,7 @@ enum ScanState
     ssGetIdent,
     ssGetReference,
     ssSkipReference,
+    ssGetErrorConstant,
     ssStop
 };
 
@@ -354,7 +357,7 @@ ScCompiler::Convention::Convention( FormulaGrammar::AddressConvention eConv )
     if (FormulaGrammar::CONV_ODF == meConv)
 /* ! */     t[33] |= SC_COMPILER_C_ODF_LABEL_OP;
 /* " */     t[34] = SC_COMPILER_C_CHAR_STRING | SC_COMPILER_C_STRING_SEP;
-/* # */     t[35] = SC_COMPILER_C_WORD_SEP;
+/* # */     t[35] = SC_COMPILER_C_WORD_SEP | SC_COMPILER_C_CHAR_ERRCONST;
 /* $ */     t[36] = SC_COMPILER_C_CHAR_WORD | SC_COMPILER_C_WORD | SC_COMPILER_C_CHAR_IDENT | SC_COMPILER_C_IDENT;
     if (FormulaGrammar::CONV_ODF == meConv)
 /* $ */     t[36] |= SC_COMPILER_C_ODF_NAME_MARKER;
@@ -804,87 +807,78 @@ struct ConventionOOO_A1 : public Convention_A1
         return aString;
     }
 
-    void MakeRefStrImpl( rtl::OUStringBuffer&   rBuffer,
-                         const ScCompiler&      rComp,
-                         const ScComplexRefData&    rRef,
-                         bool bSingleRef,
-                         bool bODF ) const
+
+    void MakeOneRefStrImpl( rtl::OUStringBuffer&    rBuffer,
+                            const ScCompiler&       rComp,
+                            const ScSingleRefData&  rRef,
+                            bool                    bForceTab,
+                            bool                    bODF ) const
     {
-        if (bODF)
-            rBuffer.append(sal_Unicode('['));
-        ScComplexRefData aRef( rRef );
-        // In case absolute/relative positions weren't separately available:
-        // transform relative to absolute!
-        aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
-        if( !bSingleRef )
-            aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
-        if( aRef.Ref1.IsFlag3D() )
+        if( rRef.IsFlag3D() || bForceTab )
         {
-            if (aRef.Ref1.IsTabDeleted())
+            if (rRef.IsTabDeleted())
             {
-                if (!aRef.Ref1.IsTabRel())
+                if (!rRef.IsTabRel())
                     rBuffer.append(sal_Unicode('$'));
-                rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
+                rBuffer.append( rComp.GetCurrentOpCodeMap()->getSymbol( ocErrRef));
                 rBuffer.append(sal_Unicode('.'));
             }
             else
             {
                 String aDoc;
-                String aRefStr( MakeTabStr( rComp, aRef.Ref1.nTab, aDoc ) );
+                String aRefStr( MakeTabStr( rComp, rRef.nTab, aDoc ) );
                 rBuffer.append(aDoc);
-                if (!aRef.Ref1.IsTabRel()) rBuffer.append(sal_Unicode('$'));
+                if (!rRef.IsTabRel())
+                    rBuffer.append(sal_Unicode('$'));
                 rBuffer.append(aRefStr);
             }
         }
         else if (bODF)
             rBuffer.append(sal_Unicode('.'));
-        if (!aRef.Ref1.IsColRel())
+        if (!rRef.IsColRel())
             rBuffer.append(sal_Unicode('$'));
-        if ( aRef.Ref1.IsColDeleted() )
-            rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
+        if ( rRef.IsColDeleted() )
+            rBuffer.append( rComp.GetCurrentOpCodeMap()->getSymbol( ocErrRef));
         else
-            MakeColStr(rBuffer, aRef.Ref1.nCol );
-        if (!aRef.Ref1.IsRowRel())
+            MakeColStr(rBuffer, rRef.nCol );
+        if (!rRef.IsRowRel())
             rBuffer.append(sal_Unicode('$'));
-        if ( aRef.Ref1.IsRowDeleted() )
-            rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
+        if ( rRef.IsRowDeleted() )
+            rBuffer.append( rComp.GetCurrentOpCodeMap()->getSymbol( ocErrRef));
+        else
+            MakeRowStr( rBuffer, rRef.nRow );
+    }
+
+
+    void MakeRefStrImpl( rtl::OUStringBuffer&   rBuffer,
+                         const ScCompiler&      rComp,
+                         const ScComplexRefData&    rRef,
+                         bool bSingleRef,
+                         bool bODF ) const
+    {
+        if (bODF)
+            rBuffer.append(sal_Unicode('['));
+        ScComplexRefData aRef( rRef );
+        // In case absolute/relative positions weren't separately available:
+        // transform relative to absolute!
+        aRef.Ref1.CalcAbsIfRel( rComp.GetPos() );
+        if( !bSingleRef )
+            aRef.Ref2.CalcAbsIfRel( rComp.GetPos() );
+        if (bODF && FormulaGrammar::isODFF( rComp.GetGrammar()) &&
+                (aRef.Ref1.IsColDeleted() || aRef.Ref1.IsRowDeleted() || aRef.Ref1.IsTabDeleted() ||
+                 aRef.Ref2.IsColDeleted() || aRef.Ref2.IsRowDeleted() || aRef.Ref2.IsTabDeleted()))
+            rBuffer.append( rComp.GetCurrentOpCodeMap()->getSymbol( ocErrRef));
+            // For ODFF write [#REF!], but not for PODF so apps reading ODF 
+            // 1.0/1.1 may have a better chance if they implemented the old 
+            // form.
         else
-            MakeRowStr( rBuffer, aRef.Ref1.nRow );
-        if (!bSingleRef)
         {
-            rBuffer.append(sal_Unicode(':'));
-            if (aRef.Ref2.IsFlag3D() || aRef.Ref2.nTab != aRef.Ref1.nTab)
+            MakeOneRefStrImpl( rBuffer, rComp, aRef.Ref1, false, bODF);
+            if (!bSingleRef)
             {
-                if (aRef.Ref2.IsTabDeleted())
-                {
-                    if (!aRef.Ref2.IsTabRel())
-                        rBuffer.append(sal_Unicode('$'));
-                    rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
-                    rBuffer.append(sal_Unicode('.'));
-                }
-                else
-                {
-                    String aDoc;
-                    String aRefStr( MakeTabStr( rComp, aRef.Ref2.nTab, aDoc ) );
-                    rBuffer.append(aDoc);
-                    if (!aRef.Ref2.IsTabRel()) rBuffer.append(sal_Unicode('$'));
-                    rBuffer.append(aRefStr);
-                }
+                rBuffer.append(sal_Unicode(':'));
+                MakeOneRefStrImpl( rBuffer, rComp, aRef.Ref2, (aRef.Ref2.nTab != aRef.Ref1.nTab), bODF);
             }
-            else if (bODF)
-                rBuffer.append(sal_Unicode('.'));
-            if (!aRef.Ref2.IsColRel())
-                rBuffer.append(sal_Unicode('$'));
-            if ( aRef.Ref2.IsColDeleted() )
-                rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
-            else
-                MakeColStr( rBuffer, aRef.Ref2.nCol );
-            if (!aRef.Ref2.IsRowRel())
-                rBuffer.append(sal_Unicode('$'));
-            if ( aRef.Ref2.IsRowDeleted() )
-                rBuffer.append(ScGlobal::GetRscString(STR_NO_REF_TABLE));
-            else
-                MakeRowStr( rBuffer, aRef.Ref2.nRow );
         }
         if (bODF)
             rBuffer.append(sal_Unicode(']'));
@@ -1911,6 +1905,7 @@ xub_StrLen ScCompiler::NextSymbol(bool bInArray)
     int nDecSeps = 0;
     bool bAutoIntersection = false;
     int nRefInName = 0;
+    bool bErrorConstantHadSlash = false;
     mnPredetectedReference = 0;
     // try to parse simple tokens before calling i18n parser
     while ((c != 0) && (eState != ssStop) )
@@ -2015,6 +2010,11 @@ Label_MaskStateMachine:
                     *pSym++ = c;
                     eState = ssGetString;
                 }
+                else if( nMask & SC_COMPILER_C_CHAR_ERRCONST )
+                {
+                    *pSym++ = c;
+                    eState = ssGetErrorConstant;
+                }
                 else if( nMask & SC_COMPILER_C_CHAR_DONTCARE )
                 {
                     nSpaces++;
@@ -2172,6 +2172,50 @@ Label_MaskStateMachine:
                 if( nMask & SC_COMPILER_C_STRING_SEP )
                     eState = ssStop;
                 break;
+            case ssGetErrorConstant:
+                {
+                    // ODFF Error ::= '#' [A-Z0-9]+ ([!?] | ('/' ([A-Z] | ([0-9] [!?]))))
+                    // BUT, in UI these may have been translated! So don't 
+                    // check for ASCII alnum. Note that this construct can't be
+                    // parsed with i18n.
+                    /* TODO: be strict when reading ODFF, check for ASCII alnum 
+                     * and proper continuation after '/'. However, even with 
+                     * the lax parsing only the error constants we have defined 
+                     * as opcode symbols will be recognized and others result 
+                     * in ocBad, so the result is actually conformant. */
+                    bool bAdd = true;
+                    if ('!' == c || '?' == c)
+                        eState = ssStop;
+                    else if ('/' == c)
+                    {
+                        if (!bErrorConstantHadSlash)
+                            bErrorConstantHadSlash = true;
+                        else
+                        {
+                            bAdd = false;
+                            eState = ssStop;
+                        }
+                    }
+                    else if ((nMask & SC_COMPILER_C_WORD_SEP) ||
+                            (c < 128 && !CharClass::isAsciiAlphaNumeric( c)))
+                    {
+                        bAdd = false;
+                        eState = ssStop;
+                    }
+                    if (!bAdd)
+                        --pSrc;
+                    else
+                    {
+                        if (pSym == &cSymbol[ MAXSTRLEN-1 ])
+                        {
+                            SetError( errStringOverflow);
+                            eState = ssStop;
+                        }
+                        else
+                            *pSym++ = c;
+                    }
+                }
+                break;
             case ssGetReference:
                 if( pSym == &cSymbol[ MAXSTRLEN-1 ] )
                 {
@@ -2183,7 +2227,7 @@ Label_MaskStateMachine:
                 // ODF reference: ['External'#$'Sheet'.A1:.B2] with dots being
                 // mandatory also if no sheet name. 'External'# is optional,
                 // sheet name is optional, quotes around sheet name are
-                // optional if no quote contained.
+                // optional if no quote contained. [#REF!] is valid.
                 // 2nd usage: ['Sheet'.$$'DefinedName']
                 // 3rd usage: ['External'#$$'DefinedName']
                 // 4th usage: ['External'#$'Sheet'.$$'DefinedName']
@@ -2220,11 +2264,13 @@ Label_MaskStateMachine:
                     static const int kMarkAhead = (1 << 8);
                     // In marked defined name.
                     static const int kDefName   = (1 << 9);
+                    // Encountered # of #REF!
+                    static const int kRefErr    = (1 << 10);
 
                     bool bAddToSymbol = true;
                     if ((nMask & SC_COMPILER_C_ODF_RBRACKET) && !(nRefInName & kOpen))
                     {
-                        OSL_ENSURE( nRefInName & (kPast | kDefName),
+                        OSL_ENSURE( nRefInName & (kPast | kDefName | kRefErr),
                                 "ScCompiler::NextSymbol: reference: "
                                 "closing bracket ']' without prior sheet name separator '.' violates ODF spec");
                         // eaten, not added to pSym
@@ -2310,6 +2356,8 @@ Label_MaskStateMachine:
                                 bAddToSymbol = !(nRefInName & kDefName);
                             }
                         }
+                        else if ('#' == c && nRefInName == 0)
+                            nRefInName |= kRefErr;
                         else if (cSheetSep == c && !(nRefInName & kOpen))
                         {
                             // unquoted sheet name separator
@@ -2631,7 +2679,13 @@ sal_Bool ScCompiler::IsPredetectedReference( const String& rName )
          * occurrences of insane "valid" sheet names like
          * 'haha.#REF!1fooledyou' and will generate an error on such. */
         if (nPos == 0)
+        {
+            // Per ODFF the correct string for a reference error is just #REF!, 
+            // so pass it on.
+            if (rName.Len() == 5)
+                return IsErrorConstant( rName);
             return false;           // #REF!.AB42 or #REF!42 or #REF!#REF!
+        }
         sal_Unicode c = rName.GetChar(nPos-1);      // before #REF!
         if ('$' == c)
         {
@@ -3269,6 +3323,21 @@ bool ScCompiler::IsBoolean( const String& rName )
         return false;
 }
 
+
+bool ScCompiler::IsErrorConstant( const String& rName )
+{
+    sal_uInt16 nError = GetErrorConstant( rName);
+    if (nError)
+    {
+        ScRawToken aToken;
+        aToken.SetErrorConstant( nError);
+        pRawToken = aToken.Clone();
+        return true;
+    }
+    else
+        return false;
+}
+
 //---------------------------------------------------------------------------
 
 void ScCompiler::AutoCorrectParsedSymbol()
@@ -3511,7 +3580,8 @@ sal_Bool ScCompiler::NextNewToken( bool bInArray )
              * would need an ocBad token with additional error value.
              * FormulaErrorToken wouldn't do because we want to preserve the
              * original string containing partial valid address
-             * information. */
+             * information if not ODFF (in that case it was already handled). 
+             * */
             ScRawToken aToken;
             aToken.SetString( aStr.GetBuffer() );
             aToken.NewOpCode( ocBad );
@@ -3569,8 +3639,19 @@ sal_Bool ScCompiler::NextNewToken( bool bInArray )
         mbRewind = false;
         const String aOrg( cSymbol );
 
-        if (bAsciiNonAlnum && IsOpCode( aOrg, bInArray ))
-            return true;
+        if (bAsciiNonAlnum)
+        {
+            if (cSymbol[0] == '#')
+            {
+                // This can be only an error constant, if any.
+                lcl_UpperAsciiOrI18n( aUpper, aOrg, meGrammar);
+                if (IsErrorConstant( aUpper))
+                    return true;
+                break;  // do; create ocBad token or set error.
+            }
+            if (IsOpCode( aOrg, bInArray ))
+                return true;
+        }
 
         aUpper.Erase();
         bool bAsciiUpper = false;
diff --git a/sc/source/core/tool/interpr1.cxx b/sc/source/core/tool/interpr1.cxx
index 54a1779..1fa28cf 100644
--- a/sc/source/core/tool/interpr1.cxx
+++ b/sc/source/core/tool/interpr1.cxx
@@ -3527,7 +3527,7 @@ double ScInterpreter::IterateParameters( ScIterFunc eFunc, sal_Bool bTextAsZero
             break;
             case svError:
             {
-                Pop();
+                PopError();
                 if ( eFunc == ifCOUNT )
                 {
                     nGlobalError = 0;
diff --git a/sc/source/core/tool/token.cxx b/sc/source/core/tool/token.cxx
index 165bdd6..b947f1d 100644
--- a/sc/source/core/tool/token.cxx
+++ b/sc/source/core/tool/token.cxx
@@ -224,6 +224,14 @@ void ScRawToken::SetDouble(double rVal)
     nRefCnt = 0;
 }
 
+void ScRawToken::SetErrorConstant( sal_uInt16 nErr )
+{
+    eOp   = ocPush;
+    eType = svError;
+    nError = nErr;
+    nRefCnt = 0;
+}
+
 void ScRawToken::SetName(bool bGlobal, sal_uInt16 nIndex)
 {
     eOp = ocName;
@@ -320,6 +328,7 @@ ScRawToken* ScRawToken::Clone() const
             case svSep:         break;
             case svByte:        n += sizeof(ScRawToken::sbyte); break;
             case svDouble:      n += sizeof(double); break;
+            case svError:       n += sizeof(nError); break;
             case svString:      n = sal::static_int_cast<sal_uInt16>( n + GetStrLenBytes( cStr ) + GetStrLenBytes( 1 ) ); break;
             case svSingleRef:
             case svDoubleRef:   n += sizeof(aRef); break;
@@ -406,6 +415,8 @@ FormulaToken* ScRawToken::CreateToken() const
             return new FormulaMissingToken;
         case svSep :
             return new FormulaToken( svSep,eOp );
+        case svError :
+            return new FormulaErrorToken( nError );
         case svUnknown :
             return new FormulaUnknownToken( eOp );
         default:


More information about the Libreoffice-commits mailing list