diff options
author | Eike Rathke <erack@erack.de> | 2011-08-12 17:48:37 +0200 |
---|---|---|
committer | Eike Rathke <erack@erack.de> | 2011-08-14 00:12:45 +0200 |
commit | baca3632869b2b0b81e5a7dd83b189d3c8367652 (patch) | |
tree | 08d0a12349f5245b188f00ef0d40216c8cde0bdb /sc | |
parent | 7e8e85adbee73346403c364326544487677cd5c6 (diff) |
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
Diffstat (limited to 'sc')
-rw-r--r-- | sc/inc/compiler.hxx | 4 | ||||
-rw-r--r-- | sc/source/core/tool/compiler.cxx | 211 | ||||
-rw-r--r-- | sc/source/core/tool/interpr1.cxx | 2 | ||||
-rw-r--r-- | sc/source/core/tool/token.cxx | 11 |
4 files changed, 162 insertions, 66 deletions
diff --git a/sc/inc/compiler.hxx b/sc/inc/compiler.hxx index 2148b00bf8e6..8d54b873869a 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 55c8229d0bc1..e17b44c005b1 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 54a177956e12..1fa28cffe1c1 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 165bdd6a0e75..b947f1d63ca5 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: |