/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* * This file is part of the LibreOffice project. * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. * * This file incorporates work covered by the following license notice: * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed * with this work for additional information regarding copyright * ownership. The ASF licenses this file to you under the Apache * License, Version 2.0 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
sal_Int16 nYear = GetInt16(); if (nGlobalError != FormulaError::NONE)
{
PushError( nGlobalError); return;
} if ( nYear < 100 )
nYear = mrContext.NFExpandTwoDigitYear( nYear ); if (nYear < 1583 || nYear > 9956)
{ // Valid Gregorian and maximum year constraints not met.
PushIllegalArgument(); return;
} // don't worry, be happy :) int B,C,D,E,F,G,H,I,K,L,M,N,O;
N = nYear % 19;
B = int(nYear / 100);
C = nYear % 100;
D = int(B / 4);
E = B % 4;
F = int((B + 8) / 25);
G = int((B - F + 1) / 3);
H = (19 * N + B - D - G + 15) % 30;
I = int(C / 4);
K = C % 4;
L = (32 + 2 * E + 2 * I - H - K) % 7;
M = int((N + 11 * H + 22 * L) / 451);
O = H + L - 7 * M + 114;
sal_Int16 nDay = sal::static_int_cast<sal_Int16>( O % 31 + 1 );
sal_Int16 nMonth = sal::static_int_cast<sal_Int16>( int(O / 31) );
PushDouble( GetDateSerial( nYear, nMonth, nDay, true ) );
}
// Weekend days defined by string, Sunday...Saturday for ( int i = 0; i < 7; i++ )
bWeekendMask[ i ] = static_cast<bool>(nWeekendDays[ ( i == 6 ? 0 : i + 1 ) ]);
}
} else
{ for ( int i = 0; i < 7; i++ )
bWeekendMask[ i] = false;
void ScInterpreter::ScGetDiffDate360()
{ /* Implementation follows * http://www.bondmarkets.com/eCommerce/SMD_Fields_030802.pdf * Appendix B: Day-Count Bases, there are 7 different ways to calculate the * 30-days count. That document also claims that Excel implements the "PSA * 30" or "NASD 30" method (funny enough they also state that Excel is the * only tool that does so). * * Note that the definition given in * http://msdn.microsoft.com/library/en-us/office97/html/SEB7C.asp * is _not_ the way how it is actually calculated by Excel (that would not * even match any of the 7 methods mentioned above) and would result in the * following test cases producing wrong results according to that appendix B: * * 28-Feb-95 31-Aug-95 181 instead of 180 * 29-Feb-96 31-Aug-96 181 instead of 180 * 30-Jan-96 31-Mar-96 61 instead of 60 * 31-Jan-96 31-Mar-96 61 instead of 60 * * Still, there is a difference between OOoCalc and Excel: * In Excel: * 02-Feb-99 31-Mar-00 results in 419 * 31-Mar-00 02-Feb-99 results in -418 * In Calc the result is 419 respectively -419. I consider the -418 a bug in Excel.
*/
bool bFlag = nParamCount == 3 && GetBool();
sal_Int32 nDate2 = GetFloor32();
sal_Int32 nDate1 = GetFloor32(); if (nGlobalError != FormulaError::NONE)
PushError( nGlobalError); else
{
sal_Int32 nSign; // #i84934# only for non-US European algorithm swap dates. Else // follow Excel's meaningless extrapolation for "interoperability". if (bFlag && (nDate2 < nDate1))
{
nSign = nDate1;
nDate1 = nDate2;
nDate2 = nSign;
nSign = -1;
} else
nSign = 1;
Date aDate1 = mrContext.NFGetNullDate();
aDate1.AddDays( nDate1);
Date aDate2 = mrContext.NFGetNullDate();
aDate2.AddDays( nDate2); if (aDate1.GetDay() == 31)
aDate1.AddDays( -1); elseif (!bFlag)
{ if (aDate1.GetMonth() == 2)
{ switch ( aDate1.GetDay() )
{ case 28 : if ( !aDate1.IsLeapYear() )
aDate1.SetDay(30); break; case 29 :
aDate1.SetDay(30); break;
}
}
} if (aDate2.GetDay() == 31)
{ if (!bFlag )
{ if (aDate1.GetDay() == 30)
aDate2.AddDays( -1);
} else
aDate2.SetDay(30);
}
PushDouble( static_cast<double>(nSign) *
( static_cast<double>(aDate2.GetDay()) + static_cast<double>(aDate2.GetMonth()) * 30.0 + static_cast<double>(aDate2.GetYear()) * 360.0
- static_cast<double>(aDate1.GetDay()) - static_cast<double>(aDate1.GetMonth()) * 30.0
- static_cast<double>(aDate1.GetYear()) * 360.0) );
}
}
// fdo#44456 function DATEDIF as defined in ODF1.2 (Par. 6.10.3) void ScInterpreter::ScGetDateDif()
{ if ( !MustHaveParamCount( GetByte(), 3 ) ) return;
if (nGlobalError != FormulaError::NONE)
{
PushError( nGlobalError); return;
}
// Excel doesn't swap dates or return negative numbers, so don't we. if (nDate1 > nDate2)
{
PushIllegalArgument(); return;
}
double dd = nDate2 - nDate1; // Zero difference or number of days can be returned immediately. if (dd == 0.0 || aInterval.equalsIgnoreAsciiCase( "d" ))
{
PushDouble( dd ); return;
}
// split dates in day, month, year for use with formats other than "d"
sal_uInt16 d1, m1, d2, m2;
sal_Int16 y1, y2;
Date aDate1( mrContext.NFGetNullDate());
aDate1.AddDays( nDate1);
y1 = aDate1.GetYear();
m1 = aDate1.GetMonth();
d1 = aDate1.GetDay();
Date aDate2( mrContext.NFGetNullDate());
aDate2.AddDays( nDate2);
y2 = aDate2.GetYear();
m2 = aDate2.GetMonth();
d2 = aDate2.GetDay();
// Close the year 0 gap to calculate year difference. if (y1 < 0 && y2 > 0)
++y1; elseif (y1 > 0 && y2 < 0)
++y2;
if ( aInterval.equalsIgnoreAsciiCase( "m" ) )
{ // Return number of months. int md = m2 - m1 + 12 * (y2 - y1); if (d1 > d2)
--md;
PushInt( md );
} elseif ( aInterval.equalsIgnoreAsciiCase( "y" ) )
{ // Return number of years. int yd; if ( y2 > y1 )
{ if (m2 > m1 || (m2 == m1 && d2 >= d1))
yd = y2 - y1; // complete years between dates else
yd = y2 - y1 - 1; // one incomplete year
} else
{ // Year is equal as we don't allow reversed arguments, no // complete year between dates.
yd = 0;
}
PushInt( yd );
} elseif ( aInterval.equalsIgnoreAsciiCase( "md" ) )
{ // Return number of days, excluding months and years. // This is actually the remainder of days when subtracting years // and months from the difference of dates. Birthday-like 23 years // and 10 months and 19 days.
// Algorithm's roll-over behavior extracted from Excel by try and // error... // If day1 <= day2 then simply day2 - day1. // If day1 > day2 then set month1 to month2-1 and year1 to // year2(-1) and subtract dates, e.g. for 2012-01-28,2012-03-01 set // 2012-02-28 and then (2012-03-01)-(2012-02-28) => 2 days (leap // year). // For 2011-01-29,2011-03-01 the non-existent 2011-02-29 rolls over // to 2011-03-01 so the result is 0. Same for day 31 in months with // only 30 days.
// Condition corresponds with "y". if (m2 > m1 || (m2 == m1 && d2 >= d1))
aDate1.SetYear( y2 ); else
aDate1.SetYear( y2 - 1 ); // XXX NOTE: Excel for the case 1988-06-22,2012-05-11 returns // 323, whereas the result here is 324. Don't they use the leap // year of 2012? // http://www.cpearson.com/excel/datedif.aspx "DATEDIF And Leap // Years" is not correct and Excel 2010 correctly returns 0 in // both cases mentioned there. Also using year1 as mentioned // produces incorrect results in other cases and different from // Excel 2010. Apparently they fixed some calculations.
aDate1.Normalize(); double fd = aDate2 - aDate1;
PushDouble( fd );
} else
PushIllegalArgument(); // unsupported format
}
double fVal = 0.0; if (nParamCount == 1)
fVal = ::rtl::math::round( GetDouble(), 0, eMode ); else
{ const sal_Int16 nDec = GetInt16(); constdouble fX = GetDouble(); if (nGlobalError == FormulaError::NONE)
{ // A quite aggressive approach with 12 significant digits. // However, using 14 or some other doesn't work because other // values may fail, like =ROUNDDOWN(2-5E-015;13) would produce // 2 (another example in tdf#124286).
constexpr sal_Int16 kSigDig = 12;
{ // tdf124286 : round to significant digits before rounding // down or up to avoid unexpected rounding errors // caused by decimal -> binary -> decimal conversion
double fRes = fX; // Similar to RoundSignificant() but omitting the back-scaling // and interim integer rounding before the final rounding, // which would result in double rounding. Instead, adjust the // decimals and round into integer part before scaling back. constdouble fTemp = floor( log10( std::abs(fRes))) + 1.0 - kSigDig; // Avoid inaccuracy of negative powers of 10. if (fTemp < 0.0)
fRes *= pow(10.0, -fTemp); else
fRes /= pow(10.0, fTemp); if (std::isfinite(fRes))
{ // fRes is now at a decimal normalized scale. // Truncate up-rounding to opposite direction for values // like 0.0600000000000005 =ROUNDUP(8.06-8;2) that here now // is 600000000000.005 and otherwise would yield 0.07 if (eMode == rtl_math_RoundingMode_Up)
fRes = ::rtl::math::approxFloor(fRes);
fVal = ::rtl::math::round( fRes, nDec + fTemp, eMode ); if (fTemp < 0.0)
fVal /= pow(10.0, -fTemp); else
fVal *= pow(10.0, fTemp);
} else
{ // Overflow. Let our round() decide if and how to round.
fVal = ::rtl::math::round( fX, nDec, eMode );
}
} else
fVal = ::rtl::math::round( fX, nDec, eMode );
}
}
PushDouble(fVal);
}
void ScInterpreter::RoundSignificant( double fX, double fDigits, double &fRes )
{ double fTemp = floor( log10( std::abs(fX) ) ) + 1.0 - fDigits; double fIn = fX; // Avoid inaccuracy of negative powers of 10. if (fTemp < 0.0)
fIn *= pow(10.0, -fTemp); else
fIn /= pow(10.0, fTemp); // For very large fX there might be an overflow in fIn resulting in // non-finite. rtl::math::round() handles that and it will be propagated as // usual.
fRes = ::rtl::math::round(fIn); if (fTemp < 0.0)
fRes /= pow(10.0, -fTemp); else
fRes *= pow(10.0, fTemp);
}
/** tdf69552 ODFF1.2 function CEILING and Excel function CEILING.MATH In essence, the difference between the two is that ODFF-CEILING needs to have arguments value and significance of the same sign and with CEILING.MATH the sign of argument significance is irrevelevant. This is why ODFF-CEILING is exported to Excel as CEILING.MATH and CEILING.MATH is imported in Calc as CEILING.MATH
*/ void ScInterpreter::ScCeil( bool bODFF )
{
sal_uInt8 nParamCount = GetByte(); if ( !MustHaveParamCount( nParamCount, 1, 3 ) ) return;
/** tdf69552 ODFF1.2 function FLOOR and Excel function FLOOR.MATH In essence, the difference between the two is that ODFF-FLOOR needs to have arguments value and significance of the same sign and with FLOOR.MATH the sign of argument significance is irrevelevant. This is why ODFF-FLOOR is exported to Excel as FLOOR.MATH and FLOOR.MATH is imported in Calc as FLOOR.MATH
*/ void ScInterpreter::ScFloor( bool bODFF )
{
sal_uInt8 nParamCount = GetByte(); if ( !MustHaveParamCount( nParamCount, 1, 3 ) ) return;
void ScInterpreter::ScNper()
{
sal_uInt8 nParamCount = GetByte(); if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) return; bool bPayInAdvance = nParamCount == 5 && GetBool(); double fFV = nParamCount >= 4 ? GetDouble() : 0; double fPV = GetDouble(); // Present Value double fPmt = GetDouble(); // Payment double fRate = GetDouble(); // Note that due to the function specification in ODFF1.2 (and Excel) the // amount to be paid to get from fPV to fFV is fFV_+_fPV. if ( fPV + fFV == 0.0 )
PushDouble( 0.0 ); elseif (fRate == 0.0)
PushDouble(-(fPV + fFV)/fPmt); elseif (bPayInAdvance)
PushDouble(log(-(fRate*fFV-fPmt*(1.0+fRate))/(fRate*fPV+fPmt*(1.0+fRate)))
/ std::log1p(fRate)); else
PushDouble(log(-(fRate*fFV-fPmt)/(fRate*fPV+fPmt)) / std::log1p(fRate));
}
bool ScInterpreter::RateIteration( double fNper, double fPayment, double fPv, double fFv, bool bPayType, double & fGuess )
{ // See also #i15090# // Newton-Raphson method: x(i+1) = x(i) - f(x(i)) / f'(x(i)) // This solution handles integer and non-integer values of Nper different. // If ODFF will constraint Nper to integer, the distinction of cases can be // removed; only the integer-part is needed then. bool bValid = true, bFound = false; double fX, fXnew, fTerm, fTermDerivation; double fGeoSeries, fGeoSeriesDerivation; const sal_uInt16 nIterationsMax = 150;
sal_uInt16 nCount = 0; constdouble fEpsilonSmall = 1.0E-14; if ( bPayType )
{ // payment at beginning of each period
fFv = fFv - fPayment;
fPv = fPv + fPayment;
} if (fNper == ::rtl::math::round( fNper ))
{ // Nper is an integer value
fX = fGuess; while (!bFound && nCount < nIterationsMax)
{ double fPowN, fPowNminus1; // for (1.0+fX)^Nper and (1.0+fX)^(Nper-1)
fPowNminus1 = pow( 1.0+fX, fNper-1.0);
fPowN = fPowNminus1 * (1.0+fX); if (fX == 0.0)
{
fGeoSeries = fNper;
fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
} else
{
fGeoSeries = (fPowN-1.0)/fX;
fGeoSeriesDerivation = fNper * fPowNminus1 / fX - fGeoSeries / fX;
}
fTerm = fFv + fPv *fPowN+ fPayment * fGeoSeries;
fTermDerivation = fPv * fNper * fPowNminus1 + fPayment * fGeoSeriesDerivation; if (std::abs(fTerm) < fEpsilonSmall)
bFound = true; // will catch root which is at an extreme else
{ if (fTermDerivation == 0.0)
fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope else
fXnew = fX - fTerm / fTermDerivation;
nCount++; // more accuracy not possible in oscillating cases
bFound = (std::abs(fXnew - fX) < SCdEpsilon);
fX = fXnew;
}
} // Gnumeric returns roots < -1, Excel gives an error in that cases, // ODFF says nothing about it. Enable the statement, if you want Excel's // behavior. //bValid =(fX >=-1.0); // Update 2013-06-17: Gnumeric (v1.12.2) doesn't return roots <= -1 // anymore.
bValid = (fX > -1.0);
} else
{ // Nper is not an integer value.
fX = (fGuess < -1.0) ? -1.0 : fGuess; // start with a valid fX while (bValid && !bFound && nCount < nIterationsMax)
{ if (fX == 0.0)
{
fGeoSeries = fNper;
fGeoSeriesDerivation = fNper * (fNper-1.0)/2.0;
} else
{
fGeoSeries = (pow( 1.0+fX, fNper) - 1.0) / fX;
fGeoSeriesDerivation = fNper * pow( 1.0+fX, fNper-1.0) / fX - fGeoSeries / fX;
}
fTerm = fFv + fPv *pow(1.0 + fX,fNper)+ fPayment * fGeoSeries;
fTermDerivation = fPv * fNper * pow( 1.0+fX, fNper-1.0) + fPayment * fGeoSeriesDerivation; if (std::abs(fTerm) < fEpsilonSmall)
bFound = true; // will catch root which is at an extreme else
{ if (fTermDerivation == 0.0)
fXnew = fX + 1.1 * SCdEpsilon; // move away from zero slope else
fXnew = fX - fTerm / fTermDerivation;
nCount++; // more accuracy not possible in oscillating cases
bFound = (std::abs(fXnew - fX) < SCdEpsilon);
fX = fXnew;
bValid = (fX >= -1.0); // otherwise pow(1.0+fX,fNper) will fail
}
}
}
fGuess = fX; // return approximate root return bValid && bFound;
}
// In Calc UI it is the function RATE(Nper;Pmt;Pv;Fv;Type;Guess) void ScInterpreter::ScRate()
{
nFuncFmtType = SvNumFormatType::PERCENT;
sal_uInt8 nParamCount = GetByte(); if ( !MustHaveParamCount( nParamCount, 3, 6 ) ) return;
if (!bValid)
{ /* TODO: try also for specified guess values, not only default? As is, * a specified 0.1 guess may be error result but a default 0.1 guess * may succeed. On the other hand, using a different guess value than
* the specified one may not be desired, even if that didn't match. */ if (bDefaultGuess)
{ /* TODO: this is rather ugly, instead of looping over different * guess values and doing a Newton goal seek for each we could * first insert the values into the RATE equation to obtain a set * of y values and then do a bisecting goal seek, possibly using
* different algorithms. */ double fX = fOrigGuess; for (int nStep = 2; nStep <= 10 && !bValid; ++nStep)
{
fGuess = fX * nStep;
bValid = RateIteration( fNper, fPayment, fPv, fFv, bPayType, fGuess); if (!bValid)
{
fGuess = fX / nStep;
bValid = RateIteration( fNper, fPayment, fPv, fFv, bPayType, fGuess);
}
}
} if (!bValid)
SetError(FormulaError::NoConvergence);
}
PushDouble(fGuess);
}
const formula::FormulaToken* x1 = p1st.get(); const formula::FormulaToken* x2 = p2nd.get(); if (sv1 == svRefList || sv2 == svRefList)
{ // Now this is a bit nasty but it simplifies things, and having // intersections with lists isn't too common, if at all... // Convert a reference to list. const formula::FormulaToken* xt[2] = { x1, x2 };
StackVar sv[2] = { sv1, sv2 }; // There may only be one reference; the other is necessarily a list // Ensure converted list proper destruction
std::unique_ptr<formula::FormulaToken> p; for (size_t i=0; i<2; ++i)
{ if (sv[i] == svSingleRef)
{
ScComplexRefData aRef;
aRef.Ref1 = aRef.Ref2 = *xt[i]->GetSingleRef();
p.reset(new ScRefListToken);
p->GetRefList()->push_back( aRef);
xt[i] = p.get();
} elseif (sv[i] == svDoubleRef)
{
ScComplexRefData aRef = *xt[i]->GetDoubleRef();
p.reset(new ScRefListToken);
p->GetRefList()->push_back( aRef);
xt[i] = p.get();
}
}
x1 = xt[0];
x2 = xt[1];
OUString aStyle2; // Style after timer if (nParamCount >= 3)
aStyle2 = GetString().getString();
tools::Long nTimeOut = 0; // timeout if (nParamCount >= 2)
nTimeOut = static_cast<tools::Long>(GetDouble()*1000.0);
OUString aStyle1 = GetString().getString(); // Style for immediate
if (nTimeOut < 0)
nTimeOut = 0;
// Execute request to apply style if ( !mrDoc.IsClipOrUndo() )
{
ScDocShell* pShell = mrDoc.GetDocumentShell(); if (pShell)
{ // Normalize style names right here, making sure that character case is correct, // and that we only apply anything when there's something to apply auto pPool = mrDoc.GetStyleSheetPool(); if (!aStyle1.isEmpty())
{ if (auto pNewStyle = pPool->FindAutoStyle(aStyle1))
aStyle1 = pNewStyle->GetName(); else
aStyle1.clear();
} if (!aStyle2.isEmpty())
{ if (auto pNewStyle = pPool->FindAutoStyle(aStyle2))
aStyle2 = pNewStyle->GetName(); else
aStyle2.clear();
} // notify object shell directly! if (!aStyle1.isEmpty() || !aStyle2.isEmpty())
{ const ScStyleSheet* pStyle = mrDoc.GetStyle(aPos.Col(), aPos.Row(), aPos.Tab());
if (!pLink)
{
pLink = new ScDdeLink( mrDoc, aAppl, aTopic, aItem, nMode );
mpLinkManager->InsertDDELink( pLink, aAppl, aTopic, aItem ); if ( mpLinkManager->GetLinks().size() == 1 ) // the first one?
{
SfxBindings* pBindings = mrDoc.GetViewBindings(); if (pBindings)
pBindings->Invalidate( SID_LINKS ); // Link-Manager enabled
}
//if the document was just loaded, but the ScDdeLink entry was missing, then //don't update this link until the links are updated in response to the users //decision if (!mrDoc.HasLinkFormulaNeedingCheck())
{ //TODO: evaluate asynchron ???
pLink->TryUpdate(); // TryUpdate doesn't call Update multiple times
}
if (pMyFormulaCell)
{ // StartListening after the Update to avoid circular references
pMyFormulaCell->StartListening( *pLink );
}
} else
{ if (pMyFormulaCell)
pMyFormulaCell->StartListening( *pLink );
}
// If a new Error from Reschedule appears when the link is executed then reset the errorflag
// Actual after strange fDebug1 and fVal < fMult is fDebug2 == fBase, but // anyway it can't be compared, then bDirt is executed an everything is good...
OUString aToUnit = GetString().getString();
OUString aFromUnit = GetString().getString(); double fVal = GetDouble(); if ( nGlobalError != FormulaError::NONE )
PushError( nGlobalError); else
{ // first of all search for the given order; if it can't be found then search for the inverse double fConv; if ( ScGlobal::GetUnitConverter()->GetValue( fConv, aFromUnit, aToUnit ) )
PushDouble( fVal * fConv ); elseif ( ScGlobal::GetUnitConverter()->GetValue( fConv, aToUnit, aFromUnit ) )
PushDouble( fVal / fConv ); else
PushNA();
}
}
/** Appends a digit (0 to 9) to the passed string. */ void lclAppendDigit( OStringBuffer& rText, sal_Int32 nDigit )
{ switch( nDigit )
{ case 0: rText.append( UTF8_TH_0 ); break; case 1: rText.append( UTF8_TH_1 ); break; case 2: rText.append( UTF8_TH_2 ); break; case 3: rText.append( UTF8_TH_3 ); break; case 4: rText.append( UTF8_TH_4 ); break; case 5: rText.append( UTF8_TH_5 ); break; case 6: rText.append( UTF8_TH_6 ); break; case 7: rText.append( UTF8_TH_7 ); break; case 8: rText.append( UTF8_TH_8 ); break; case 9: rText.append( UTF8_TH_9 ); break; default: OSL_FAIL( "lclAppendDigit - illegal digit" );
}
}
/** Appends a value raised to a power of 10: nDigit*10^nPow10. @param nDigit A digit in the range from 1 to 9. @param nPow10 A value in the range from 2 to 5.
*/ void lclAppendPow10( OStringBuffer& rText, sal_Int32 nDigit, sal_Int32 nPow10 )
{
OSL_ENSURE( (1 <= nDigit) && (nDigit <= 9), "lclAppendPow10 - illegal digit" );
lclAppendDigit( rText, nDigit ); switch( nPow10 )
{ case 2: rText.append( UTF8_TH_1E2 ); break; case 3: rText.append( UTF8_TH_1E3 ); break; case 4: rText.append( UTF8_TH_1E4 ); break; case 5: rText.append( UTF8_TH_1E5 ); break; default: OSL_FAIL( "lclAppendPow10 - illegal power" );
}
}
bool bOldSyntax = false; if (nParamCount == 2)
{ // if the first parameter is a ref, assume old syntax
StackVar eFirstType = GetStackType(2); if (eFirstType == svSingleRef || eFirstType == svDoubleRef)
bOldSyntax = true;
}
sal_uInt16 i = nFilterCount; while (i > 0)
{
--i; /* TODO: also, in case of numeric the entire filter match should * not be on a (even if locale independent) formatted string down
* below in pDPObj->GetPivotData(). */
bool bEvaluateFormatIndex; switch (GetRawStackType())
{ case svSingleRef: case svDoubleRef:
bEvaluateFormatIndex = true; break; default:
bEvaluateFormatIndex = false;
}
// Parse possible number from MatchValueName and format // locale independent as MatchValue.
sal_uInt32 nNumFormat = 0; double fValue; if (mrContext.NFIsNumberFormat( aFilters[i].MatchValueName, nNumFormat, fValue))
aFilters[i].MatchValue = ScDPCache::GetLocaleIndependentFormattedString(
fValue, mrContext, nNumFormat); else
aFilters[i].MatchValue = aFilters[i].MatchValueName;
}
aDataFieldName = GetString().getString(); // First parameter is data field name.
}
// Early bail-out, don't grind through data pilot cache and all. if (nGlobalError != FormulaError::NONE)
{
PushError( nGlobalError); return;
}
// NOTE : MS Excel docs claim to use the 'most recent' which is not // exactly the same as what we do in ScDocument::GetDPAtBlock // However we do need to use GetDPABlock
ScDPObject* pDPObj = mrDoc.GetDPAtBlock(aBlock); if (!pDPObj)
{
PushError(FormulaError::NoRef); return;
}
if (bOldSyntax)
{
OUString aFilterStr = aDataFieldName;
std::vector<sal_Int16> aFilterFuncs; if (!pDPObj->ParseFilters(aDataFieldName, aFilters, aFilterFuncs, aFilterStr))
{
PushError(FormulaError::NoRef); return;
}
// TODO : For now, we ignore filter functions since we couldn't find a // live example of how they are supposed to be used. We'll support // this again once we come across a real-world example.
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.