tdf#96198 detect old ISO/WEEKNUM usage with two arguments, tdf#50950 follow-up

5.0 and earlier implemented WEEKNUM(date,mode) with mode!=1 such that
effectively an ISO 8601 week number was calculated. WEEKNUM was wrongly
saved as ISOWEEKNUM (even with two parameters though it is defined to
have only one) so that when reading it we can try to detect a literal
double argument for mode and if it is not 1 remove it to keep
ISOWEEKNUM(date) instead of calling WEEKNUM(date,mode) which wouldn't
match.

A further change to 5.0 to accept also only one parameter in
WEEKNUM(date) and for this default the mode to not 1 for ISO week will
yield forward compatibility.

Change-Id: I88de7dd809d69b6826a190505d2a1dd3fe79c90b
This commit is contained in:
Eike Rathke 2016-01-05 11:54:32 +01:00
parent 72ac13eb64
commit 2f79244cb3
3 changed files with 98 additions and 1 deletions

View File

@ -1271,6 +1271,7 @@ void FormulaCompiler::Factor()
else
SetError( errPairExpected);
sal_uInt8 nSepCount = 0;
const sal_uInt16 nSepPos = pArr->nIndex - 1; // separator position, if any
if( !bNoParam )
{
nSepCount++;
@ -1289,7 +1290,30 @@ void FormulaCompiler::Factor()
pFacToken->SetByte( nSepCount );
if (nSepCount == 2)
{
pFacToken->NewOpCode( ocWeek, FormulaToken::PrivateAccess());
// An old mode!=1 indicates ISO week, remove argument if
// literal double value and keep function. Anything else
// can not be resolved, there exists no "like ISO but week
// starts on Sunday" mode in WEEKNUM and for an expression
// we can't determine, so let ISOWEEKNUM generate an error
// for two arguments in these cases.
if (pc >= 2 && pArr->nIndex == nSepPos + 3 &&
pArr->pCode[nSepPos+1]->GetType() == svDouble &&
pArr->pCode[nSepPos+1]->GetDouble() != 1.0 &&
pArr->RemoveToken( nSepPos, 2) == 2)
{
// Remove the ocPush/svDouble just removed also from
// the compiler local RPN array.
--pCode, --pc;
(*pCode)->DecRef(); // may be dead now
pFacToken->SetByte( nSepCount - 1 );
}
else
{
/* FIXME: introduce (hidden?) compatibility function? */
#if 0
pFacToken->NewOpCode( ocWeeknumCompat, FormulaToken::PrivateAccess());
#endif
}
}
PutCode( pFacToken );
}

View File

@ -900,6 +900,63 @@ FormulaToken* FormulaTokenArray::ReplaceToken( sal_uInt16 nOffset, FormulaToken*
}
}
sal_uInt16 FormulaTokenArray::RemoveToken( sal_uInt16 nOffset, sal_uInt16 nCount )
{
if (nOffset < nLen)
{
SAL_WARN_IF( nOffset + nCount > nLen, "formula.core",
"FormulaTokenArray::RemoveToken - nOffset " << nOffset << " + nCount " << nCount << " > nLen " << nLen);
const sal_uInt16 nStop = ::std::min( static_cast<sal_uInt16>(nOffset + nCount), nLen);
nCount = nStop - nOffset;
for (sal_uInt16 j = nOffset; j < nStop; ++j)
{
FormulaToken* p = pCode[j];
if (p->GetRef() > 1)
{
for (sal_uInt16 i=0; i < nRPN; ++i)
{
if (pRPN[i] == p)
{
// Shift remaining tokens in pRPN down.
for (sal_uInt16 x=i+1; x < nRPN; ++x)
{
pRPN[x-1] = pRPN[x];
}
--nRPN;
p->DecRef();
if (p->GetRef() == 1)
break; // for
}
}
}
p->DecRef(); // may be dead now
}
// Shift remaining tokens in pCode down.
for (sal_uInt16 x = nStop; x < nLen; ++x)
{
pCode[x-nCount] = pCode[x];
}
nLen -= nCount;
if (nIndex >= nOffset)
{
if (nIndex < nStop)
nIndex = nOffset + 1;
else
nIndex -= nStop - nOffset;
}
return nCount;
}
else
{
SAL_WARN("formula.core","FormulaTokenArray::RemoveToken - nOffset " << nOffset << " >= nLen " << nLen);
return 0;
}
}
FormulaToken* FormulaTokenArray::Add( FormulaToken* t )
{
if( !pCode )

View File

@ -148,6 +148,22 @@ protected:
*/
FormulaToken* ReplaceToken( sal_uInt16 nOffset, FormulaToken*, ReplaceMode eMode );
/** Remove a sequence of tokens from pCode array, and pRPN array if the
tokens are referenced there.
This' nLen and nRPN are adapted, as is nIndex if it points behind
nOffset. If nIndex points into the to be removed range
(nOffset < nIndex < nOffset+nCount) it is set to nOffset+1.
@param nOffset
Start offset into pCode.
@param nCount
Count of tokens to remove.
@return The actual number of tokens removed from pCode array.
*/
sal_uInt16 RemoveToken( sal_uInt16 nOffset, sal_uInt16 nCount );
inline void SetCombinedBitsRecalcMode( ScRecalcMode nBits )
{ nMode |= (nBits & ~RECALCMODE_EMASK); }
inline ScRecalcMode GetCombinedBitsRecalcMode() const