summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEike Rathke <erack@erack.de>2011-08-12 17:48:37 +0200
committerEike Rathke <erack@erack.de>2011-08-14 00:12:45 +0200
commitbaca3632869b2b0b81e5a7dd83b189d3c8367652 (patch)
tree08d0a12349f5245b188f00ef0d40216c8cde0bdb
parent7e8e85adbee73346403c364326544487677cd5c6 (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
-rw-r--r--formula/inc/formula/FormulaCompiler.hxx6
-rw-r--r--formula/source/core/api/FormulaCompiler.cxx75
-rw-r--r--sc/inc/compiler.hxx4
-rw-r--r--sc/source/core/tool/compiler.cxx211
-rw-r--r--sc/source/core/tool/interpr1.cxx2
-rw-r--r--sc/source/core/tool/token.cxx11
6 files changed, 243 insertions, 66 deletions
diff --git a/formula/inc/formula/FormulaCompiler.hxx b/formula/inc/formula/FormulaCompiler.hxx
index 9ff456beb4d2..e053c57751df 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 8fd651225676..c1b4e5f9ae3b 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 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: