forked from amazingfate/loongoffice
4513 lines
163 KiB
C++
4513 lines
163 KiB
C++
/* -*- 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 .
|
|
*/
|
|
|
|
#include "interpre.hxx"
|
|
|
|
#include <rangelst.hxx>
|
|
#include <sfx2/app.hxx>
|
|
#include <sfx2/docfile.hxx>
|
|
#include <sfx2/objsh.hxx>
|
|
#include <sfx2/docfilt.hxx>
|
|
#include <basic/sbmeth.hxx>
|
|
#include <basic/sbmod.hxx>
|
|
#include <basic/sbstar.hxx>
|
|
#include <basic/sbx.hxx>
|
|
#include <basic/sbxobj.hxx>
|
|
#include <basic/sbuno.hxx>
|
|
#include <svl/zforlist.hxx>
|
|
#include <svl/sharedstringpool.hxx>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <signal.h>
|
|
|
|
#include <com/sun/star/table/XCellRange.hpp>
|
|
#include <com/sun/star/sheet/XSheetCellRange.hpp>
|
|
#include <comphelper/processfactory.hxx>
|
|
|
|
#include "global.hxx"
|
|
#include "dbdata.hxx"
|
|
#include "formulacell.hxx"
|
|
#include "callform.hxx"
|
|
#include "addincol.hxx"
|
|
#include "document.hxx"
|
|
#include "dociter.hxx"
|
|
#include "docoptio.hxx"
|
|
#include "scmatrix.hxx"
|
|
#include "adiasync.hxx"
|
|
#include "sc.hrc"
|
|
#include "cellsuno.hxx"
|
|
#include "optuno.hxx"
|
|
#include "rangeseq.hxx"
|
|
#include "addinlis.hxx"
|
|
#include "jumpmatrix.hxx"
|
|
#include "parclass.hxx"
|
|
#include "externalrefmgr.hxx"
|
|
#include <formula/FormulaCompiler.hxx>
|
|
#include "macromgr.hxx"
|
|
#include "doubleref.hxx"
|
|
#include "queryparam.hxx"
|
|
#include "tokenarray.hxx"
|
|
|
|
#include <math.h>
|
|
#include <float.h>
|
|
#include <map>
|
|
#include <algorithm>
|
|
#include <functional>
|
|
#include <basic/basmgr.hxx>
|
|
#include <vbahelper/vbaaccesshelper.hxx>
|
|
#include <memory>
|
|
#include <boost/scoped_array.hpp>
|
|
|
|
using namespace com::sun::star;
|
|
using namespace formula;
|
|
using ::std::auto_ptr;
|
|
|
|
#define ADDIN_MAXSTRLEN 256
|
|
|
|
//-----------------------------static data -----------------
|
|
|
|
// Funktionen fuer den Zugriff auf das Document
|
|
|
|
void ScInterpreter::ReplaceCell( ScAddress& rPos )
|
|
{
|
|
size_t ListSize = pDok->aTableOpList.size();
|
|
for ( size_t i = 0; i < ListSize; ++i )
|
|
{
|
|
ScInterpreterTableOpParams* pTOp = &pDok->aTableOpList[ i ];
|
|
if ( rPos == pTOp->aOld1 )
|
|
{
|
|
rPos = pTOp->aNew1;
|
|
return ;
|
|
}
|
|
else if ( rPos == pTOp->aOld2 )
|
|
{
|
|
rPos = pTOp->aNew2;
|
|
return ;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::ReplaceCell( SCCOL& rCol, SCROW& rRow, SCTAB& rTab )
|
|
{
|
|
ScAddress aCellPos( rCol, rRow, rTab );
|
|
size_t ListSize = pDok->aTableOpList.size();
|
|
for ( size_t i = 0; i < ListSize; ++i )
|
|
{
|
|
ScInterpreterTableOpParams* pTOp = &pDok->aTableOpList[ i ];
|
|
if ( aCellPos == pTOp->aOld1 )
|
|
{
|
|
rCol = pTOp->aNew1.Col();
|
|
rRow = pTOp->aNew1.Row();
|
|
rTab = pTOp->aNew1.Tab();
|
|
return ;
|
|
}
|
|
else if ( aCellPos == pTOp->aOld2 )
|
|
{
|
|
rCol = pTOp->aNew2.Col();
|
|
rRow = pTOp->aNew2.Row();
|
|
rTab = pTOp->aNew2.Tab();
|
|
return ;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool ScInterpreter::IsTableOpInRange( const ScRange& rRange )
|
|
{
|
|
if ( rRange.aStart == rRange.aEnd )
|
|
return false; // not considered to be a range in TableOp sense
|
|
|
|
// we can't replace a single cell in a range
|
|
size_t ListSize = pDok->aTableOpList.size();
|
|
for ( size_t i = 0; i < ListSize; ++i )
|
|
{
|
|
ScInterpreterTableOpParams* pTOp = &pDok->aTableOpList[ i ];
|
|
if ( rRange.In( pTOp->aOld1 ) )
|
|
return true;
|
|
if ( rRange.In( pTOp->aOld2 ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
sal_uLong ScInterpreter::GetCellNumberFormat( const ScAddress& rPos, ScRefCellValue& rCell )
|
|
{
|
|
sal_uLong nFormat;
|
|
sal_uInt16 nErr;
|
|
if (rCell.isEmpty())
|
|
{
|
|
nFormat = pDok->GetNumberFormat( rPos );
|
|
nErr = 0;
|
|
}
|
|
else
|
|
{
|
|
if (rCell.meType == CELLTYPE_FORMULA)
|
|
nErr = rCell.mpFormula->GetErrCode();
|
|
else
|
|
nErr = 0;
|
|
nFormat = pDok->GetNumberFormat( rPos );
|
|
}
|
|
|
|
SetError(nErr);
|
|
return nFormat;
|
|
}
|
|
|
|
/// Only ValueCell, formula cells already store the result rounded.
|
|
double ScInterpreter::GetValueCellValue( const ScAddress& rPos, double fOrig )
|
|
{
|
|
if ( bCalcAsShown && fOrig != 0.0 )
|
|
{
|
|
sal_uLong nFormat = pDok->GetNumberFormat( rPos );
|
|
fOrig = pDok->RoundValueAsShown( fOrig, nFormat );
|
|
}
|
|
return fOrig;
|
|
}
|
|
|
|
sal_uInt16 ScInterpreter::GetCellErrCode( const ScRefCellValue& rCell )
|
|
{
|
|
return rCell.meType == CELLTYPE_FORMULA ? rCell.mpFormula->GetErrCode() : 0;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
bool isEmptyString( const OUString& rStr )
|
|
{
|
|
if (rStr.isEmpty())
|
|
return true;
|
|
else if (rStr[0] == ' ')
|
|
{
|
|
const sal_Unicode* p = rStr.getStr() + 1;
|
|
const sal_Unicode* const pStop = p - 1 + rStr.getLength();
|
|
while (p < pStop && *p == ' ')
|
|
++p;
|
|
if (p == pStop)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/** Convert string content to numeric value.
|
|
|
|
Converted are only integer numbers including exponent, and ISO 8601 dates
|
|
and times in their extended formats with separators. Anything else,
|
|
especially fractional numeric values with decimal separators or dates other
|
|
than ISO 8601 would be locale dependent and is a no-no. Leading and
|
|
trailing blanks are ignored.
|
|
|
|
The following ISO 8601 formats are converted:
|
|
|
|
CCYY-MM-DD
|
|
CCYY-MM-DDThh:mm
|
|
CCYY-MM-DDThh:mm:ss
|
|
CCYY-MM-DDThh:mm:ss,s
|
|
CCYY-MM-DDThh:mm:ss.s
|
|
hh:mm
|
|
hh:mm:ss
|
|
hh:mm:ss,s
|
|
hh:mm:ss.s
|
|
|
|
The century CC may not be omitted and the two-digit year setting is not
|
|
taken into account. Instead of the T date and time separator exactly one
|
|
blank may be used.
|
|
|
|
If a date is given, it must be a valid Gregorian calendar date. In this
|
|
case the optional time must be in the range 00:00 to 23:59:59.99999...
|
|
If only time is given, it may have any value for hours, taking elapsed time
|
|
into account; minutes and seconds are limited to the value 59 as well.
|
|
*/
|
|
|
|
double ScInterpreter::ConvertStringToValue( const OUString& rStr )
|
|
{
|
|
// We keep ScCalcConfig::STRING_CONVERSION_LOCALE_DEPENDENT default until
|
|
// we provide a friendly way to convert string numbers into numbers in the UI.
|
|
|
|
double fValue = 0.0;
|
|
if (mnStringNoValueError == errCellNoValue)
|
|
{
|
|
// Requested that all strings result in 0, error handled by caller.
|
|
SetError( mnStringNoValueError);
|
|
return fValue;
|
|
}
|
|
|
|
switch (maCalcConfig.meStringConversion)
|
|
{
|
|
case ScCalcConfig::STRING_CONVERSION_AS_ERROR:
|
|
SetError( mnStringNoValueError);
|
|
return fValue;
|
|
case ScCalcConfig::STRING_CONVERSION_AS_ZERO:
|
|
return fValue;
|
|
case ScCalcConfig::STRING_CONVERSION_LOCALE_DEPENDENT:
|
|
{
|
|
if (maCalcConfig.mbEmptyStringAsZero)
|
|
{
|
|
// The number scanner does not accept empty strings or strings
|
|
// containing only spaces, be on par in these cases with what was
|
|
// accepted in OOo and is in AOO (see also the
|
|
// STRING_CONVERSION_UNAMBIGUOUS branch) and convert to 0 to prevent
|
|
// interoperability nightmares.
|
|
|
|
if (isEmptyString( rStr))
|
|
return fValue;
|
|
}
|
|
|
|
sal_uInt32 nFIndex = 0;
|
|
if (!pFormatter->IsNumberFormat(rStr, nFIndex, fValue))
|
|
{
|
|
SetError( mnStringNoValueError);
|
|
fValue = 0.0;
|
|
}
|
|
return fValue;
|
|
}
|
|
break;
|
|
case ScCalcConfig::STRING_CONVERSION_UNAMBIGUOUS:
|
|
{
|
|
if (!maCalcConfig.mbEmptyStringAsZero)
|
|
{
|
|
if (isEmptyString( rStr))
|
|
{
|
|
SetError( mnStringNoValueError);
|
|
return fValue;
|
|
}
|
|
}
|
|
}
|
|
// continue below, pulled from switch case for better readability
|
|
break;
|
|
}
|
|
|
|
OUString aStr( rStr);
|
|
rtl_math_ConversionStatus eStatus;
|
|
sal_Int32 nParseEnd;
|
|
// Decimal and group separator 0 => only integer and possibly exponent,
|
|
// stops at first non-digit non-sign.
|
|
fValue = ::rtl::math::stringToDouble( aStr, 0, 0, &eStatus, &nParseEnd);
|
|
sal_Int32 nLen;
|
|
if (eStatus == rtl_math_ConversionStatus_Ok && nParseEnd < (nLen = aStr.getLength()))
|
|
{
|
|
// Not at string end, check for trailing blanks or switch to date or
|
|
// time parsing or bail out.
|
|
const sal_Unicode* const pStart = aStr.getStr();
|
|
const sal_Unicode* p = pStart + nParseEnd;
|
|
const sal_Unicode* const pStop = pStart + nLen;
|
|
switch (*p++)
|
|
{
|
|
case ' ':
|
|
while (p < pStop && *p == ' ')
|
|
++p;
|
|
if (p < pStop)
|
|
SetError( mnStringNoValueError);
|
|
break;
|
|
case '-':
|
|
case ':':
|
|
{
|
|
bool bDate = (*(p-1) == '-');
|
|
enum State { year = 0, month, day, hour, minute, second, fraction, done, blank, stop };
|
|
sal_Int32 nUnit[done] = {0,0,0,0,0,0,0};
|
|
const sal_Int32 nLimit[done] = {0,12,31,0,59,59,0};
|
|
State eState = (bDate ? month : minute);
|
|
nCurFmtType = (bDate ? NUMBERFORMAT_DATE : NUMBERFORMAT_TIME);
|
|
nUnit[eState-1] = aStr.copy( 0, nParseEnd).toInt32();
|
|
const sal_Unicode* pLastStart = p;
|
|
// Ensure there's no preceding sign. Negative dates
|
|
// currently aren't handled correctly. Also discard
|
|
// +CCYY-MM-DD
|
|
p = pStart;
|
|
while (p < pStop && *p == ' ')
|
|
++p;
|
|
if (p < pStop && !rtl::isAsciiDigit(*p))
|
|
SetError( mnStringNoValueError);
|
|
p = pLastStart;
|
|
while (p < pStop && !nGlobalError && eState < blank)
|
|
{
|
|
if (eState == minute)
|
|
nCurFmtType |= NUMBERFORMAT_TIME;
|
|
if (rtl::isAsciiDigit(*p))
|
|
{
|
|
// Maximum 2 digits per unit, except fractions.
|
|
if (p - pLastStart >= 2 && eState != fraction)
|
|
SetError( mnStringNoValueError);
|
|
}
|
|
else if (p > pLastStart)
|
|
{
|
|
// We had at least one digit.
|
|
if (eState < done)
|
|
{
|
|
nUnit[eState] = aStr.copy( pLastStart - pStart, p - pLastStart).toInt32();
|
|
if (nLimit[eState] && nLimit[eState] < nUnit[eState])
|
|
SetError( mnStringNoValueError);
|
|
}
|
|
pLastStart = p + 1; // hypothetical next start
|
|
// Delimiters must match, a trailing delimiter
|
|
// yields an invalid date/time.
|
|
switch (eState)
|
|
{
|
|
case month:
|
|
// Month must be followed by separator and
|
|
// day, no trailing blanks.
|
|
if (*p != '-' || (p+1 == pStop))
|
|
SetError( mnStringNoValueError);
|
|
break;
|
|
case day:
|
|
if ((*p != 'T' || (p+1 == pStop)) && *p != ' ')
|
|
SetError( mnStringNoValueError);
|
|
// Take one blank as a valid delimiter
|
|
// between date and time.
|
|
break;
|
|
case hour:
|
|
// Hour must be followed by separator and
|
|
// minute, no trailing blanks.
|
|
if (*p != ':' || (p+1 == pStop))
|
|
SetError( mnStringNoValueError);
|
|
break;
|
|
case minute:
|
|
if ((*p != ':' || (p+1 == pStop)) && *p != ' ')
|
|
SetError( mnStringNoValueError);
|
|
if (*p == ' ')
|
|
eState = done;
|
|
break;
|
|
case second:
|
|
if (((*p != ',' && *p != '.') || (p+1 == pStop)) && *p != ' ')
|
|
SetError( mnStringNoValueError);
|
|
if (*p == ' ')
|
|
eState = done;
|
|
break;
|
|
case fraction:
|
|
eState = done;
|
|
break;
|
|
case year:
|
|
case done:
|
|
case blank:
|
|
case stop:
|
|
SetError( mnStringNoValueError);
|
|
break;
|
|
}
|
|
eState = static_cast<State>(eState + 1);
|
|
}
|
|
else
|
|
SetError( mnStringNoValueError);
|
|
++p;
|
|
}
|
|
if (eState == blank)
|
|
{
|
|
while (p < pStop && *p == ' ')
|
|
++p;
|
|
if (p < pStop)
|
|
SetError( mnStringNoValueError);
|
|
eState = stop;
|
|
}
|
|
|
|
// Month without day, or hour without minute.
|
|
if (eState == month || (eState == day && p <= pLastStart) ||
|
|
eState == hour || (eState == minute && p <= pLastStart))
|
|
SetError( mnStringNoValueError);
|
|
|
|
if (!nGlobalError)
|
|
{
|
|
// Catch the very last unit at end of string.
|
|
if (p > pLastStart && eState < done)
|
|
{
|
|
nUnit[eState] = aStr.copy( pLastStart - pStart, p - pLastStart).toInt32();
|
|
if (nLimit[eState] && nLimit[eState] < nUnit[eState])
|
|
SetError( mnStringNoValueError);
|
|
}
|
|
if (bDate && nUnit[hour] > 23)
|
|
SetError( mnStringNoValueError);
|
|
if (!nGlobalError)
|
|
{
|
|
if (bDate && nUnit[day] == 0)
|
|
nUnit[day] = 1;
|
|
double fFraction = (nUnit[fraction] <= 0 ? 0.0 :
|
|
::rtl::math::pow10Exp( nUnit[fraction],
|
|
static_cast<int>( -ceil( log10( static_cast<double>( nUnit[fraction]))))));
|
|
fValue = (bDate ? GetDateSerial(
|
|
sal::static_int_cast<sal_Int16>(nUnit[year]),
|
|
sal::static_int_cast<sal_Int16>(nUnit[month]),
|
|
sal::static_int_cast<sal_Int16>(nUnit[day]),
|
|
true, false) : 0.0);
|
|
fValue += ((nUnit[hour] * 3600) + (nUnit[minute] * 60) + nUnit[second] + fFraction) / 86400.0;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
SetError( mnStringNoValueError);
|
|
}
|
|
if (nGlobalError)
|
|
fValue = 0.0;
|
|
}
|
|
return fValue;
|
|
}
|
|
|
|
double ScInterpreter::GetCellValue( const ScAddress& rPos, ScRefCellValue& rCell )
|
|
{
|
|
sal_uInt16 nErr = nGlobalError;
|
|
nGlobalError = 0;
|
|
double nVal = GetCellValueOrZero(rPos, rCell);
|
|
if ( !nGlobalError || nGlobalError == errCellNoValue )
|
|
nGlobalError = nErr;
|
|
return nVal;
|
|
}
|
|
|
|
double ScInterpreter::GetCellValueOrZero( const ScAddress& rPos, ScRefCellValue& rCell )
|
|
{
|
|
double fValue = 0.0;
|
|
|
|
CellType eType = rCell.meType;
|
|
switch (eType)
|
|
{
|
|
case CELLTYPE_FORMULA:
|
|
{
|
|
ScFormulaCell* pFCell = rCell.mpFormula;
|
|
sal_uInt16 nErr = pFCell->GetErrCode();
|
|
if( !nErr )
|
|
{
|
|
if (pFCell->IsValue())
|
|
{
|
|
fValue = pFCell->GetValue();
|
|
pDok->GetNumberFormatInfo( nCurFmtType, nCurFmtIndex,
|
|
rPos );
|
|
}
|
|
else
|
|
{
|
|
fValue = ConvertStringToValue(pFCell->GetString().getString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
fValue = 0.0;
|
|
SetError(nErr);
|
|
}
|
|
}
|
|
break;
|
|
case CELLTYPE_VALUE:
|
|
{
|
|
fValue = rCell.mfValue;
|
|
nCurFmtIndex = pDok->GetNumberFormat( rPos );
|
|
nCurFmtType = pFormatter->GetType( nCurFmtIndex );
|
|
if ( bCalcAsShown && fValue != 0.0 )
|
|
fValue = pDok->RoundValueAsShown( fValue, nCurFmtIndex );
|
|
}
|
|
break;
|
|
case CELLTYPE_STRING:
|
|
case CELLTYPE_EDIT:
|
|
{
|
|
// SUM(A1:A2) differs from A1+A2. No good. But people insist on
|
|
// it ... #i5658#
|
|
OUString aStr = rCell.getString(pDok);
|
|
fValue = ConvertStringToValue( aStr );
|
|
}
|
|
break;
|
|
case CELLTYPE_NONE:
|
|
fValue = 0.0; // empty or broadcaster cell
|
|
break;
|
|
}
|
|
|
|
return fValue;
|
|
}
|
|
|
|
void ScInterpreter::GetCellString( svl::SharedString& rStr, ScRefCellValue& rCell )
|
|
{
|
|
sal_uInt16 nErr = 0;
|
|
|
|
switch (rCell.meType)
|
|
{
|
|
case CELLTYPE_STRING:
|
|
case CELLTYPE_EDIT:
|
|
rStr = mrStrPool.intern(rCell.getString(pDok));
|
|
break;
|
|
case CELLTYPE_FORMULA:
|
|
{
|
|
ScFormulaCell* pFCell = rCell.mpFormula;
|
|
nErr = pFCell->GetErrCode();
|
|
if (pFCell->IsValue())
|
|
{
|
|
double fVal = pFCell->GetValue();
|
|
sal_uLong nIndex = pFormatter->GetStandardFormat(
|
|
NUMBERFORMAT_NUMBER,
|
|
ScGlobal::eLnge);
|
|
OUString aStr;
|
|
pFormatter->GetInputLineString(fVal, nIndex, aStr);
|
|
rStr = mrStrPool.intern(aStr);
|
|
}
|
|
else
|
|
rStr = pFCell->GetString();
|
|
}
|
|
break;
|
|
case CELLTYPE_VALUE:
|
|
{
|
|
double fVal = rCell.mfValue;
|
|
sal_uLong nIndex = pFormatter->GetStandardFormat(
|
|
NUMBERFORMAT_NUMBER,
|
|
ScGlobal::eLnge);
|
|
OUString aStr;
|
|
pFormatter->GetInputLineString(fVal, nIndex, aStr);
|
|
rStr = mrStrPool.intern(aStr);
|
|
}
|
|
break;
|
|
default:
|
|
rStr = svl::SharedString::getEmptyString();
|
|
break;
|
|
}
|
|
|
|
SetError(nErr);
|
|
}
|
|
|
|
bool ScInterpreter::CreateDoubleArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
|
|
SCCOL nCol2, SCROW nRow2, SCTAB nTab2, sal_uInt8* pCellArr)
|
|
{
|
|
|
|
// Old Add-Ins are hard limited to sal_uInt16 values.
|
|
#if MAXCOLCOUNT_DEFINE > USHRT_MAX
|
|
#error Add check for columns > USHRT_MAX!
|
|
#endif
|
|
if (nRow1 > USHRT_MAX || nRow2 > USHRT_MAX)
|
|
return false;
|
|
|
|
sal_uInt16 nCount = 0;
|
|
sal_uInt16* p = (sal_uInt16*) pCellArr;
|
|
*p++ = static_cast<sal_uInt16>(nCol1);
|
|
*p++ = static_cast<sal_uInt16>(nRow1);
|
|
*p++ = static_cast<sal_uInt16>(nTab1);
|
|
*p++ = static_cast<sal_uInt16>(nCol2);
|
|
*p++ = static_cast<sal_uInt16>(nRow2);
|
|
*p++ = static_cast<sal_uInt16>(nTab2);
|
|
sal_uInt16* pCount = p;
|
|
*p++ = 0;
|
|
sal_uInt16 nPos = 14;
|
|
SCTAB nTab = nTab1;
|
|
ScAddress aAdr;
|
|
while (nTab <= nTab2)
|
|
{
|
|
aAdr.SetTab( nTab );
|
|
SCROW nRow = nRow1;
|
|
while (nRow <= nRow2)
|
|
{
|
|
aAdr.SetRow( nRow );
|
|
SCCOL nCol = nCol1;
|
|
while (nCol <= nCol2)
|
|
{
|
|
aAdr.SetCol( nCol );
|
|
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
if (!aCell.isEmpty())
|
|
{
|
|
sal_uInt16 nErr = 0;
|
|
double nVal = 0.0;
|
|
bool bOk = true;
|
|
switch (aCell.meType)
|
|
{
|
|
case CELLTYPE_VALUE :
|
|
nVal = GetValueCellValue(aAdr, aCell.mfValue);
|
|
break;
|
|
case CELLTYPE_FORMULA :
|
|
if (aCell.mpFormula->IsValue())
|
|
{
|
|
nErr = aCell.mpFormula->GetErrCode();
|
|
nVal = aCell.mpFormula->GetValue();
|
|
}
|
|
else
|
|
bOk = false;
|
|
break;
|
|
default :
|
|
bOk = false;
|
|
break;
|
|
}
|
|
if (bOk)
|
|
{
|
|
if ((nPos + (4 * sizeof(sal_uInt16)) + sizeof(double)) > MAXARRSIZE)
|
|
return false;
|
|
*p++ = static_cast<sal_uInt16>(nCol);
|
|
*p++ = static_cast<sal_uInt16>(nRow);
|
|
*p++ = static_cast<sal_uInt16>(nTab);
|
|
*p++ = nErr;
|
|
memcpy( p, &nVal, sizeof(double));
|
|
nPos += 8 + sizeof(double);
|
|
p = (sal_uInt16*) ( pCellArr + nPos );
|
|
nCount++;
|
|
}
|
|
}
|
|
nCol++;
|
|
}
|
|
nRow++;
|
|
}
|
|
nTab++;
|
|
}
|
|
*pCount = nCount;
|
|
return true;
|
|
}
|
|
|
|
bool ScInterpreter::CreateStringArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
|
|
SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
|
|
sal_uInt8* pCellArr)
|
|
{
|
|
|
|
// Old Add-Ins are hard limited to sal_uInt16 values.
|
|
#if MAXCOLCOUNT_DEFINE > USHRT_MAX
|
|
#error Add check for columns > USHRT_MAX!
|
|
#endif
|
|
if (nRow1 > USHRT_MAX || nRow2 > USHRT_MAX)
|
|
return false;
|
|
|
|
sal_uInt16 nCount = 0;
|
|
sal_uInt16* p = (sal_uInt16*) pCellArr;
|
|
*p++ = static_cast<sal_uInt16>(nCol1);
|
|
*p++ = static_cast<sal_uInt16>(nRow1);
|
|
*p++ = static_cast<sal_uInt16>(nTab1);
|
|
*p++ = static_cast<sal_uInt16>(nCol2);
|
|
*p++ = static_cast<sal_uInt16>(nRow2);
|
|
*p++ = static_cast<sal_uInt16>(nTab2);
|
|
sal_uInt16* pCount = p;
|
|
*p++ = 0;
|
|
sal_uInt16 nPos = 14;
|
|
SCTAB nTab = nTab1;
|
|
while (nTab <= nTab2)
|
|
{
|
|
SCROW nRow = nRow1;
|
|
while (nRow <= nRow2)
|
|
{
|
|
SCCOL nCol = nCol1;
|
|
while (nCol <= nCol2)
|
|
{
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, ScAddress(nCol, nRow, nTab));
|
|
if (!aCell.isEmpty())
|
|
{
|
|
OUString aStr;
|
|
sal_uInt16 nErr = 0;
|
|
bool bOk = true;
|
|
switch (aCell.meType)
|
|
{
|
|
case CELLTYPE_STRING:
|
|
case CELLTYPE_EDIT:
|
|
aStr = aCell.getString(pDok);
|
|
break;
|
|
case CELLTYPE_FORMULA:
|
|
if (!aCell.mpFormula->IsValue())
|
|
{
|
|
nErr = aCell.mpFormula->GetErrCode();
|
|
aStr = aCell.mpFormula->GetString().getString();
|
|
}
|
|
else
|
|
bOk = false;
|
|
break;
|
|
default :
|
|
bOk = false;
|
|
break;
|
|
}
|
|
if (bOk)
|
|
{
|
|
OString aTmp(OUStringToOString(aStr,
|
|
osl_getThreadTextEncoding()));
|
|
// Old Add-Ins are limited to sal_uInt16 string
|
|
// lengths, and room for pad byte check.
|
|
if ( aTmp.getLength() > SAL_MAX_UINT16 - 2 )
|
|
return false;
|
|
// Append a 0-pad-byte if string length is odd
|
|
//! MUST be sal_uInt16
|
|
sal_uInt16 nStrLen = (sal_uInt16) aTmp.getLength();
|
|
sal_uInt16 nLen = ( nStrLen + 2 ) & ~1;
|
|
|
|
if (((sal_uLong)nPos + (5 * sizeof(sal_uInt16)) + nLen) > MAXARRSIZE)
|
|
return false;
|
|
*p++ = static_cast<sal_uInt16>(nCol);
|
|
*p++ = static_cast<sal_uInt16>(nRow);
|
|
*p++ = static_cast<sal_uInt16>(nTab);
|
|
*p++ = nErr;
|
|
*p++ = nLen;
|
|
memcpy( p, aTmp.getStr(), nStrLen + 1);
|
|
nPos += 10 + nStrLen + 1;
|
|
sal_uInt8* q = ( pCellArr + nPos );
|
|
if( (nStrLen & 1) == 0 )
|
|
*q++ = 0, nPos++;
|
|
p = (sal_uInt16*) ( pCellArr + nPos );
|
|
nCount++;
|
|
}
|
|
}
|
|
nCol++;
|
|
}
|
|
nRow++;
|
|
}
|
|
nTab++;
|
|
}
|
|
*pCount = nCount;
|
|
return true;
|
|
}
|
|
|
|
bool ScInterpreter::CreateCellArr(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
|
|
SCCOL nCol2, SCROW nRow2, SCTAB nTab2,
|
|
sal_uInt8* pCellArr)
|
|
{
|
|
|
|
// Old Add-Ins are hard limited to sal_uInt16 values.
|
|
#if MAXCOLCOUNT_DEFINE > USHRT_MAX
|
|
#error Add check for columns > USHRT_MAX!
|
|
#endif
|
|
if (nRow1 > USHRT_MAX || nRow2 > USHRT_MAX)
|
|
return false;
|
|
|
|
sal_uInt16 nCount = 0;
|
|
sal_uInt16* p = (sal_uInt16*) pCellArr;
|
|
*p++ = static_cast<sal_uInt16>(nCol1);
|
|
*p++ = static_cast<sal_uInt16>(nRow1);
|
|
*p++ = static_cast<sal_uInt16>(nTab1);
|
|
*p++ = static_cast<sal_uInt16>(nCol2);
|
|
*p++ = static_cast<sal_uInt16>(nRow2);
|
|
*p++ = static_cast<sal_uInt16>(nTab2);
|
|
sal_uInt16* pCount = p;
|
|
*p++ = 0;
|
|
sal_uInt16 nPos = 14;
|
|
SCTAB nTab = nTab1;
|
|
ScAddress aAdr;
|
|
while (nTab <= nTab2)
|
|
{
|
|
aAdr.SetTab( nTab );
|
|
SCROW nRow = nRow1;
|
|
while (nRow <= nRow2)
|
|
{
|
|
aAdr.SetRow( nRow );
|
|
SCCOL nCol = nCol1;
|
|
while (nCol <= nCol2)
|
|
{
|
|
aAdr.SetCol( nCol );
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
if (!aCell.isEmpty())
|
|
{
|
|
sal_uInt16 nErr = 0;
|
|
sal_uInt16 nType = 0; // 0 = Zahl; 1 = String
|
|
double nVal = 0.0;
|
|
OUString aStr;
|
|
bool bOk = true;
|
|
switch (aCell.meType)
|
|
{
|
|
case CELLTYPE_STRING :
|
|
case CELLTYPE_EDIT :
|
|
aStr = aCell.getString(pDok);
|
|
nType = 1;
|
|
break;
|
|
case CELLTYPE_VALUE :
|
|
nVal = GetValueCellValue(aAdr, aCell.mfValue);
|
|
break;
|
|
case CELLTYPE_FORMULA :
|
|
nErr = aCell.mpFormula->GetErrCode();
|
|
if (aCell.mpFormula->IsValue())
|
|
nVal = aCell.mpFormula->GetValue();
|
|
else
|
|
aStr = aCell.mpFormula->GetString().getString();
|
|
break;
|
|
default :
|
|
bOk = false;
|
|
break;
|
|
}
|
|
if (bOk)
|
|
{
|
|
if ((nPos + (5 * sizeof(sal_uInt16))) > MAXARRSIZE)
|
|
return false;
|
|
*p++ = static_cast<sal_uInt16>(nCol);
|
|
*p++ = static_cast<sal_uInt16>(nRow);
|
|
*p++ = static_cast<sal_uInt16>(nTab);
|
|
*p++ = nErr;
|
|
*p++ = nType;
|
|
nPos += 10;
|
|
if (nType == 0)
|
|
{
|
|
if ((nPos + sizeof(double)) > MAXARRSIZE)
|
|
return false;
|
|
memcpy( p, &nVal, sizeof(double));
|
|
nPos += sizeof(double);
|
|
}
|
|
else
|
|
{
|
|
OString aTmp(OUStringToOString(aStr,
|
|
osl_getThreadTextEncoding()));
|
|
// Old Add-Ins are limited to sal_uInt16 string
|
|
// lengths, and room for pad byte check.
|
|
if ( aTmp.getLength() > SAL_MAX_UINT16 - 2 )
|
|
return false;
|
|
// Append a 0-pad-byte if string length is odd
|
|
//! MUST be sal_uInt16
|
|
sal_uInt16 nStrLen = (sal_uInt16) aTmp.getLength();
|
|
sal_uInt16 nLen = ( nStrLen + 2 ) & ~1;
|
|
if ( ((sal_uLong)nPos + 2 + nLen) > MAXARRSIZE)
|
|
return false;
|
|
*p++ = nLen;
|
|
memcpy( p, aTmp.getStr(), nStrLen + 1);
|
|
nPos += 2 + nStrLen + 1;
|
|
sal_uInt8* q = ( pCellArr + nPos );
|
|
if( (nStrLen & 1) == 0 )
|
|
*q++ = 0, nPos++;
|
|
}
|
|
nCount++;
|
|
p = (sal_uInt16*) ( pCellArr + nPos );
|
|
}
|
|
}
|
|
nCol++;
|
|
}
|
|
nRow++;
|
|
}
|
|
nTab++;
|
|
}
|
|
*pCount = nCount;
|
|
return true;
|
|
}
|
|
|
|
// Stack operations
|
|
|
|
// Also releases a TempToken if appropriate.
|
|
|
|
void ScInterpreter::PushWithoutError( FormulaToken& r )
|
|
{
|
|
if ( sp >= MAXSTACK )
|
|
SetError( errStackOverflow );
|
|
else
|
|
{
|
|
nCurFmtType = NUMBERFORMAT_UNDEFINED;
|
|
r.IncRef();
|
|
if( sp >= maxsp )
|
|
maxsp = sp + 1;
|
|
else
|
|
pStack[ sp ]->DecRef();
|
|
pStack[ sp ] = (ScToken*) &r;
|
|
++sp;
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::Push( FormulaToken& r )
|
|
{
|
|
if ( sp >= MAXSTACK )
|
|
SetError( errStackOverflow );
|
|
else
|
|
{
|
|
if (nGlobalError)
|
|
{
|
|
if (r.GetType() == svError)
|
|
{
|
|
r.SetError( nGlobalError);
|
|
PushWithoutError( r);
|
|
}
|
|
else
|
|
PushWithoutError( *(new FormulaErrorToken( nGlobalError)));
|
|
}
|
|
else
|
|
PushWithoutError( r);
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::PushTempToken( FormulaToken* p )
|
|
{
|
|
if ( sp >= MAXSTACK )
|
|
{
|
|
SetError( errStackOverflow );
|
|
if (!p->GetRef())
|
|
//! p is a dangling pointer hereafter!
|
|
p->Delete();
|
|
}
|
|
else
|
|
{
|
|
if (nGlobalError)
|
|
{
|
|
if (p->GetType() == svError)
|
|
{
|
|
p->SetError( nGlobalError);
|
|
PushTempTokenWithoutError( p);
|
|
}
|
|
else
|
|
{
|
|
if (!p->GetRef())
|
|
//! p is a dangling pointer hereafter!
|
|
p->Delete();
|
|
PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
|
|
}
|
|
}
|
|
else
|
|
PushTempTokenWithoutError( p);
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::PushTempTokenWithoutError( FormulaToken* p )
|
|
{
|
|
p->IncRef();
|
|
if ( sp >= MAXSTACK )
|
|
{
|
|
SetError( errStackOverflow );
|
|
//! p may be a dangling pointer hereafter!
|
|
p->DecRef();
|
|
}
|
|
else
|
|
{
|
|
if( sp >= maxsp )
|
|
maxsp = sp + 1;
|
|
else
|
|
pStack[ sp ]->DecRef();
|
|
pStack[ sp ] = p;
|
|
++sp;
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::PushTempToken( const FormulaToken& r )
|
|
{
|
|
if (!IfErrorPushError())
|
|
PushTempTokenWithoutError( r.Clone());
|
|
}
|
|
|
|
void ScInterpreter::PushCellResultToken( bool bDisplayEmptyAsString,
|
|
const ScAddress & rAddress, short * pRetTypeExpr, sal_uLong * pRetIndexExpr )
|
|
{
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, rAddress);
|
|
if (aCell.hasEmptyValue())
|
|
{
|
|
bool bInherited = (aCell.meType == CELLTYPE_FORMULA);
|
|
if (pRetTypeExpr && pRetIndexExpr)
|
|
pDok->GetNumberFormatInfo(*pRetTypeExpr, *pRetIndexExpr, rAddress);
|
|
PushTempToken( new ScEmptyCellToken( bInherited, bDisplayEmptyAsString));
|
|
return;
|
|
}
|
|
|
|
sal_uInt16 nErr = 0;
|
|
if (aCell.meType == CELLTYPE_FORMULA)
|
|
nErr = aCell.mpFormula->GetErrCode();
|
|
|
|
if (nErr)
|
|
{
|
|
PushError( nErr);
|
|
if (pRetTypeExpr)
|
|
*pRetTypeExpr = NUMBERFORMAT_UNDEFINED;
|
|
if (pRetIndexExpr)
|
|
*pRetIndexExpr = 0;
|
|
}
|
|
else if (aCell.hasString())
|
|
{
|
|
svl::SharedString aRes;
|
|
GetCellString( aRes, aCell);
|
|
PushString( aRes);
|
|
if (pRetTypeExpr)
|
|
*pRetTypeExpr = NUMBERFORMAT_TEXT;
|
|
if (pRetIndexExpr)
|
|
*pRetIndexExpr = 0;
|
|
}
|
|
else
|
|
{
|
|
double fVal = GetCellValue(rAddress, aCell);
|
|
PushDouble( fVal);
|
|
if (pRetTypeExpr)
|
|
*pRetTypeExpr = nCurFmtType;
|
|
if (pRetIndexExpr)
|
|
*pRetIndexExpr = nCurFmtIndex;
|
|
}
|
|
}
|
|
|
|
// Simply throw away TOS.
|
|
|
|
void ScInterpreter::Pop()
|
|
{
|
|
if( sp )
|
|
sp--;
|
|
else
|
|
SetError(errUnknownStackVariable);
|
|
}
|
|
|
|
// Simply throw away TOS and set error code, used with ocIsError et al.
|
|
|
|
void ScInterpreter::PopError()
|
|
{
|
|
if( sp )
|
|
{
|
|
sp--;
|
|
if (pStack[sp]->GetType() == svError)
|
|
nGlobalError = pStack[sp]->GetError();
|
|
}
|
|
else
|
|
SetError(errUnknownStackVariable);
|
|
}
|
|
|
|
FormulaTokenRef ScInterpreter::PopToken()
|
|
{
|
|
if (sp)
|
|
{
|
|
sp--;
|
|
FormulaToken* p = pStack[ sp ];
|
|
if (p->GetType() == svError)
|
|
nGlobalError = p->GetError();
|
|
return p;
|
|
}
|
|
else
|
|
SetError(errUnknownStackVariable);
|
|
return NULL;
|
|
}
|
|
|
|
double ScInterpreter::PopDouble()
|
|
{
|
|
nCurFmtType = NUMBERFORMAT_NUMBER;
|
|
nCurFmtIndex = 0;
|
|
if( sp )
|
|
{
|
|
--sp;
|
|
FormulaToken* p = pStack[ sp ];
|
|
switch (p->GetType())
|
|
{
|
|
case svError:
|
|
nGlobalError = p->GetError();
|
|
break;
|
|
case svDouble:
|
|
return p->GetDouble();
|
|
case svEmptyCell:
|
|
case svMissing:
|
|
return 0.0;
|
|
default:
|
|
SetError( errIllegalArgument);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
return 0.0;
|
|
}
|
|
|
|
svl::SharedString ScInterpreter::PopString()
|
|
{
|
|
nCurFmtType = NUMBERFORMAT_TEXT;
|
|
nCurFmtIndex = 0;
|
|
if( sp )
|
|
{
|
|
--sp;
|
|
FormulaToken* p = pStack[ sp ];
|
|
switch (p->GetType())
|
|
{
|
|
case svError:
|
|
nGlobalError = p->GetError();
|
|
break;
|
|
case svString:
|
|
return p->GetString();
|
|
case svEmptyCell:
|
|
case svMissing:
|
|
return svl::SharedString::getEmptyString();
|
|
default:
|
|
SetError( errIllegalArgument);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
|
|
return svl::SharedString::getEmptyString();
|
|
}
|
|
|
|
void ScInterpreter::ValidateRef( const ScSingleRefData & rRef )
|
|
{
|
|
SCCOL nCol;
|
|
SCROW nRow;
|
|
SCTAB nTab;
|
|
SingleRefToVars( rRef, nCol, nRow, nTab);
|
|
}
|
|
|
|
void ScInterpreter::ValidateRef( const ScComplexRefData & rRef )
|
|
{
|
|
ValidateRef( rRef.Ref1);
|
|
ValidateRef( rRef.Ref2);
|
|
}
|
|
|
|
void ScInterpreter::ValidateRef( const ScRefList & rRefList )
|
|
{
|
|
ScRefList::const_iterator it( rRefList.begin());
|
|
ScRefList::const_iterator end( rRefList.end());
|
|
for ( ; it != end; ++it)
|
|
{
|
|
ValidateRef( *it);
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::SingleRefToVars( const ScSingleRefData & rRef,
|
|
SCCOL & rCol, SCROW & rRow, SCTAB & rTab )
|
|
{
|
|
if ( rRef.IsColRel() )
|
|
rCol = aPos.Col() + rRef.Col();
|
|
else
|
|
rCol = rRef.Col();
|
|
|
|
if ( rRef.IsRowRel() )
|
|
rRow = aPos.Row() + rRef.Row();
|
|
else
|
|
rRow = rRef.Row();
|
|
|
|
if ( rRef.IsTabRel() )
|
|
rTab = aPos.Tab() + rRef.Tab();
|
|
else
|
|
rTab = rRef.Tab();
|
|
|
|
if( !ValidCol( rCol) || rRef.IsColDeleted() )
|
|
SetError( errNoRef ), rCol = 0;
|
|
if( !ValidRow( rRow) || rRef.IsRowDeleted() )
|
|
SetError( errNoRef ), rRow = 0;
|
|
if( !ValidTab( rTab, pDok->GetTableCount() - 1) || rRef.IsTabDeleted() )
|
|
SetError( errNoRef ), rTab = 0;
|
|
}
|
|
|
|
void ScInterpreter::PopSingleRef(SCCOL& rCol, SCROW &rRow, SCTAB& rTab)
|
|
{
|
|
if( sp )
|
|
{
|
|
--sp;
|
|
FormulaToken* p = pStack[ sp ];
|
|
switch (p->GetType())
|
|
{
|
|
case svError:
|
|
nGlobalError = p->GetError();
|
|
break;
|
|
case svSingleRef:
|
|
SingleRefToVars( static_cast<ScToken*>(p)->GetSingleRef(), rCol, rRow, rTab);
|
|
if ( !pDok->aTableOpList.empty() )
|
|
ReplaceCell( rCol, rRow, rTab );
|
|
break;
|
|
default:
|
|
SetError( errIllegalParameter);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
}
|
|
|
|
void ScInterpreter::PopSingleRef( ScAddress& rAdr )
|
|
{
|
|
if( sp )
|
|
{
|
|
--sp;
|
|
FormulaToken* p = pStack[ sp ];
|
|
switch (p->GetType())
|
|
{
|
|
case svError:
|
|
nGlobalError = p->GetError();
|
|
break;
|
|
case svSingleRef:
|
|
{
|
|
SCCOL nCol;
|
|
SCROW nRow;
|
|
SCTAB nTab;
|
|
SingleRefToVars( static_cast<ScToken*>(p)->GetSingleRef(), nCol, nRow, nTab);
|
|
rAdr.Set( nCol, nRow, nTab );
|
|
if ( !pDok->aTableOpList.empty() )
|
|
ReplaceCell( rAdr );
|
|
}
|
|
break;
|
|
default:
|
|
SetError( errIllegalParameter);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
}
|
|
|
|
void ScInterpreter::DoubleRefToVars( const ScToken* p,
|
|
SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1,
|
|
SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2,
|
|
bool bDontCheckForTableOp )
|
|
{
|
|
const ScComplexRefData& rCRef = p->GetDoubleRef();
|
|
SingleRefToVars( rCRef.Ref1, rCol1, rRow1, rTab1);
|
|
SingleRefToVars( rCRef.Ref2, rCol2, rRow2, rTab2);
|
|
if ( !pDok->aTableOpList.empty() && !bDontCheckForTableOp )
|
|
{
|
|
ScRange aRange( rCol1, rRow1, rTab1, rCol2, rRow2, rTab2 );
|
|
if ( IsTableOpInRange( aRange ) )
|
|
SetError( errIllegalParameter );
|
|
}
|
|
}
|
|
|
|
ScDBRangeBase* ScInterpreter::PopDBDoubleRef()
|
|
{
|
|
StackVar eType = GetStackType();
|
|
switch (eType)
|
|
{
|
|
case svUnknown:
|
|
SetError(errUnknownStackVariable);
|
|
break;
|
|
case svError:
|
|
PopError();
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
SCCOL nCol1, nCol2;
|
|
SCROW nRow1, nRow2;
|
|
SCTAB nTab1, nTab2;
|
|
PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, false);
|
|
if (nGlobalError)
|
|
break;
|
|
return new ScDBInternalRange(pDok,
|
|
ScRange(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2));
|
|
}
|
|
case svMatrix:
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScMatrixRef pMat;
|
|
if (eType == svMatrix)
|
|
pMat = PopMatrix();
|
|
else
|
|
PopExternalDoubleRef(pMat);
|
|
if (nGlobalError)
|
|
break;
|
|
return new ScDBExternalRange(pDok, pMat);
|
|
}
|
|
default:
|
|
SetError( errIllegalParameter);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
void ScInterpreter::PopDoubleRef(SCCOL& rCol1, SCROW &rRow1, SCTAB& rTab1,
|
|
SCCOL& rCol2, SCROW &rRow2, SCTAB& rTab2,
|
|
bool bDontCheckForTableOp )
|
|
{
|
|
if( sp )
|
|
{
|
|
--sp;
|
|
FormulaToken* p = pStack[ sp ];
|
|
switch (p->GetType())
|
|
{
|
|
case svError:
|
|
nGlobalError = p->GetError();
|
|
break;
|
|
case svDoubleRef:
|
|
DoubleRefToVars( static_cast<ScToken*>(p), rCol1, rRow1, rTab1, rCol2, rRow2, rTab2,
|
|
bDontCheckForTableOp);
|
|
break;
|
|
default:
|
|
SetError( errIllegalParameter);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
}
|
|
|
|
void ScInterpreter::DoubleRefToRange( const ScComplexRefData & rCRef,
|
|
ScRange & rRange, bool bDontCheckForTableOp )
|
|
{
|
|
SCCOL nCol;
|
|
SCROW nRow;
|
|
SCTAB nTab;
|
|
SingleRefToVars( rCRef.Ref1, nCol, nRow, nTab);
|
|
rRange.aStart.Set( nCol, nRow, nTab );
|
|
SingleRefToVars( rCRef.Ref2, nCol, nRow, nTab);
|
|
rRange.aEnd.Set( nCol, nRow, nTab );
|
|
if (! pDok->aTableOpList.empty() && !bDontCheckForTableOp )
|
|
{
|
|
if ( IsTableOpInRange( rRange ) )
|
|
SetError( errIllegalParameter );
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::PopDoubleRef( ScRange & rRange, short & rParam, size_t & rRefInList )
|
|
{
|
|
if (sp)
|
|
{
|
|
formula::FormulaToken* pToken = pStack[ sp-1 ];
|
|
ScToken* p = static_cast<ScToken*>(pToken);
|
|
switch (pToken->GetType())
|
|
{
|
|
case svError:
|
|
nGlobalError = p->GetError();
|
|
break;
|
|
case svDoubleRef:
|
|
--sp;
|
|
DoubleRefToRange( p->GetDoubleRef(), rRange);
|
|
break;
|
|
case svRefList:
|
|
{
|
|
const ScRefList* pList = p->GetRefList();
|
|
if (rRefInList < pList->size())
|
|
{
|
|
DoubleRefToRange( (*pList)[rRefInList], rRange);
|
|
if (++rRefInList < pList->size())
|
|
++rParam;
|
|
else
|
|
{
|
|
--sp;
|
|
rRefInList = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
--sp;
|
|
rRefInList = 0;
|
|
SetError( errIllegalParameter);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
SetError( errIllegalParameter);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
}
|
|
|
|
void ScInterpreter::PopDoubleRef( ScRange& rRange, bool bDontCheckForTableOp )
|
|
{
|
|
if( sp )
|
|
{
|
|
--sp;
|
|
FormulaToken* p = pStack[ sp ];
|
|
switch (p->GetType())
|
|
{
|
|
case svError:
|
|
nGlobalError = p->GetError();
|
|
break;
|
|
case svDoubleRef:
|
|
DoubleRefToRange( static_cast<ScToken*>(p)->GetDoubleRef(), rRange, bDontCheckForTableOp);
|
|
break;
|
|
default:
|
|
SetError( errIllegalParameter);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
}
|
|
|
|
void ScInterpreter::PopExternalSingleRef(sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef)
|
|
{
|
|
if (!sp)
|
|
{
|
|
SetError(errUnknownStackVariable);
|
|
return;
|
|
}
|
|
|
|
--sp;
|
|
FormulaToken* p = pStack[sp];
|
|
StackVar eType = p->GetType();
|
|
|
|
if (eType == svError)
|
|
{
|
|
nGlobalError = p->GetError();
|
|
return;
|
|
}
|
|
|
|
if (eType != svExternalSingleRef)
|
|
{
|
|
SetError( errIllegalParameter);
|
|
return;
|
|
}
|
|
|
|
rFileId = p->GetIndex();
|
|
rTabName = p->GetString().getString();
|
|
rRef = static_cast<ScToken*>(p)->GetSingleRef();
|
|
}
|
|
|
|
void ScInterpreter::PopExternalSingleRef(ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt)
|
|
{
|
|
sal_uInt16 nFileId;
|
|
OUString aTabName;
|
|
ScSingleRefData aData;
|
|
PopExternalSingleRef(nFileId, aTabName, aData, rToken, pFmt);
|
|
}
|
|
|
|
void ScInterpreter::PopExternalSingleRef(
|
|
sal_uInt16& rFileId, OUString& rTabName, ScSingleRefData& rRef,
|
|
ScExternalRefCache::TokenRef& rToken, ScExternalRefCache::CellFormat* pFmt)
|
|
{
|
|
PopExternalSingleRef(rFileId, rTabName, rRef);
|
|
if (nGlobalError)
|
|
return;
|
|
|
|
ScExternalRefManager* pRefMgr = pDok->GetExternalRefManager();
|
|
const OUString* pFile = pRefMgr->getExternalFileName(rFileId);
|
|
if (!pFile)
|
|
{
|
|
SetError(errNoName);
|
|
return;
|
|
}
|
|
|
|
if (rRef.IsTabRel())
|
|
{
|
|
OSL_FAIL("ScCompiler::GetToken: external single reference must have an absolute table reference!");
|
|
SetError(errNoRef);
|
|
return;
|
|
}
|
|
|
|
ScAddress aAddr = rRef.toAbs(aPos);
|
|
ScExternalRefCache::CellFormat aFmt;
|
|
ScExternalRefCache::TokenRef xNew = pRefMgr->getSingleRefToken(
|
|
rFileId, rTabName, aAddr, &aPos, NULL, &aFmt);
|
|
|
|
if (!xNew)
|
|
{
|
|
SetError(errNoRef);
|
|
return;
|
|
}
|
|
|
|
rToken = xNew;
|
|
if (pFmt)
|
|
*pFmt = aFmt;
|
|
}
|
|
|
|
void ScInterpreter::PopExternalDoubleRef(sal_uInt16& rFileId, OUString& rTabName, ScComplexRefData& rRef)
|
|
{
|
|
if (!sp)
|
|
{
|
|
SetError(errUnknownStackVariable);
|
|
return;
|
|
}
|
|
|
|
--sp;
|
|
FormulaToken* p = pStack[sp];
|
|
StackVar eType = p->GetType();
|
|
|
|
if (eType == svError)
|
|
{
|
|
nGlobalError = p->GetError();
|
|
return;
|
|
}
|
|
|
|
if (eType != svExternalDoubleRef)
|
|
{
|
|
SetError( errIllegalParameter);
|
|
return;
|
|
}
|
|
|
|
rFileId = p->GetIndex();
|
|
rTabName = p->GetString().getString();
|
|
rRef = static_cast<ScToken*>(p)->GetDoubleRef();
|
|
}
|
|
|
|
void ScInterpreter::PopExternalDoubleRef(ScExternalRefCache::TokenArrayRef& rArray)
|
|
{
|
|
sal_uInt16 nFileId;
|
|
OUString aTabName;
|
|
ScComplexRefData aData;
|
|
PopExternalDoubleRef(nFileId, aTabName, aData);
|
|
if (nGlobalError)
|
|
return;
|
|
|
|
GetExternalDoubleRef(nFileId, aTabName, aData, rArray);
|
|
if (nGlobalError)
|
|
return;
|
|
}
|
|
|
|
void ScInterpreter::PopExternalDoubleRef(ScMatrixRef& rMat)
|
|
{
|
|
ScExternalRefCache::TokenArrayRef pArray;
|
|
PopExternalDoubleRef(pArray);
|
|
if (nGlobalError)
|
|
return;
|
|
|
|
// For now, we only support single range data for external
|
|
// references, which means the array should only contain a
|
|
// single matrix token.
|
|
ScToken* p = static_cast<ScToken*>(pArray->First());
|
|
if (!p || p->GetType() != svMatrix)
|
|
SetError( errIllegalParameter);
|
|
else
|
|
{
|
|
rMat = p->GetMatrix();
|
|
if (!rMat)
|
|
SetError( errUnknownVariable);
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::GetExternalDoubleRef(
|
|
sal_uInt16 nFileId, const OUString& rTabName, const ScComplexRefData& rData, ScExternalRefCache::TokenArrayRef& rArray)
|
|
{
|
|
ScExternalRefManager* pRefMgr = pDok->GetExternalRefManager();
|
|
const OUString* pFile = pRefMgr->getExternalFileName(nFileId);
|
|
if (!pFile)
|
|
{
|
|
SetError(errNoName);
|
|
return;
|
|
}
|
|
if (rData.Ref1.IsTabRel() || rData.Ref2.IsTabRel())
|
|
{
|
|
OSL_FAIL("ScCompiler::GetToken: external double reference must have an absolute table reference!");
|
|
SetError(errNoRef);
|
|
return;
|
|
}
|
|
|
|
ScComplexRefData aData(rData);
|
|
ScRange aRange = aData.toAbs(aPos);
|
|
ScExternalRefCache::TokenArrayRef pArray = pRefMgr->getDoubleRefTokens(
|
|
nFileId, rTabName, aRange, &aPos);
|
|
|
|
if (!pArray)
|
|
{
|
|
SetError(errIllegalArgument);
|
|
return;
|
|
}
|
|
|
|
ScToken* pToken = static_cast<ScToken*>(pArray->First());
|
|
if (pToken->GetType() != svMatrix)
|
|
{
|
|
SetError(errIllegalArgument);
|
|
return;
|
|
}
|
|
|
|
if (pArray->Next())
|
|
{
|
|
// Can't handle more than one matrix per parameter.
|
|
SetError( errIllegalArgument);
|
|
return;
|
|
}
|
|
|
|
rArray = pArray;
|
|
}
|
|
|
|
bool ScInterpreter::PopDoubleRefOrSingleRef( ScAddress& rAdr )
|
|
{
|
|
switch ( GetStackType() )
|
|
{
|
|
case svDoubleRef :
|
|
{
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange, true );
|
|
return DoubleRefToPosSingleRef( aRange, rAdr );
|
|
}
|
|
case svSingleRef :
|
|
{
|
|
PopSingleRef( rAdr );
|
|
return true;
|
|
}
|
|
default:
|
|
PopError();
|
|
SetError( errNoRef );
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ScInterpreter::PopDoubleRefPushMatrix()
|
|
{
|
|
if ( GetStackType() == svDoubleRef )
|
|
{
|
|
ScMatrixRef pMat = GetMatrix();
|
|
if ( pMat )
|
|
PushMatrix( pMat );
|
|
else
|
|
PushIllegalParameter();
|
|
}
|
|
else
|
|
SetError( errNoRef );
|
|
}
|
|
|
|
ScTokenMatrixMap* ScInterpreter::CreateTokenMatrixMap()
|
|
{
|
|
return new ScTokenMatrixMap;
|
|
}
|
|
|
|
bool ScInterpreter::ConvertMatrixParameters()
|
|
{
|
|
sal_uInt16 nParams = pCur->GetParamCount();
|
|
OSL_ENSURE( nParams <= sp, "ConvertMatrixParameters: stack/param count mismatch");
|
|
SCSIZE nJumpCols = 0, nJumpRows = 0;
|
|
for ( sal_uInt16 i=1; i <= nParams && i <= sp; ++i )
|
|
{
|
|
FormulaToken* p = pStack[ sp - i ];
|
|
if ( p->GetOpCode() != ocPush && p->GetOpCode() != ocMissing)
|
|
{
|
|
OSL_FAIL( "ConvertMatrixParameters: not a push");
|
|
}
|
|
else
|
|
{
|
|
switch ( p->GetType() )
|
|
{
|
|
case svDouble:
|
|
case svString:
|
|
case svSingleRef:
|
|
case svExternalSingleRef:
|
|
case svMissing:
|
|
case svError:
|
|
case svEmptyCell:
|
|
// nothing to do
|
|
break;
|
|
case svMatrix:
|
|
{
|
|
if ( ScParameterClassification::GetParameterType( pCur, nParams - i)
|
|
== ScParameterClassification::Value )
|
|
{ // only if single value expected
|
|
ScMatrixRef pMat = static_cast<ScToken*>(p)->GetMatrix();
|
|
if ( !pMat )
|
|
SetError( errUnknownVariable);
|
|
else
|
|
{
|
|
SCSIZE nCols, nRows;
|
|
pMat->GetDimensions( nCols, nRows);
|
|
if ( nJumpCols < nCols )
|
|
nJumpCols = nCols;
|
|
if ( nJumpRows < nRows )
|
|
nJumpRows = nRows;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScParameterClassification::Type eType =
|
|
ScParameterClassification::GetParameterType( pCur, nParams - i);
|
|
if ( eType != ScParameterClassification::Reference &&
|
|
eType != ScParameterClassification::ReferenceOrForceArray)
|
|
{
|
|
SCCOL nCol1, nCol2;
|
|
SCROW nRow1, nRow2;
|
|
SCTAB nTab1, nTab2;
|
|
DoubleRefToVars( static_cast<const ScToken*>( p), nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
|
|
// Make sure the map exists, created if not.
|
|
GetTokenMatrixMap();
|
|
ScMatrixRef pMat = CreateMatrixFromDoubleRef( p,
|
|
nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
|
|
if (pMat)
|
|
{
|
|
if ( eType == ScParameterClassification::Value )
|
|
{ // only if single value expected
|
|
if ( nJumpCols < static_cast<SCSIZE>(nCol2 - nCol1 + 1) )
|
|
nJumpCols = static_cast<SCSIZE>(nCol2 - nCol1 + 1);
|
|
if ( nJumpRows < static_cast<SCSIZE>(nRow2 - nRow1 + 1) )
|
|
nJumpRows = static_cast<SCSIZE>(nRow2 - nRow1 + 1);
|
|
}
|
|
ScToken* pNew = new ScMatrixToken( pMat);
|
|
pNew->IncRef();
|
|
pStack[ sp - i ] = pNew;
|
|
p->DecRef(); // p may be dead now!
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScParameterClassification::Type eType =
|
|
ScParameterClassification::GetParameterType( pCur, nParams - i);
|
|
if (eType == ScParameterClassification::Array)
|
|
{
|
|
sal_uInt16 nFileId = p->GetIndex();
|
|
OUString aTabName = p->GetString().getString();
|
|
const ScComplexRefData& rRef = static_cast<ScToken*>(p)->GetDoubleRef();
|
|
ScExternalRefCache::TokenArrayRef pArray;
|
|
GetExternalDoubleRef(nFileId, aTabName, rRef, pArray);
|
|
if (nGlobalError)
|
|
break;
|
|
|
|
ScToken* pTemp = static_cast<ScToken*>(pArray->First());
|
|
if (!pTemp)
|
|
break;
|
|
|
|
ScMatrixRef pMat = pTemp->GetMatrix();
|
|
if (pMat)
|
|
{
|
|
ScToken* pNew = new ScMatrixToken( pMat);
|
|
pNew->IncRef();
|
|
pStack[ sp - i ] = pNew;
|
|
p->DecRef(); // p may be dead now!
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svRefList:
|
|
{
|
|
ScParameterClassification::Type eType =
|
|
ScParameterClassification::GetParameterType( pCur, nParams - i);
|
|
if ( eType != ScParameterClassification::Reference &&
|
|
eType != ScParameterClassification::ReferenceOrForceArray)
|
|
{
|
|
// can't convert to matrix
|
|
SetError( errNoValue);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
OSL_FAIL( "ConvertMatrixParameters: unknown parameter type");
|
|
}
|
|
}
|
|
}
|
|
if( nJumpCols && nJumpRows )
|
|
{
|
|
short nPC = aCode.GetPC();
|
|
short nStart = nPC - 1; // restart on current code (-1)
|
|
short nNext = nPC; // next instruction after subroutine
|
|
short nStop = nPC + 1; // stop subroutine before reaching that
|
|
FormulaTokenRef xNew;
|
|
ScTokenMatrixMap::const_iterator aMapIter;
|
|
if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) !=
|
|
pTokenMatrixMap->end()))
|
|
xNew = (*aMapIter).second;
|
|
else
|
|
{
|
|
ScJumpMatrix* pJumpMat = new ScJumpMatrix( nJumpCols, nJumpRows);
|
|
pJumpMat->SetAllJumps( 1.0, nStart, nNext, nStop);
|
|
// pop parameters and store in ScJumpMatrix, push in JumpMatrix()
|
|
ScTokenVec* pParams = new ScTokenVec( nParams);
|
|
for ( sal_uInt16 i=1; i <= nParams && sp > 0; ++i )
|
|
{
|
|
FormulaToken* p = pStack[ --sp ];
|
|
p->IncRef();
|
|
// store in reverse order such that a push may simply iterate
|
|
(*pParams)[ nParams - i ] = p;
|
|
}
|
|
pJumpMat->SetJumpParameters( pParams);
|
|
xNew = new ScJumpMatrixToken( pJumpMat );
|
|
GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type( pCur,
|
|
xNew));
|
|
}
|
|
PushTempToken( xNew.get());
|
|
// set continuation point of path for main code line
|
|
aCode.Jump( nNext, nNext);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ScMatrixRef ScInterpreter::PopMatrix()
|
|
{
|
|
if( sp )
|
|
{
|
|
--sp;
|
|
FormulaToken* p = pStack[ sp ];
|
|
switch (p->GetType())
|
|
{
|
|
case svError:
|
|
nGlobalError = p->GetError();
|
|
break;
|
|
case svMatrix:
|
|
{
|
|
ScMatrix* pMat = static_cast<ScToken*>(p)->GetMatrix();
|
|
if ( pMat )
|
|
pMat->SetErrorInterpreter( this);
|
|
else
|
|
SetError( errUnknownVariable);
|
|
return pMat;
|
|
}
|
|
default:
|
|
SetError( errIllegalParameter);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
return NULL;
|
|
}
|
|
|
|
sc::RangeMatrix ScInterpreter::PopRangeMatrix()
|
|
{
|
|
sc::RangeMatrix aRet;
|
|
if (sp)
|
|
{
|
|
switch (pStack[sp-1]->GetType())
|
|
{
|
|
case svMatrix:
|
|
{
|
|
--sp;
|
|
FormulaToken* p = pStack[sp];
|
|
ScToken* p2 = static_cast<ScToken*>(p);
|
|
aRet.mpMat = p2->GetMatrix();
|
|
if (aRet.mpMat)
|
|
{
|
|
aRet.mpMat->SetErrorInterpreter(this);
|
|
if (p2->GetByte() == MATRIX_TOKEN_HAS_RANGE)
|
|
{
|
|
const ScComplexRefData& rRef = p2->GetDoubleRef();
|
|
if (!rRef.Ref1.IsColRel() && !rRef.Ref1.IsRowRel() && !rRef.Ref2.IsColRel() && !rRef.Ref2.IsRowRel())
|
|
{
|
|
aRet.mnCol1 = rRef.Ref1.Col();
|
|
aRet.mnRow1 = rRef.Ref1.Row();
|
|
aRet.mnTab1 = rRef.Ref1.Tab();
|
|
aRet.mnCol2 = rRef.Ref2.Col();
|
|
aRet.mnRow2 = rRef.Ref2.Row();
|
|
aRet.mnTab2 = rRef.Ref2.Tab();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownVariable);
|
|
}
|
|
break;
|
|
default:
|
|
aRet.mpMat = PopMatrix();
|
|
}
|
|
}
|
|
return aRet;
|
|
}
|
|
|
|
void ScInterpreter::QueryMatrixType(ScMatrixRef& xMat, short& rRetTypeExpr, sal_uLong& rRetIndexExpr)
|
|
{
|
|
if (xMat)
|
|
{
|
|
SCSIZE nCols, nRows;
|
|
xMat->GetDimensions(nCols, nRows);
|
|
ScMatrixValue nMatVal = xMat->Get(0, 0);
|
|
ScMatValType nMatValType = nMatVal.nType;
|
|
if (ScMatrix::IsNonValueType( nMatValType))
|
|
{
|
|
if ( xMat->IsEmptyPath( 0, 0))
|
|
{ // result of empty FALSE jump path
|
|
FormulaTokenRef xRes = new FormulaDoubleToken( 0.0);
|
|
PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
|
|
rRetTypeExpr = NUMBERFORMAT_LOGICAL;
|
|
}
|
|
else
|
|
{
|
|
svl::SharedString aStr( nMatVal.GetString());
|
|
FormulaTokenRef xRes = new FormulaStringToken( aStr);
|
|
PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
|
|
rRetTypeExpr = NUMBERFORMAT_TEXT;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sal_uInt16 nErr = GetDoubleErrorValue( nMatVal.fVal);
|
|
FormulaTokenRef xRes;
|
|
if (nErr)
|
|
xRes = new FormulaErrorToken( nErr);
|
|
else
|
|
xRes = new FormulaDoubleToken( nMatVal.fVal);
|
|
PushTempToken( new ScMatrixFormulaCellToken(nCols, nRows, xMat, xRes.get()));
|
|
if ( rRetTypeExpr != NUMBERFORMAT_LOGICAL )
|
|
rRetTypeExpr = NUMBERFORMAT_NUMBER;
|
|
}
|
|
rRetIndexExpr = 0;
|
|
xMat->SetErrorInterpreter( NULL);
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
}
|
|
|
|
void ScInterpreter::PushDouble(double nVal)
|
|
{
|
|
TreatDoubleError( nVal );
|
|
if (!IfErrorPushError())
|
|
PushTempTokenWithoutError( new FormulaDoubleToken( nVal ) );
|
|
}
|
|
|
|
void ScInterpreter::PushInt(int nVal)
|
|
{
|
|
if (!IfErrorPushError())
|
|
PushTempTokenWithoutError( new FormulaDoubleToken( nVal ) );
|
|
}
|
|
|
|
void ScInterpreter::PushStringBuffer( const sal_Unicode* pString )
|
|
{
|
|
if ( pString )
|
|
{
|
|
svl::SharedString aSS = pDok->GetSharedStringPool().intern(OUString(pString));
|
|
PushString(aSS);
|
|
}
|
|
else
|
|
PushString(svl::SharedString::getEmptyString());
|
|
}
|
|
|
|
void ScInterpreter::PushString( const OUString& rStr )
|
|
{
|
|
PushString(pDok->GetSharedStringPool().intern(rStr));
|
|
}
|
|
|
|
void ScInterpreter::PushString( const svl::SharedString& rString )
|
|
{
|
|
if (!IfErrorPushError())
|
|
PushTempTokenWithoutError( new FormulaStringToken( rString ) );
|
|
}
|
|
|
|
void ScInterpreter::PushSingleRef(SCCOL nCol, SCROW nRow, SCTAB nTab)
|
|
{
|
|
if (!IfErrorPushError())
|
|
{
|
|
ScSingleRefData aRef;
|
|
aRef.InitAddress(ScAddress(nCol,nRow,nTab));
|
|
PushTempTokenWithoutError( new ScSingleRefToken( aRef ) );
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::PushDoubleRef(SCCOL nCol1, SCROW nRow1, SCTAB nTab1,
|
|
SCCOL nCol2, SCROW nRow2, SCTAB nTab2)
|
|
{
|
|
if (!IfErrorPushError())
|
|
{
|
|
ScComplexRefData aRef;
|
|
aRef.InitRange(ScRange(nCol1,nRow1,nTab1,nCol2,nRow2,nTab2));
|
|
PushTempTokenWithoutError( new ScDoubleRefToken( aRef ) );
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::PushExternalSingleRef(
|
|
sal_uInt16 nFileId, const OUString& rTabName, SCCOL nCol, SCROW nRow, SCTAB nTab)
|
|
{
|
|
if (!IfErrorPushError())
|
|
{
|
|
ScSingleRefData aRef;
|
|
aRef.InitAddress(ScAddress(nCol,nRow,nTab));
|
|
PushTempTokenWithoutError( new ScExternalSingleRefToken(nFileId, rTabName, aRef)) ;
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::PushExternalDoubleRef(
|
|
sal_uInt16 nFileId, const OUString& rTabName,
|
|
SCCOL nCol1, SCROW nRow1, SCTAB nTab1, SCCOL nCol2, SCROW nRow2, SCTAB nTab2)
|
|
{
|
|
if (!IfErrorPushError())
|
|
{
|
|
ScComplexRefData aRef;
|
|
aRef.InitRange(ScRange(nCol1,nRow1,nTab1,nCol2,nRow2,nTab2));
|
|
PushTempTokenWithoutError( new ScExternalDoubleRefToken(nFileId, rTabName, aRef) );
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::PushMatrix( const sc::RangeMatrix& rMat )
|
|
{
|
|
if (!rMat.isRangeValid())
|
|
{
|
|
// Just push the matrix part only.
|
|
PushMatrix(rMat.mpMat);
|
|
return;
|
|
}
|
|
|
|
rMat.mpMat->SetErrorInterpreter(NULL);
|
|
nGlobalError = 0;
|
|
PushTempTokenWithoutError(new ScMatrixRangeToken(rMat));
|
|
}
|
|
|
|
void ScInterpreter::PushMatrix(const ScMatrixRef& pMat)
|
|
{
|
|
pMat->SetErrorInterpreter( NULL);
|
|
// No if (!IfErrorPushError()) because ScMatrix stores errors itself,
|
|
// but with notifying ScInterpreter via nGlobalError, substituting it would
|
|
// mean to inherit the error on all array elements in all following
|
|
// operations.
|
|
nGlobalError = 0;
|
|
PushTempTokenWithoutError( new ScMatrixToken( pMat ) );
|
|
}
|
|
|
|
void ScInterpreter::PushError( sal_uInt16 nError )
|
|
{
|
|
SetError( nError ); // only sets error if not already set
|
|
PushTempTokenWithoutError( new FormulaErrorToken( nGlobalError));
|
|
}
|
|
|
|
void ScInterpreter::PushParameterExpected()
|
|
{
|
|
PushError( errParameterExpected);
|
|
}
|
|
|
|
void ScInterpreter::PushIllegalParameter()
|
|
{
|
|
PushError( errIllegalParameter);
|
|
}
|
|
|
|
void ScInterpreter::PushIllegalArgument()
|
|
{
|
|
PushError( errIllegalArgument);
|
|
}
|
|
|
|
void ScInterpreter::PushNA()
|
|
{
|
|
PushError( NOTAVAILABLE);
|
|
}
|
|
|
|
void ScInterpreter::PushNoValue()
|
|
{
|
|
PushError( errNoValue);
|
|
}
|
|
|
|
bool ScInterpreter::IsMissing()
|
|
{
|
|
return sp && pStack[sp - 1]->GetType() == svMissing;
|
|
}
|
|
|
|
StackVar ScInterpreter::GetRawStackType()
|
|
{
|
|
StackVar eRes;
|
|
if( sp )
|
|
{
|
|
eRes = pStack[sp - 1]->GetType();
|
|
}
|
|
else
|
|
{
|
|
SetError(errUnknownStackVariable);
|
|
eRes = svUnknown;
|
|
}
|
|
return eRes;
|
|
}
|
|
|
|
StackVar ScInterpreter::GetStackType()
|
|
{
|
|
StackVar eRes;
|
|
if( sp )
|
|
{
|
|
eRes = pStack[sp - 1]->GetType();
|
|
if( eRes == svMissing || eRes == svEmptyCell )
|
|
eRes = svDouble; // default!
|
|
}
|
|
else
|
|
{
|
|
SetError(errUnknownStackVariable);
|
|
eRes = svUnknown;
|
|
}
|
|
return eRes;
|
|
}
|
|
|
|
StackVar ScInterpreter::GetStackType( sal_uInt8 nParam )
|
|
{
|
|
StackVar eRes;
|
|
if( sp > nParam-1 )
|
|
{
|
|
eRes = pStack[sp - nParam]->GetType();
|
|
if( eRes == svMissing || eRes == svEmptyCell )
|
|
eRes = svDouble; // default!
|
|
}
|
|
else
|
|
eRes = svUnknown;
|
|
return eRes;
|
|
}
|
|
|
|
bool ScInterpreter::DoubleRefToPosSingleRef( const ScRange& rRange, ScAddress& rAdr )
|
|
{
|
|
// Check for a singleton first - no implicit intersection for them.
|
|
if( rRange.aStart == rRange.aEnd )
|
|
{
|
|
rAdr = rRange.aStart;
|
|
return true;
|
|
}
|
|
|
|
bool bOk = false;
|
|
|
|
if ( pJumpMatrix )
|
|
{
|
|
bOk = rRange.aStart.Tab() == rRange.aEnd.Tab();
|
|
if ( !bOk )
|
|
SetError( errIllegalArgument);
|
|
else
|
|
{
|
|
SCSIZE nC, nR;
|
|
pJumpMatrix->GetPos( nC, nR);
|
|
rAdr.SetCol( sal::static_int_cast<SCCOL>( rRange.aStart.Col() + nC ) );
|
|
rAdr.SetRow( sal::static_int_cast<SCROW>( rRange.aStart.Row() + nR ) );
|
|
rAdr.SetTab( rRange.aStart.Tab());
|
|
bOk = rRange.aStart.Col() <= rAdr.Col() && rAdr.Col() <=
|
|
rRange.aEnd.Col() && rRange.aStart.Row() <= rAdr.Row() &&
|
|
rAdr.Row() <= rRange.aEnd.Row();
|
|
if ( !bOk )
|
|
SetError( errNoValue);
|
|
}
|
|
return bOk;
|
|
}
|
|
|
|
SCCOL nMyCol = aPos.Col();
|
|
SCROW nMyRow = aPos.Row();
|
|
SCTAB nMyTab = aPos.Tab();
|
|
SCCOL nCol = 0;
|
|
SCROW nRow = 0;
|
|
SCTAB nTab;
|
|
nTab = rRange.aStart.Tab();
|
|
if ( rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
|
|
{
|
|
nRow = rRange.aStart.Row();
|
|
if ( nRow == rRange.aEnd.Row() )
|
|
{
|
|
bOk = true;
|
|
nCol = nMyCol;
|
|
}
|
|
else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
|
|
&& rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
|
|
{
|
|
bOk = true;
|
|
nCol = nMyCol;
|
|
nRow = nMyRow;
|
|
}
|
|
}
|
|
else if ( rRange.aStart.Row() <= nMyRow && nMyRow <= rRange.aEnd.Row() )
|
|
{
|
|
nCol = rRange.aStart.Col();
|
|
if ( nCol == rRange.aEnd.Col() )
|
|
{
|
|
bOk = true;
|
|
nRow = nMyRow;
|
|
}
|
|
else if ( nTab != nMyTab && nTab == rRange.aEnd.Tab()
|
|
&& rRange.aStart.Col() <= nMyCol && nMyCol <= rRange.aEnd.Col() )
|
|
{
|
|
bOk = true;
|
|
nCol = nMyCol;
|
|
nRow = nMyRow;
|
|
}
|
|
}
|
|
if ( bOk )
|
|
{
|
|
if ( nTab == rRange.aEnd.Tab() )
|
|
; // all done
|
|
else if ( nTab <= nMyTab && nMyTab <= rRange.aEnd.Tab() )
|
|
nTab = nMyTab;
|
|
else
|
|
bOk = false;
|
|
if ( bOk )
|
|
rAdr.Set( nCol, nRow, nTab );
|
|
}
|
|
if ( !bOk )
|
|
SetError( errNoValue );
|
|
return bOk;
|
|
}
|
|
|
|
double ScInterpreter::GetDoubleFromMatrix(const ScMatrixRef& pMat)
|
|
{
|
|
if (!pMat)
|
|
return 0.0;
|
|
|
|
if ( !pJumpMatrix )
|
|
return pMat->GetDouble( 0 );
|
|
|
|
SCSIZE nCols, nRows, nC, nR;
|
|
pMat->GetDimensions( nCols, nRows);
|
|
pJumpMatrix->GetPos( nC, nR);
|
|
if ( nC < nCols && nR < nRows )
|
|
return pMat->GetDouble( nC, nR);
|
|
|
|
SetError( errNoValue);
|
|
return 0.0;
|
|
}
|
|
|
|
double ScInterpreter::GetDouble()
|
|
{
|
|
double nVal(0.0);
|
|
switch( GetRawStackType() )
|
|
{
|
|
case svDouble:
|
|
nVal = PopDouble();
|
|
break;
|
|
case svString:
|
|
nVal = ConvertStringToValue( PopString().getString());
|
|
break;
|
|
case svSingleRef:
|
|
{
|
|
ScAddress aAdr;
|
|
PopSingleRef( aAdr );
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
nVal = GetCellValue(aAdr, aCell);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{ // generate position dependent SingleRef
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
ScAddress aAdr;
|
|
if ( !nGlobalError && DoubleRefToPosSingleRef( aRange, aAdr ) )
|
|
{
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
nVal = GetCellValue(aAdr, aCell);
|
|
}
|
|
else
|
|
nVal = 0.0;
|
|
}
|
|
break;
|
|
case svExternalSingleRef:
|
|
{
|
|
ScExternalRefCache::TokenRef pToken;
|
|
PopExternalSingleRef(pToken);
|
|
if (!nGlobalError && pToken)
|
|
nVal = pToken->GetDouble();
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScMatrixRef pMat;
|
|
PopExternalDoubleRef(pMat);
|
|
if (nGlobalError)
|
|
break;
|
|
|
|
nVal = GetDoubleFromMatrix(pMat);
|
|
}
|
|
break;
|
|
case svMatrix:
|
|
{
|
|
ScMatrixRef pMat = PopMatrix();
|
|
nVal = GetDoubleFromMatrix(pMat);
|
|
}
|
|
break;
|
|
case svError:
|
|
PopError();
|
|
nVal = 0.0;
|
|
break;
|
|
case svEmptyCell:
|
|
case svMissing:
|
|
Pop();
|
|
nVal = 0.0;
|
|
break;
|
|
default:
|
|
PopError();
|
|
SetError( errIllegalParameter);
|
|
nVal = 0.0;
|
|
}
|
|
if ( nFuncFmtType == nCurFmtType )
|
|
nFuncFmtIndex = nCurFmtIndex;
|
|
return nVal;
|
|
}
|
|
|
|
double ScInterpreter::GetDoubleWithDefault(double nDefault)
|
|
{
|
|
bool bMissing = IsMissing();
|
|
double nResultVal = GetDouble();
|
|
if ( bMissing )
|
|
nResultVal = nDefault;
|
|
return nResultVal;
|
|
}
|
|
|
|
svl::SharedString ScInterpreter::GetString()
|
|
{
|
|
switch (GetRawStackType())
|
|
{
|
|
case svError:
|
|
PopError();
|
|
return svl::SharedString::getEmptyString();
|
|
case svMissing:
|
|
case svEmptyCell:
|
|
Pop();
|
|
return svl::SharedString::getEmptyString();
|
|
case svDouble:
|
|
{
|
|
double fVal = PopDouble();
|
|
sal_uLong nIndex = pFormatter->GetStandardFormat(
|
|
NUMBERFORMAT_NUMBER,
|
|
ScGlobal::eLnge);
|
|
OUString aStr;
|
|
pFormatter->GetInputLineString(fVal, nIndex, aStr);
|
|
return mrStrPool.intern(aStr);
|
|
}
|
|
case svString:
|
|
return PopString();
|
|
case svSingleRef:
|
|
{
|
|
ScAddress aAdr;
|
|
PopSingleRef( aAdr );
|
|
if (nGlobalError == 0)
|
|
{
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
svl::SharedString aSS;
|
|
GetCellString(aSS, aCell);
|
|
return aSS;
|
|
}
|
|
else
|
|
return svl::SharedString::getEmptyString();
|
|
}
|
|
case svDoubleRef:
|
|
{ // generate position dependent SingleRef
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
ScAddress aAdr;
|
|
if ( !nGlobalError && DoubleRefToPosSingleRef( aRange, aAdr ) )
|
|
{
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
svl::SharedString aSS;
|
|
GetCellString(aSS, aCell);
|
|
return aSS;
|
|
}
|
|
else
|
|
return svl::SharedString::getEmptyString();
|
|
}
|
|
case svExternalSingleRef:
|
|
{
|
|
ScExternalRefCache::TokenRef pToken;
|
|
PopExternalSingleRef(pToken);
|
|
if (nGlobalError)
|
|
return svl::SharedString::getEmptyString();
|
|
|
|
return pToken->GetString();
|
|
}
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScMatrixRef pMat;
|
|
PopExternalDoubleRef(pMat);
|
|
return GetStringFromMatrix(pMat);
|
|
}
|
|
case svMatrix:
|
|
{
|
|
ScMatrixRef pMat = PopMatrix();
|
|
return GetStringFromMatrix(pMat);
|
|
}
|
|
break;
|
|
default:
|
|
PopError();
|
|
SetError( errIllegalArgument);
|
|
}
|
|
return svl::SharedString::getEmptyString();
|
|
}
|
|
|
|
svl::SharedString ScInterpreter::GetStringFromMatrix(const ScMatrixRef& pMat)
|
|
{
|
|
if ( !pMat )
|
|
; // nothing
|
|
else if ( !pJumpMatrix )
|
|
{
|
|
return pMat->GetString( *pFormatter, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
SCSIZE nCols, nRows, nC, nR;
|
|
pMat->GetDimensions( nCols, nRows);
|
|
pJumpMatrix->GetPos( nC, nR);
|
|
if ( nC < nCols && nR < nRows )
|
|
{
|
|
return pMat->GetString( *pFormatter, nC, nR);
|
|
}
|
|
else
|
|
SetError( errNoValue);
|
|
}
|
|
return svl::SharedString::getEmptyString();
|
|
}
|
|
|
|
ScMatValType ScInterpreter::GetDoubleOrStringFromMatrix(
|
|
double& rDouble, svl::SharedString& rString )
|
|
{
|
|
|
|
rDouble = 0.0;
|
|
rString = svl::SharedString::getEmptyString();
|
|
ScMatValType nMatValType = SC_MATVAL_EMPTY;
|
|
|
|
ScMatrixRef pMat;
|
|
StackVar eType = GetStackType();
|
|
if (eType == svExternalDoubleRef || eType == svExternalSingleRef || eType == svMatrix)
|
|
{
|
|
pMat = GetMatrix();
|
|
}
|
|
else
|
|
{
|
|
PopError();
|
|
SetError( errIllegalParameter);
|
|
return nMatValType;
|
|
}
|
|
|
|
ScMatrixValue nMatVal;
|
|
if (!pMat)
|
|
{
|
|
// nothing
|
|
}
|
|
else if (!pJumpMatrix)
|
|
{
|
|
nMatVal = pMat->Get(0, 0);
|
|
nMatValType = nMatVal.nType;
|
|
}
|
|
else
|
|
{
|
|
SCSIZE nCols, nRows, nC, nR;
|
|
pMat->GetDimensions( nCols, nRows);
|
|
pJumpMatrix->GetPos( nC, nR);
|
|
if ( nC < nCols && nR < nRows )
|
|
{
|
|
nMatVal = pMat->Get( nC, nR);
|
|
nMatValType = nMatVal.nType;
|
|
}
|
|
else
|
|
SetError( errNoValue);
|
|
}
|
|
|
|
if (nMatValType == SC_MATVAL_VALUE)
|
|
rDouble = nMatVal.fVal;
|
|
else if (nMatValType == SC_MATVAL_BOOLEAN)
|
|
{
|
|
rDouble = nMatVal.fVal;
|
|
nMatValType = SC_MATVAL_VALUE;
|
|
}
|
|
else
|
|
rString = nMatVal.GetString();
|
|
|
|
if (ScMatrix::IsValueType( nMatValType))
|
|
{
|
|
sal_uInt16 nError = nMatVal.GetError();
|
|
if (nError)
|
|
SetError( nError);
|
|
}
|
|
|
|
return nMatValType;
|
|
}
|
|
|
|
void ScInterpreter::ScDBGet()
|
|
{
|
|
bool bMissingField = false;
|
|
SAL_WNODEPRECATED_DECLARATIONS_PUSH
|
|
auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
|
|
SAL_WNODEPRECATED_DECLARATIONS_POP
|
|
if (!pQueryParam.get())
|
|
{
|
|
// Failed to create query param.
|
|
PushIllegalParameter();
|
|
return;
|
|
}
|
|
|
|
pQueryParam->mbSkipString = false;
|
|
ScDBQueryDataIterator aValIter(pDok, pQueryParam.release());
|
|
ScDBQueryDataIterator::Value aValue;
|
|
if (!aValIter.GetFirst(aValue) || aValue.mnError)
|
|
{
|
|
// No match found.
|
|
PushNoValue();
|
|
return;
|
|
}
|
|
|
|
ScDBQueryDataIterator::Value aValNext;
|
|
if (aValIter.GetNext(aValNext) && !aValNext.mnError)
|
|
{
|
|
// There should be only one unique match.
|
|
PushIllegalArgument();
|
|
return;
|
|
}
|
|
|
|
if (aValue.mbIsNumber)
|
|
PushDouble(aValue.mfValue);
|
|
else
|
|
PushString(aValue.maString);
|
|
}
|
|
|
|
void ScInterpreter::ScExternal()
|
|
{
|
|
sal_uInt8 nParamCount = GetByte();
|
|
OUString aUnoName;
|
|
OUString aFuncName( ScGlobal::pCharClass->uppercase( pCur->GetExternal() ) );
|
|
FuncData* pFuncData = ScGlobal::GetFuncCollection()->findByName(aFuncName);
|
|
if (pFuncData)
|
|
{
|
|
// Old binary non-UNO add-in function.
|
|
// NOTE: parameter count is 1-based with the 0th "parameter" being the
|
|
// return value, included in pFuncDatat->GetParamCount()
|
|
if (nParamCount < MAXFUNCPARAM && nParamCount == pFuncData->GetParamCount() - 1)
|
|
{
|
|
ParamType eParamType[MAXFUNCPARAM];
|
|
void* ppParam[MAXFUNCPARAM];
|
|
double nVal[MAXFUNCPARAM];
|
|
sal_Char* pStr[MAXFUNCPARAM];
|
|
sal_uInt8* pCellArr[MAXFUNCPARAM];
|
|
short i;
|
|
|
|
for (i = 0; i < MAXFUNCPARAM; i++)
|
|
{
|
|
eParamType[i] = pFuncData->GetParamType(i);
|
|
ppParam[i] = NULL;
|
|
nVal[i] = 0.0;
|
|
pStr[i] = NULL;
|
|
pCellArr[i] = NULL;
|
|
}
|
|
|
|
for (i = nParamCount; (i > 0) && (nGlobalError == 0); i--)
|
|
{
|
|
switch (eParamType[i])
|
|
{
|
|
case PTR_DOUBLE :
|
|
{
|
|
nVal[i-1] = GetDouble();
|
|
ppParam[i] = &nVal[i-1];
|
|
}
|
|
break;
|
|
case PTR_STRING :
|
|
{
|
|
OString aStr(OUStringToOString(GetString().getString(),
|
|
osl_getThreadTextEncoding()));
|
|
if ( aStr.getLength() >= ADDIN_MAXSTRLEN )
|
|
SetError( errStringOverflow );
|
|
else
|
|
{
|
|
pStr[i-1] = new sal_Char[ADDIN_MAXSTRLEN];
|
|
strncpy( pStr[i-1], aStr.getStr(), ADDIN_MAXSTRLEN );
|
|
pStr[i-1][ADDIN_MAXSTRLEN-1] = 0;
|
|
ppParam[i] = pStr[i-1];
|
|
}
|
|
}
|
|
break;
|
|
case PTR_DOUBLE_ARR :
|
|
{
|
|
SCCOL nCol1;
|
|
SCROW nRow1;
|
|
SCTAB nTab1;
|
|
SCCOL nCol2;
|
|
SCROW nRow2;
|
|
SCTAB nTab2;
|
|
PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
|
|
pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
|
|
if (!CreateDoubleArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
|
|
SetError(errCodeOverflow);
|
|
else
|
|
ppParam[i] = pCellArr[i-1];
|
|
}
|
|
break;
|
|
case PTR_STRING_ARR :
|
|
{
|
|
SCCOL nCol1;
|
|
SCROW nRow1;
|
|
SCTAB nTab1;
|
|
SCCOL nCol2;
|
|
SCROW nRow2;
|
|
SCTAB nTab2;
|
|
PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
|
|
pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
|
|
if (!CreateStringArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
|
|
SetError(errCodeOverflow);
|
|
else
|
|
ppParam[i] = pCellArr[i-1];
|
|
}
|
|
break;
|
|
case PTR_CELL_ARR :
|
|
{
|
|
SCCOL nCol1;
|
|
SCROW nRow1;
|
|
SCTAB nTab1;
|
|
SCCOL nCol2;
|
|
SCROW nRow2;
|
|
SCTAB nTab2;
|
|
PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
|
|
pCellArr[i-1] = new sal_uInt8[MAXARRSIZE];
|
|
if (!CreateCellArr(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, pCellArr[i-1]))
|
|
SetError(errCodeOverflow);
|
|
else
|
|
ppParam[i] = pCellArr[i-1];
|
|
}
|
|
break;
|
|
default :
|
|
SetError(errIllegalParameter);
|
|
break;
|
|
}
|
|
}
|
|
while ( i-- )
|
|
Pop(); // im Fehlerfall (sonst ist i==0) Parameter wegpoppen
|
|
|
|
if (nGlobalError == 0)
|
|
{
|
|
if ( pFuncData->GetAsyncType() == NONE )
|
|
{
|
|
switch ( eParamType[0] )
|
|
{
|
|
case PTR_DOUBLE :
|
|
{
|
|
double nErg = 0.0;
|
|
ppParam[0] = &nErg;
|
|
pFuncData->Call(ppParam);
|
|
PushDouble(nErg);
|
|
}
|
|
break;
|
|
case PTR_STRING :
|
|
{
|
|
boost::scoped_array<sal_Char> pcErg(new sal_Char[ADDIN_MAXSTRLEN]);
|
|
ppParam[0] = pcErg.get();
|
|
pFuncData->Call(ppParam);
|
|
OUString aUni( pcErg.get(), strlen(pcErg.get()), osl_getThreadTextEncoding() );
|
|
PushString( aUni );
|
|
}
|
|
break;
|
|
default:
|
|
PushError( errUnknownState );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// nach dem Laden Asyncs wieder anwerfen
|
|
if ( rArr.IsRecalcModeNormal() )
|
|
rArr.SetExclusiveRecalcModeOnLoad();
|
|
// garantiert identischer Handle bei identischem Aufruf?!?
|
|
// sonst schei*e ...
|
|
double nErg = 0.0;
|
|
ppParam[0] = &nErg;
|
|
pFuncData->Call(ppParam);
|
|
sal_uLong nHandle = sal_uLong( nErg );
|
|
if ( nHandle >= 65536 )
|
|
{
|
|
ScAddInAsync* pAs = ScAddInAsync::Get( nHandle );
|
|
if ( !pAs )
|
|
{
|
|
pAs = new ScAddInAsync(nHandle, pFuncData, pDok);
|
|
pMyFormulaCell->StartListening( *pAs );
|
|
}
|
|
else
|
|
{
|
|
// falls per cut/copy/paste
|
|
pMyFormulaCell->StartListening( *pAs );
|
|
// in anderes Dokument?
|
|
if ( !pAs->HasDocument( pDok ) )
|
|
pAs->AddDocument( pDok );
|
|
}
|
|
if ( pAs->IsValid() )
|
|
{
|
|
switch ( pAs->GetType() )
|
|
{
|
|
case PTR_DOUBLE :
|
|
PushDouble( pAs->GetValue() );
|
|
break;
|
|
case PTR_STRING :
|
|
PushString( pAs->GetString() );
|
|
break;
|
|
default:
|
|
PushError( errUnknownState );
|
|
}
|
|
}
|
|
else
|
|
PushNA();
|
|
}
|
|
else
|
|
PushNoValue();
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < MAXFUNCPARAM; i++)
|
|
{
|
|
delete[] pStr[i];
|
|
delete[] pCellArr[i];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while( nParamCount-- > 0)
|
|
Pop();
|
|
PushIllegalParameter();
|
|
}
|
|
}
|
|
else if ( !( aUnoName = ScGlobal::GetAddInCollection()->FindFunction(aFuncName, false) ).isEmpty() )
|
|
{
|
|
// bLocalFirst=false in FindFunction, cFunc should be the stored
|
|
// internal name
|
|
|
|
ScUnoAddInCall aCall( *ScGlobal::GetAddInCollection(), aUnoName, nParamCount );
|
|
|
|
if ( !aCall.ValidParamCount() )
|
|
SetError( errIllegalParameter );
|
|
|
|
if ( aCall.NeedsCaller() && !GetError() )
|
|
{
|
|
SfxObjectShell* pShell = pDok->GetDocumentShell();
|
|
if (pShell)
|
|
aCall.SetCallerFromObjectShell( pShell );
|
|
else
|
|
{
|
|
// use temporary model object (without document) to supply options
|
|
aCall.SetCaller( static_cast<beans::XPropertySet*>(
|
|
new ScDocOptionsObj( pDok->GetDocOptions() ) ) );
|
|
}
|
|
}
|
|
|
|
short nPar = nParamCount;
|
|
while ( nPar > 0 && !GetError() )
|
|
{
|
|
--nPar; // 0 .. (nParamCount-1)
|
|
|
|
ScAddInArgumentType eType = aCall.GetArgType( nPar );
|
|
sal_uInt8 nStackType = sal::static_int_cast<sal_uInt8>( GetStackType() );
|
|
|
|
uno::Any aParam;
|
|
switch (eType)
|
|
{
|
|
case SC_ADDINARG_INTEGER:
|
|
{
|
|
double fVal = GetDouble();
|
|
double fInt = (fVal >= 0.0) ? ::rtl::math::approxFloor( fVal ) :
|
|
::rtl::math::approxCeil( fVal );
|
|
if ( fInt >= LONG_MIN && fInt <= LONG_MAX )
|
|
aParam <<= (sal_Int32)fInt;
|
|
else
|
|
SetError(errIllegalArgument);
|
|
}
|
|
break;
|
|
|
|
case SC_ADDINARG_DOUBLE:
|
|
aParam <<= (double) GetDouble();
|
|
break;
|
|
|
|
case SC_ADDINARG_STRING:
|
|
aParam <<= GetString().getString();
|
|
break;
|
|
|
|
case SC_ADDINARG_INTEGER_ARRAY:
|
|
switch( nStackType )
|
|
{
|
|
case svDouble:
|
|
case svString:
|
|
case svSingleRef:
|
|
{
|
|
double fVal = GetDouble();
|
|
double fInt = (fVal >= 0.0) ? ::rtl::math::approxFloor( fVal ) :
|
|
::rtl::math::approxCeil( fVal );
|
|
if ( fInt >= LONG_MIN && fInt <= LONG_MAX )
|
|
{
|
|
sal_Int32 nIntVal = (long)fInt;
|
|
uno::Sequence<sal_Int32> aInner( &nIntVal, 1 );
|
|
uno::Sequence< uno::Sequence<sal_Int32> > aOuter( &aInner, 1 );
|
|
aParam <<= aOuter;
|
|
}
|
|
else
|
|
SetError(errIllegalArgument);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
if (!ScRangeToSequence::FillLongArray( aParam, pDok, aRange ))
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
case svMatrix:
|
|
if (!ScRangeToSequence::FillLongArray( aParam, PopMatrix().get() ))
|
|
SetError(errIllegalParameter);
|
|
break;
|
|
default:
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
|
|
case SC_ADDINARG_DOUBLE_ARRAY:
|
|
switch( nStackType )
|
|
{
|
|
case svDouble:
|
|
case svString:
|
|
case svSingleRef:
|
|
{
|
|
double fVal = GetDouble();
|
|
uno::Sequence<double> aInner( &fVal, 1 );
|
|
uno::Sequence< uno::Sequence<double> > aOuter( &aInner, 1 );
|
|
aParam <<= aOuter;
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
if (!ScRangeToSequence::FillDoubleArray( aParam, pDok, aRange ))
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
case svMatrix:
|
|
if (!ScRangeToSequence::FillDoubleArray( aParam, PopMatrix().get() ))
|
|
SetError(errIllegalParameter);
|
|
break;
|
|
default:
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
|
|
case SC_ADDINARG_STRING_ARRAY:
|
|
switch( nStackType )
|
|
{
|
|
case svDouble:
|
|
case svString:
|
|
case svSingleRef:
|
|
{
|
|
OUString aString = GetString().getString();
|
|
uno::Sequence<OUString> aInner( &aString, 1 );
|
|
uno::Sequence< uno::Sequence<OUString> > aOuter( &aInner, 1 );
|
|
aParam <<= aOuter;
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
if (!ScRangeToSequence::FillStringArray( aParam, pDok, aRange ))
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
case svMatrix:
|
|
if (!ScRangeToSequence::FillStringArray( aParam, PopMatrix().get(), pFormatter ))
|
|
SetError(errIllegalParameter);
|
|
break;
|
|
default:
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
|
|
case SC_ADDINARG_MIXED_ARRAY:
|
|
switch( nStackType )
|
|
{
|
|
case svDouble:
|
|
case svString:
|
|
case svSingleRef:
|
|
{
|
|
uno::Any aElem;
|
|
if ( nStackType == svDouble )
|
|
aElem <<= (double) GetDouble();
|
|
else if ( nStackType == svString )
|
|
aElem <<= GetString().getString();
|
|
else
|
|
{
|
|
ScAddress aAdr;
|
|
if ( PopDoubleRefOrSingleRef( aAdr ) )
|
|
{
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
if (aCell.hasString())
|
|
{
|
|
svl::SharedString aStr;
|
|
GetCellString(aStr, aCell);
|
|
aElem <<= aStr.getString();
|
|
}
|
|
else
|
|
aElem <<= GetCellValue(aAdr, aCell);
|
|
}
|
|
}
|
|
uno::Sequence<uno::Any> aInner( &aElem, 1 );
|
|
uno::Sequence< uno::Sequence<uno::Any> > aOuter( &aInner, 1 );
|
|
aParam <<= aOuter;
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
if (!ScRangeToSequence::FillMixedArray( aParam, pDok, aRange ))
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
case svMatrix:
|
|
if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix().get() ))
|
|
SetError(errIllegalParameter);
|
|
break;
|
|
default:
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
|
|
case SC_ADDINARG_VALUE_OR_ARRAY:
|
|
if ( IsMissing() )
|
|
nStackType = svMissing;
|
|
switch( nStackType )
|
|
{
|
|
case svDouble:
|
|
aParam <<= (double) GetDouble();
|
|
break;
|
|
case svString:
|
|
aParam <<= GetString().getString();
|
|
break;
|
|
case svSingleRef:
|
|
{
|
|
ScAddress aAdr;
|
|
if ( PopDoubleRefOrSingleRef( aAdr ) )
|
|
{
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, aAdr);
|
|
if (aCell.hasString())
|
|
{
|
|
svl::SharedString aStr;
|
|
GetCellString(aStr, aCell);
|
|
aParam <<= aStr.getString();
|
|
}
|
|
else
|
|
aParam <<= GetCellValue(aAdr, aCell);
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
if (!ScRangeToSequence::FillMixedArray( aParam, pDok, aRange ))
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
case svMatrix:
|
|
if (!ScRangeToSequence::FillMixedArray( aParam, PopMatrix().get() ))
|
|
SetError(errIllegalParameter);
|
|
break;
|
|
case svMissing:
|
|
Pop();
|
|
aParam.clear();
|
|
break;
|
|
default:
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
|
|
case SC_ADDINARG_CELLRANGE:
|
|
switch( nStackType )
|
|
{
|
|
case svSingleRef:
|
|
{
|
|
ScAddress aAdr;
|
|
PopSingleRef( aAdr );
|
|
ScRange aRange( aAdr );
|
|
uno::Reference<table::XCellRange> xObj =
|
|
ScCellRangeObj::CreateRangeFromDoc( pDok, aRange );
|
|
if (xObj.is())
|
|
aParam <<= xObj;
|
|
else
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
uno::Reference<table::XCellRange> xObj =
|
|
ScCellRangeObj::CreateRangeFromDoc( pDok, aRange );
|
|
if (xObj.is())
|
|
{
|
|
aParam <<= xObj;
|
|
}
|
|
else
|
|
{
|
|
SetError(errIllegalParameter);
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
PopError();
|
|
SetError(errIllegalParameter);
|
|
}
|
|
aCall.SetParam( nPar, aParam );
|
|
}
|
|
|
|
while (nPar-- > 0)
|
|
{
|
|
Pop(); // in case of error, remove remaining args
|
|
}
|
|
if ( !GetError() )
|
|
{
|
|
aCall.ExecuteCall();
|
|
|
|
if ( aCall.HasVarRes() ) // handle async functions
|
|
{
|
|
if ( rArr.IsRecalcModeNormal() )
|
|
{
|
|
rArr.SetExclusiveRecalcModeOnLoad();
|
|
}
|
|
uno::Reference<sheet::XVolatileResult> xRes = aCall.GetVarRes();
|
|
ScAddInListener* pLis = ScAddInListener::Get( xRes );
|
|
if ( !pLis )
|
|
{
|
|
pLis = ScAddInListener::CreateListener( xRes, pDok );
|
|
pMyFormulaCell->StartListening( *pLis );
|
|
}
|
|
else
|
|
{
|
|
pMyFormulaCell->StartListening( *pLis );
|
|
if ( !pLis->HasDocument( pDok ) )
|
|
{
|
|
pLis->AddDocument( pDok );
|
|
}
|
|
}
|
|
|
|
aCall.SetResult( pLis->GetResult() ); // use result from async
|
|
}
|
|
|
|
if ( aCall.GetErrCode() )
|
|
{
|
|
PushError( aCall.GetErrCode() );
|
|
}
|
|
else if ( aCall.HasMatrix() )
|
|
{
|
|
ScMatrixRef xMat = aCall.GetMatrix();
|
|
PushMatrix( xMat );
|
|
}
|
|
else if ( aCall.HasString() )
|
|
{
|
|
PushString( aCall.GetString() );
|
|
}
|
|
else
|
|
{
|
|
PushDouble( aCall.GetValue() );
|
|
}
|
|
}
|
|
else // error...
|
|
PushError( GetError());
|
|
}
|
|
else
|
|
{
|
|
while( nParamCount-- > 0)
|
|
{
|
|
Pop();
|
|
}
|
|
PushError( errNoAddin );
|
|
}
|
|
}
|
|
|
|
void ScInterpreter::ScMissing()
|
|
{
|
|
PushTempToken( new FormulaMissingToken );
|
|
}
|
|
|
|
#ifndef DISABLE_SCRIPTING
|
|
|
|
static uno::Any lcl_getSheetModule( const uno::Reference<table::XCellRange>& xCellRange, ScDocument* pDok )
|
|
{
|
|
uno::Reference< sheet::XSheetCellRange > xSheetRange( xCellRange, uno::UNO_QUERY_THROW );
|
|
uno::Reference< beans::XPropertySet > xProps( xSheetRange->getSpreadsheet(), uno::UNO_QUERY_THROW );
|
|
OUString sCodeName;
|
|
xProps->getPropertyValue("CodeName") >>= sCodeName;
|
|
// #TODO #FIXME ideally we should 'throw' here if we don't get a valid parent, but... it is possible
|
|
// to create a module ( and use 'Option VBASupport 1' ) for a calc document, in this scenario there
|
|
// are *NO* special document module objects ( of course being able to switch between vba/non vba mode at
|
|
// the document in the future could fix this, especially IF the switching of the vba mode takes care to
|
|
// create the special document module objects if they don't exist.
|
|
BasicManager* pBasMgr = pDok->GetDocumentShell()->GetBasicManager();
|
|
|
|
uno::Reference< uno::XInterface > xIf;
|
|
if ( pBasMgr && !pBasMgr->GetName().isEmpty() )
|
|
{
|
|
OUString sProj( "Standard" );
|
|
if ( !pDok->GetDocumentShell()->GetBasicManager()->GetName().isEmpty() )
|
|
{
|
|
sProj = pDok->GetDocumentShell()->GetBasicManager()->GetName();
|
|
}
|
|
StarBASIC* pBasic = pDok->GetDocumentShell()->GetBasicManager()->GetLib( sProj );
|
|
if ( pBasic )
|
|
{
|
|
SbModule* pMod = pBasic->FindModule( sCodeName );
|
|
if ( pMod )
|
|
{
|
|
xIf = pMod->GetUnoModule();
|
|
}
|
|
}
|
|
}
|
|
return uno::makeAny( xIf );
|
|
}
|
|
|
|
static bool lcl_setVBARange( ScRange& aRange, ScDocument* pDok, SbxVariable* pPar )
|
|
{
|
|
bool bOk = false;
|
|
try
|
|
{
|
|
uno::Reference< uno::XInterface > xVBARange;
|
|
uno::Reference<table::XCellRange> xCellRange = ScCellRangeObj::CreateRangeFromDoc( pDok, aRange );
|
|
uno::Sequence< uno::Any > aArgs(2);
|
|
aArgs[0] = lcl_getSheetModule( xCellRange, pDok );
|
|
aArgs[1] = uno::Any( xCellRange );
|
|
xVBARange = ooo::vba::createVBAUnoAPIServiceWithArgs( pDok->GetDocumentShell(), "ooo.vba.excel.Range", aArgs );
|
|
if ( xVBARange.is() )
|
|
{
|
|
OUString sDummy("A-Range");
|
|
SbxObjectRef aObj = GetSbUnoObject( sDummy, uno::Any( xVBARange ) );
|
|
SetSbUnoObjectDfltPropName( aObj );
|
|
bOk = pPar->PutObject( aObj );
|
|
}
|
|
}
|
|
catch( uno::Exception& )
|
|
{
|
|
}
|
|
return bOk;
|
|
}
|
|
|
|
#endif
|
|
|
|
void ScInterpreter::ScMacro()
|
|
{
|
|
|
|
#ifdef DISABLE_SCRIPTING
|
|
PushNoValue(); // ohne DocShell kein CallBasic
|
|
return;
|
|
#else
|
|
SbxBase::ResetError();
|
|
|
|
sal_uInt8 nParamCount = GetByte();
|
|
OUString aMacro( pCur->GetExternal() );
|
|
|
|
SfxObjectShell* pDocSh = pDok->GetDocumentShell();
|
|
if ( !pDocSh || !pDok->CheckMacroWarn() )
|
|
{
|
|
PushNoValue(); // ohne DocShell kein CallBasic
|
|
return;
|
|
}
|
|
|
|
// keine Sicherheitsabfrage mehr vorneweg (nur CheckMacroWarn), das passiert im CallBasic
|
|
|
|
// Wenn das Dok waehrend eines Basic-Calls geladen wurde,
|
|
// ist das Sbx-Objekt evtl. nicht angelegt (?)
|
|
// pDocSh->GetSbxObject();
|
|
|
|
// Funktion ueber den einfachen Namen suchen,
|
|
// dann aBasicStr, aMacroStr fuer SfxObjectShell::CallBasic zusammenbauen
|
|
|
|
StarBASIC* pRoot;
|
|
|
|
try
|
|
{
|
|
pRoot = pDocSh->GetBasic();
|
|
}
|
|
catch (...)
|
|
{
|
|
pRoot = NULL;
|
|
}
|
|
|
|
SbxVariable* pVar = pRoot ? pRoot->Find(aMacro, SbxCLASS_METHOD) : NULL;
|
|
if( !pVar || pVar->GetType() == SbxVOID || !pVar->ISA(SbMethod) )
|
|
{
|
|
PushError( errNoMacro );
|
|
return;
|
|
}
|
|
|
|
bool bVolatileMacro = false;
|
|
SbMethod* pMethod = (SbMethod*)pVar;
|
|
|
|
SbModule* pModule = pMethod->GetModule();
|
|
bool bUseVBAObjects = pModule->IsVBACompat();
|
|
SbxObject* pObject = pModule->GetParent();
|
|
OSL_ENSURE(pObject->IsA(TYPE(StarBASIC)), "No Basic found!");
|
|
OUString aMacroStr = pObject->GetName() + "." + pModule->GetName() + "." + pMethod->GetName();
|
|
OUString aBasicStr;
|
|
if (pObject->GetParent())
|
|
{
|
|
aBasicStr = pObject->GetParent()->GetName(); // Dokumentenbasic
|
|
}
|
|
else
|
|
{
|
|
aBasicStr = SfxGetpApp()->GetName(); // Applikationsbasic
|
|
}
|
|
// Parameter-Array zusammenbauen
|
|
|
|
SbxArrayRef refPar = new SbxArray;
|
|
bool bOk = true;
|
|
for( short i = nParamCount; i && bOk ; i-- )
|
|
{
|
|
SbxVariable* pPar = refPar->Get( (sal_uInt16) i );
|
|
sal_uInt8 nStackType = sal::static_int_cast<sal_uInt8>( GetStackType() );
|
|
switch( nStackType )
|
|
{
|
|
case svDouble:
|
|
pPar->PutDouble( GetDouble() );
|
|
break;
|
|
case svString:
|
|
pPar->PutString( GetString().getString() );
|
|
break;
|
|
case svExternalSingleRef:
|
|
{
|
|
ScExternalRefCache::TokenRef pToken;
|
|
PopExternalSingleRef(pToken);
|
|
if ( pToken->GetType() == svString )
|
|
pPar->PutString( pToken->GetString().getString() );
|
|
else if ( pToken->GetType() == svDouble )
|
|
pPar->PutDouble( pToken->GetDouble() );
|
|
else
|
|
{
|
|
SetError( errIllegalArgument );
|
|
bOk = false;
|
|
}
|
|
}
|
|
break;
|
|
case svSingleRef:
|
|
{
|
|
ScAddress aAdr;
|
|
PopSingleRef( aAdr );
|
|
if ( bUseVBAObjects )
|
|
{
|
|
ScRange aRange( aAdr );
|
|
bOk = lcl_setVBARange( aRange, pDok, pPar );
|
|
}
|
|
else
|
|
{
|
|
bOk = SetSbxVariable( pPar, aAdr );
|
|
}
|
|
}
|
|
break;
|
|
case svDoubleRef:
|
|
{
|
|
SCCOL nCol1;
|
|
SCROW nRow1;
|
|
SCTAB nTab1;
|
|
SCCOL nCol2;
|
|
SCROW nRow2;
|
|
SCTAB nTab2;
|
|
PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
|
|
if( nTab1 != nTab2 )
|
|
{
|
|
SetError( errIllegalParameter );
|
|
bOk = false;
|
|
}
|
|
else
|
|
{
|
|
if ( bUseVBAObjects )
|
|
{
|
|
ScRange aRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
|
|
bOk = lcl_setVBARange( aRange, pDok, pPar );
|
|
}
|
|
else
|
|
{
|
|
SbxDimArrayRef refArray = new SbxDimArray;
|
|
refArray->AddDim32( 1, nRow2 - nRow1 + 1 );
|
|
refArray->AddDim32( 1, nCol2 - nCol1 + 1 );
|
|
ScAddress aAdr( nCol1, nRow1, nTab1 );
|
|
for( SCROW nRow = nRow1; bOk && nRow <= nRow2; nRow++ )
|
|
{
|
|
aAdr.SetRow( nRow );
|
|
sal_Int32 nIdx[ 2 ];
|
|
nIdx[ 0 ] = nRow-nRow1+1;
|
|
for( SCCOL nCol = nCol1; bOk && nCol <= nCol2; nCol++ )
|
|
{
|
|
aAdr.SetCol( nCol );
|
|
nIdx[ 1 ] = nCol-nCol1+1;
|
|
SbxVariable* p = refArray->Get32( nIdx );
|
|
bOk = SetSbxVariable( p, aAdr );
|
|
}
|
|
}
|
|
pPar->PutObject( refArray );
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
case svMatrix:
|
|
{
|
|
ScMatrixRef pMat = GetMatrix();
|
|
SCSIZE nC, nR;
|
|
if (pMat && !nGlobalError)
|
|
{
|
|
pMat->GetDimensions(nC, nR);
|
|
SbxDimArrayRef refArray = new SbxDimArray;
|
|
refArray->AddDim32( 1, static_cast<sal_Int32>(nR) );
|
|
refArray->AddDim32( 1, static_cast<sal_Int32>(nC) );
|
|
for( SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++ )
|
|
{
|
|
sal_Int32 nIdx[ 2 ];
|
|
nIdx[ 0 ] = static_cast<sal_Int32>(nMatRow+1);
|
|
for( SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++ )
|
|
{
|
|
nIdx[ 1 ] = static_cast<sal_Int32>(nMatCol+1);
|
|
SbxVariable* p = refArray->Get32( nIdx );
|
|
if (pMat->IsString(nMatCol, nMatRow))
|
|
{
|
|
p->PutString( pMat->GetString(nMatCol, nMatRow).getString() );
|
|
}
|
|
else
|
|
{
|
|
p->PutDouble( pMat->GetDouble(nMatCol, nMatRow));
|
|
}
|
|
}
|
|
}
|
|
pPar->PutObject( refArray );
|
|
}
|
|
else
|
|
{
|
|
SetError( errIllegalParameter );
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
SetError( errIllegalParameter );
|
|
bOk = false;
|
|
}
|
|
}
|
|
if( bOk )
|
|
{
|
|
pDok->LockTable( aPos.Tab() );
|
|
SbxVariableRef refRes = new SbxVariable;
|
|
pDok->IncMacroInterpretLevel();
|
|
ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, refPar, refRes );
|
|
pDok->DecMacroInterpretLevel();
|
|
pDok->UnlockTable( aPos.Tab() );
|
|
|
|
ScMacroManager* pMacroMgr = pDok->GetMacroManager();
|
|
if (pMacroMgr)
|
|
{
|
|
bVolatileMacro = pMacroMgr->GetUserFuncVolatile( pMethod->GetName() );
|
|
pMacroMgr->AddDependentCell(pModule->GetName(), pMyFormulaCell);
|
|
}
|
|
|
|
SbxDataType eResType = refRes->GetType();
|
|
if( SbxBase::GetError() )
|
|
{
|
|
SetError( errNoValue);
|
|
}
|
|
if ( eRet != ERRCODE_NONE )
|
|
{
|
|
PushNoValue();
|
|
}
|
|
else if( eResType >= SbxINTEGER && eResType <= SbxDOUBLE )
|
|
{
|
|
PushDouble( refRes->GetDouble() );
|
|
}
|
|
else if ( eResType & SbxARRAY )
|
|
{
|
|
SbxBase* pElemObj = refRes->GetObject();
|
|
SbxDimArray* pDimArray = PTR_CAST(SbxDimArray,pElemObj);
|
|
short nDim = pDimArray->GetDims();
|
|
if ( 1 <= nDim && nDim <= 2 )
|
|
{
|
|
sal_Int32 nCs, nCe, nRs, nRe;
|
|
SCSIZE nC, nR;
|
|
SCCOL nColIdx;
|
|
SCROW nRowIdx;
|
|
if ( nDim == 1 )
|
|
{ // array( cols ) eine Zeile, mehrere Spalten
|
|
pDimArray->GetDim32( 1, nCs, nCe );
|
|
nC = static_cast<SCSIZE>(nCe - nCs + 1);
|
|
nRs = nRe = 0;
|
|
nR = 1;
|
|
nColIdx = 0;
|
|
nRowIdx = 1;
|
|
}
|
|
else
|
|
{ // array( rows, cols )
|
|
pDimArray->GetDim32( 1, nRs, nRe );
|
|
nR = static_cast<SCSIZE>(nRe - nRs + 1);
|
|
pDimArray->GetDim32( 2, nCs, nCe );
|
|
nC = static_cast<SCSIZE>(nCe - nCs + 1);
|
|
nColIdx = 1;
|
|
nRowIdx = 0;
|
|
}
|
|
ScMatrixRef pMat = GetNewMat( nC, nR);
|
|
if ( pMat )
|
|
{
|
|
SbxVariable* pV;
|
|
SbxDataType eType;
|
|
for ( SCSIZE j=0; j < nR; j++ )
|
|
{
|
|
sal_Int32 nIdx[ 2 ];
|
|
// bei eindimensionalem array( cols ) wird nIdx[1]
|
|
// von SbxDimArray::Get ignoriert
|
|
nIdx[ nRowIdx ] = nRs + static_cast<sal_Int32>(j);
|
|
for ( SCSIZE i=0; i < nC; i++ )
|
|
{
|
|
nIdx[ nColIdx ] = nCs + static_cast<sal_Int32>(i);
|
|
pV = pDimArray->Get32( nIdx );
|
|
eType = pV->GetType();
|
|
if ( eType >= SbxINTEGER && eType <= SbxDOUBLE )
|
|
{
|
|
pMat->PutDouble( pV->GetDouble(), i, j );
|
|
}
|
|
else
|
|
{
|
|
pMat->PutString(mrStrPool.intern(pV->GetOUString()), i, j);
|
|
}
|
|
}
|
|
}
|
|
PushMatrix( pMat );
|
|
}
|
|
else
|
|
{
|
|
PushIllegalArgument();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PushNoValue();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
PushString( refRes->GetOUString() );
|
|
}
|
|
}
|
|
|
|
if (bVolatileMacro && meVolatileType == NOT_VOLATILE)
|
|
meVolatileType = VOLATILE_MACRO;
|
|
#endif
|
|
}
|
|
|
|
#ifndef DISABLE_SCRIPTING
|
|
|
|
bool ScInterpreter::SetSbxVariable( SbxVariable* pVar, const ScAddress& rPos )
|
|
{
|
|
bool bOk = true;
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, rPos);
|
|
if (!aCell.isEmpty())
|
|
{
|
|
sal_uInt16 nErr;
|
|
double nVal;
|
|
switch (aCell.meType)
|
|
{
|
|
case CELLTYPE_VALUE :
|
|
nVal = GetValueCellValue(rPos, aCell.mfValue);
|
|
pVar->PutDouble( nVal );
|
|
break;
|
|
case CELLTYPE_STRING :
|
|
case CELLTYPE_EDIT :
|
|
pVar->PutString(aCell.getString(pDok));
|
|
break;
|
|
case CELLTYPE_FORMULA :
|
|
nErr = aCell.mpFormula->GetErrCode();
|
|
if( !nErr )
|
|
{
|
|
if (aCell.mpFormula->IsValue())
|
|
{
|
|
nVal = aCell.mpFormula->GetValue();
|
|
pVar->PutDouble(aCell.mpFormula->GetValue());
|
|
}
|
|
else
|
|
pVar->PutString(aCell.mpFormula->GetString().getString());
|
|
}
|
|
else
|
|
SetError( nErr ), bOk = false;
|
|
break;
|
|
default :
|
|
pVar->PutDouble( 0.0 );
|
|
}
|
|
}
|
|
else
|
|
pVar->PutDouble( 0.0 );
|
|
return bOk;
|
|
}
|
|
|
|
#endif
|
|
|
|
namespace {
|
|
|
|
class FindByPointer : ::std::unary_function<ScInterpreterTableOpParams, bool>
|
|
{
|
|
const ScInterpreterTableOpParams* mpTableOp;
|
|
public:
|
|
FindByPointer(const ScInterpreterTableOpParams* p) : mpTableOp(p) {}
|
|
bool operator() (const ScInterpreterTableOpParams& val) const
|
|
{
|
|
return &val == mpTableOp;
|
|
}
|
|
};
|
|
|
|
}
|
|
|
|
void ScInterpreter::ScTableOp()
|
|
{
|
|
sal_uInt8 nParamCount = GetByte();
|
|
if (nParamCount != 3 && nParamCount != 5)
|
|
{
|
|
PushIllegalParameter();
|
|
return;
|
|
}
|
|
ScInterpreterTableOpParams* pTableOp = new ScInterpreterTableOpParams;
|
|
if (nParamCount == 5)
|
|
{
|
|
PopSingleRef( pTableOp->aNew2 );
|
|
PopSingleRef( pTableOp->aOld2 );
|
|
}
|
|
PopSingleRef( pTableOp->aNew1 );
|
|
PopSingleRef( pTableOp->aOld1 );
|
|
PopSingleRef( pTableOp->aFormulaPos );
|
|
|
|
pTableOp->bValid = true;
|
|
pDok->aTableOpList.push_back( pTableOp );
|
|
pDok->IncInterpreterTableOpLevel();
|
|
|
|
bool bReuseLastParams = (pDok->aLastTableOpParams == *pTableOp);
|
|
if ( bReuseLastParams )
|
|
{
|
|
pTableOp->aNotifiedFormulaPos = pDok->aLastTableOpParams.aNotifiedFormulaPos;
|
|
pTableOp->bRefresh = true;
|
|
for ( ::std::vector< ScAddress >::const_iterator iBroadcast(
|
|
pTableOp->aNotifiedFormulaPos.begin() );
|
|
iBroadcast != pTableOp->aNotifiedFormulaPos.end();
|
|
++iBroadcast )
|
|
{ // emulate broadcast and indirectly collect cell pointers
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, *iBroadcast);
|
|
if (aCell.meType == CELLTYPE_FORMULA)
|
|
aCell.mpFormula->SetTableOpDirty();
|
|
}
|
|
}
|
|
else
|
|
{ // broadcast and indirectly collect cell pointers and positions
|
|
pDok->SetTableOpDirty( pTableOp->aOld1 );
|
|
if ( nParamCount == 5 )
|
|
pDok->SetTableOpDirty( pTableOp->aOld2 );
|
|
}
|
|
pTableOp->bCollectNotifications = false;
|
|
|
|
ScRefCellValue aCell;
|
|
aCell.assign(*pDok, pTableOp->aFormulaPos);
|
|
if (aCell.meType == CELLTYPE_FORMULA)
|
|
aCell.mpFormula->SetDirtyVar();
|
|
if (aCell.hasNumeric())
|
|
{
|
|
PushDouble(GetCellValue(pTableOp->aFormulaPos, aCell));
|
|
}
|
|
else
|
|
{
|
|
svl::SharedString aCellString;
|
|
GetCellString(aCellString, aCell);
|
|
PushString( aCellString );
|
|
}
|
|
|
|
boost::ptr_vector< ScInterpreterTableOpParams >::iterator itr =
|
|
::std::find_if(pDok->aTableOpList.begin(), pDok->aTableOpList.end(), FindByPointer(pTableOp));
|
|
if (itr != pDok->aTableOpList.end())
|
|
pTableOp = pDok->aTableOpList.release(itr).release();
|
|
|
|
// set dirty again once more to be able to recalculate original
|
|
for ( ::std::vector< ScFormulaCell* >::const_iterator iBroadcast(
|
|
pTableOp->aNotifiedFormulaCells.begin() );
|
|
iBroadcast != pTableOp->aNotifiedFormulaCells.end();
|
|
++iBroadcast )
|
|
{
|
|
(*iBroadcast)->SetTableOpDirty();
|
|
}
|
|
|
|
// save these params for next incarnation
|
|
if ( !bReuseLastParams )
|
|
pDok->aLastTableOpParams = *pTableOp;
|
|
|
|
if (aCell.meType == CELLTYPE_FORMULA)
|
|
{
|
|
aCell.mpFormula->SetDirtyVar();
|
|
aCell.mpFormula->GetErrCode(); // recalculate original
|
|
}
|
|
|
|
// Reset all dirty flags so next incarnation does really collect all cell
|
|
// pointers during notifications and not just non-dirty ones, which may
|
|
// happen if a formula cell is used by more than one TableOp block.
|
|
for ( ::std::vector< ScFormulaCell* >::const_iterator iBroadcast2(
|
|
pTableOp->aNotifiedFormulaCells.begin() );
|
|
iBroadcast2 != pTableOp->aNotifiedFormulaCells.end();
|
|
++iBroadcast2 )
|
|
{
|
|
(*iBroadcast2)->ResetTableOpDirtyVar();
|
|
}
|
|
delete pTableOp;
|
|
|
|
pDok->DecInterpreterTableOpLevel();
|
|
}
|
|
|
|
void ScInterpreter::ScDBArea()
|
|
{
|
|
ScDBData* pDBData = pDok->GetDBCollection()->getNamedDBs().findByIndex(pCur->GetIndex());
|
|
if (pDBData)
|
|
{
|
|
ScComplexRefData aRefData;
|
|
aRefData.InitFlags();
|
|
ScRange aRange;
|
|
pDBData->GetArea(aRange);
|
|
aRange.aEnd.SetTab(aRange.aStart.Tab());
|
|
aRefData.SetRange(aRange, aPos);
|
|
PushTempToken( new ScDoubleRefToken( aRefData ) );
|
|
}
|
|
else
|
|
PushError( errNoName);
|
|
}
|
|
|
|
void ScInterpreter::ScColRowNameAuto()
|
|
{
|
|
ScComplexRefData aRefData( static_cast<const ScToken*>(pCur)->GetDoubleRef() );
|
|
ScRange aAbs = aRefData.toAbs(aPos);
|
|
if (!ValidRange(aAbs))
|
|
{
|
|
PushError( errNoRef );
|
|
return;
|
|
}
|
|
|
|
SCsCOL nStartCol;
|
|
SCsROW nStartRow;
|
|
|
|
// evtl. Begrenzung durch definierte ColRowNameRanges merken
|
|
SCsCOL nCol2 = aAbs.aEnd.Col();
|
|
SCsROW nRow2 = aAbs.aEnd.Row();
|
|
// DataArea of the first cell
|
|
nStartCol = aAbs.aStart.Col();
|
|
nStartRow = aAbs.aStart.Row();
|
|
aAbs.aEnd = aAbs.aStart; // Shrink to the top-left cell.
|
|
|
|
{
|
|
// Expand to the data area. Only modify the end position.
|
|
SCCOL nDACol1 = aAbs.aStart.Col(), nDACol2 = aAbs.aEnd.Col();
|
|
SCROW nDARow1 = aAbs.aStart.Row(), nDARow2 = aAbs.aEnd.Row();
|
|
pDok->GetDataArea(aAbs.aStart.Tab(), nDACol1, nDARow1, nDACol2, nDARow2, true, false);
|
|
aAbs.aEnd.SetCol(nDACol2);
|
|
aAbs.aEnd.SetRow(nDARow2);
|
|
}
|
|
|
|
//! korrespondiert mit ScCompiler::GetToken
|
|
if ( aRefData.Ref1.IsColRel() )
|
|
{ // ColName
|
|
aAbs.aEnd.SetCol(nStartCol);
|
|
// evtl. vorherige Begrenzung durch definierte ColRowNameRanges erhalten
|
|
if (aAbs.aEnd.Row() > nRow2)
|
|
aAbs.aEnd.SetRow(nRow2);
|
|
SCROW nMyRow;
|
|
if ( aPos.Col() == nStartCol
|
|
&& nStartRow <= (nMyRow = aPos.Row()) && nMyRow <= aAbs.aEnd.Row())
|
|
{ // Formel in gleicher Spalte und innerhalb des Range
|
|
if ( nMyRow == nStartRow )
|
|
{ // direkt unter dem Namen den Rest nehmen
|
|
nStartRow++;
|
|
if ( nStartRow > MAXROW )
|
|
nStartRow = MAXROW;
|
|
aAbs.aStart.SetRow(nStartRow);
|
|
}
|
|
else
|
|
{ // weiter unten vom Namen bis zur Formelzelle
|
|
aAbs.aEnd.SetRow(nMyRow - 1);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{ // RowName
|
|
aAbs.aEnd.SetRow(nStartRow);
|
|
// evtl. vorherige Begrenzung durch definierte ColRowNameRanges erhalten
|
|
if (aAbs.aEnd.Col() > nCol2)
|
|
aAbs.aEnd.SetCol(nCol2);
|
|
SCCOL nMyCol;
|
|
if ( aPos.Row() == nStartRow
|
|
&& nStartCol <= (nMyCol = aPos.Col()) && nMyCol <= aAbs.aEnd.Col())
|
|
{ // Formel in gleicher Zeile und innerhalb des Range
|
|
if ( nMyCol == nStartCol )
|
|
{ // direkt neben dem Namen den Rest nehmen
|
|
nStartCol++;
|
|
if ( nStartCol > MAXCOL )
|
|
nStartCol = MAXCOL;
|
|
aAbs.aStart.SetCol(nStartCol);
|
|
}
|
|
else
|
|
{ // weiter rechts vom Namen bis zur Formelzelle
|
|
aAbs.aEnd.SetCol(nMyCol - 1);
|
|
}
|
|
}
|
|
}
|
|
aRefData.SetRange(aAbs, aPos);
|
|
PushTempToken( new ScDoubleRefToken( aRefData ) );
|
|
}
|
|
|
|
// --- internals ------------------------------------------------------------
|
|
|
|
void ScInterpreter::ScTTT()
|
|
{ // Temporaerer Test-Tanz, zum auspropieren von Funktionen etc.
|
|
sal_uInt8 nParamCount = GetByte();
|
|
// do something, nParamCount bei Pops runterzaehlen!
|
|
|
|
// Stack aufraeumen
|
|
while ( nParamCount-- > 0)
|
|
Pop();
|
|
PushError(errNoValue);
|
|
}
|
|
|
|
ScInterpreter::ScInterpreter( ScFormulaCell* pCell, ScDocument* pDoc,
|
|
const ScAddress& rPos, ScTokenArray& r )
|
|
: aCode(r)
|
|
, aPos(rPos)
|
|
, rArr(r)
|
|
, pDok(pDoc)
|
|
, mrStrPool(pDoc->GetSharedStringPool())
|
|
, pJumpMatrix(NULL)
|
|
, pTokenMatrixMap(NULL)
|
|
, pMyFormulaCell(pCell)
|
|
, pFormatter(pDoc->GetFormatTable())
|
|
, pCur(NULL)
|
|
, nGlobalError(0)
|
|
, sp(0)
|
|
, maxsp(0)
|
|
, nFuncFmtIndex(0)
|
|
, nCurFmtIndex(0)
|
|
, nRetFmtIndex(0)
|
|
, nFuncFmtType(0)
|
|
, nCurFmtType(0)
|
|
, nRetFmtType(0)
|
|
, mnStringNoValueError(errNoValue)
|
|
, mnSubTotalFlags(0)
|
|
, cPar(0)
|
|
, bCalcAsShown(pDoc->GetDocOptions().IsCalcAsShown())
|
|
, meVolatileType(r.IsRecalcModeAlways() ? VOLATILE : NOT_VOLATILE)
|
|
{
|
|
MergeCalcConfig();
|
|
|
|
if(pMyFormulaCell)
|
|
{
|
|
sal_uInt8 cMatFlag = pMyFormulaCell->GetMatrixFlag();
|
|
bMatrixFormula = ( cMatFlag == MM_FORMULA || cMatFlag == MM_FAKE );
|
|
}
|
|
else
|
|
bMatrixFormula = false;
|
|
|
|
if (!bGlobalStackInUse)
|
|
{
|
|
bGlobalStackInUse = true;
|
|
if (!pGlobalStack)
|
|
pGlobalStack = new ScTokenStack;
|
|
pStackObj = pGlobalStack;
|
|
}
|
|
else
|
|
{
|
|
pStackObj = new ScTokenStack;
|
|
}
|
|
pStack = pStackObj->pPointer;
|
|
}
|
|
|
|
ScInterpreter::~ScInterpreter()
|
|
{
|
|
// delete pStack;
|
|
|
|
if ( pStackObj == pGlobalStack )
|
|
bGlobalStackInUse = false;
|
|
else
|
|
delete pStackObj;
|
|
if (pTokenMatrixMap)
|
|
delete pTokenMatrixMap;
|
|
}
|
|
|
|
void ScInterpreter::SetGlobalConfig(const ScCalcConfig& rConfig)
|
|
{
|
|
maGlobalConfig = rConfig;
|
|
}
|
|
|
|
const ScCalcConfig& ScInterpreter::GetGlobalConfig()
|
|
{
|
|
return maGlobalConfig;
|
|
}
|
|
|
|
void ScInterpreter::MergeCalcConfig()
|
|
{
|
|
maCalcConfig = maGlobalConfig;
|
|
maCalcConfig.MergeDocumentSpecific( pDok->GetCalcConfig());
|
|
}
|
|
|
|
void ScInterpreter::GlobalExit()
|
|
{
|
|
OSL_ENSURE(!bGlobalStackInUse, "who is still using the TokenStack?");
|
|
DELETEZ(pGlobalStack);
|
|
}
|
|
|
|
namespace {
|
|
|
|
double applyImplicitIntersection(const sc::RangeMatrix& rMat, const ScAddress& rPos)
|
|
{
|
|
if (rMat.mnRow1 <= rPos.Row() && rPos.Row() <= rMat.mnRow2 && rMat.mnCol1 == rMat.mnCol2)
|
|
{
|
|
SCROW nOffset = rPos.Row() - rMat.mnRow1;
|
|
return rMat.mpMat->GetDouble(0, nOffset);
|
|
}
|
|
|
|
if (rMat.mnCol1 <= rPos.Col() && rPos.Col() <= rMat.mnCol2 && rMat.mnRow1 == rMat.mnRow2)
|
|
{
|
|
SCROW nOffset = rPos.Col() - rMat.mnCol1;
|
|
return rMat.mpMat->GetDouble(nOffset, 0);
|
|
}
|
|
|
|
double fVal;
|
|
rtl::math::setNan(&fVal);
|
|
return fVal;
|
|
}
|
|
|
|
}
|
|
|
|
StackVar ScInterpreter::Interpret()
|
|
{
|
|
short nRetTypeExpr = NUMBERFORMAT_UNDEFINED;
|
|
sal_uLong nRetIndexExpr = 0;
|
|
sal_uInt16 nErrorFunction = 0;
|
|
sal_uInt16 nErrorFunctionCount = 0;
|
|
sal_uInt16 nStackBase;
|
|
|
|
nGlobalError = 0;
|
|
nStackBase = sp = maxsp = 0;
|
|
nRetFmtType = NUMBERFORMAT_UNDEFINED;
|
|
nFuncFmtType = NUMBERFORMAT_UNDEFINED;
|
|
nFuncFmtIndex = nCurFmtIndex = nRetFmtIndex = 0;
|
|
xResult = NULL;
|
|
pJumpMatrix = NULL;
|
|
mnSubTotalFlags = 0x00;
|
|
ScTokenMatrixMap::const_iterator aTokenMatrixMapIter;
|
|
|
|
// Once upon a time we used to have FP exceptions on, and there was a
|
|
// Windows printer driver that kept switching off exceptions, so we had to
|
|
// switch them back on again every time. Who knows if there isn't a driver
|
|
// that keeps switching exceptions on, now that we run with exceptions off,
|
|
// so reassure exceptions are really off.
|
|
SAL_MATH_FPEXCEPTIONS_OFF();
|
|
|
|
aCode.Reset();
|
|
while( ( pCur = aCode.Next() ) != NULL
|
|
&& (!nGlobalError || nErrorFunction <= nErrorFunctionCount) )
|
|
{
|
|
OpCode eOp = pCur->GetOpCode();
|
|
cPar = pCur->GetByte();
|
|
if ( eOp == ocPush )
|
|
{
|
|
// RPN code push without error
|
|
PushWithoutError( (FormulaToken&) *pCur );
|
|
}
|
|
else if (pTokenMatrixMap &&
|
|
!(eOp == ocIf || eOp == ocIfError || eOp == ocIfNA || eOp == ocChose) &&
|
|
((aTokenMatrixMapIter = pTokenMatrixMap->find( pCur)) !=
|
|
pTokenMatrixMap->end()) &&
|
|
(*aTokenMatrixMapIter).second->GetType() != svJumpMatrix)
|
|
{
|
|
// Path already calculated, reuse result.
|
|
nStackBase = sp - pCur->GetParamCount();
|
|
if ( nStackBase > sp )
|
|
nStackBase = sp; // underflow?!?
|
|
sp = nStackBase;
|
|
PushTempToken( (*aTokenMatrixMapIter).second.get());
|
|
}
|
|
else
|
|
{
|
|
// previous expression determines the current number format
|
|
nCurFmtType = nRetTypeExpr;
|
|
nCurFmtIndex = nRetIndexExpr;
|
|
// default function's format, others are set if needed
|
|
nFuncFmtType = NUMBERFORMAT_NUMBER;
|
|
nFuncFmtIndex = 0;
|
|
|
|
if ( eOp == ocIf || eOp == ocChose || eOp == ocIfError || eOp == ocIfNA )
|
|
nStackBase = sp; // don't mess around with the jumps
|
|
else
|
|
{
|
|
// Convert parameters to matrix if in array/matrix formula and
|
|
// parameters of function indicate doing so. Create JumpMatrix
|
|
// if necessary.
|
|
if ( MatrixParameterConversion() )
|
|
{
|
|
eOp = ocNone; // JumpMatrix created
|
|
nStackBase = sp;
|
|
}
|
|
else
|
|
nStackBase = sp - pCur->GetParamCount();
|
|
}
|
|
if ( nStackBase > sp )
|
|
nStackBase = sp; // underflow?!?
|
|
|
|
switch( eOp )
|
|
{
|
|
case ocSep:
|
|
case ocClose: // pushed by the compiler
|
|
case ocMissing : ScMissing(); break;
|
|
case ocMacro : ScMacro(); break;
|
|
case ocDBArea : ScDBArea(); break;
|
|
case ocColRowNameAuto : ScColRowNameAuto(); break;
|
|
case ocIf : ScIfJump(); break;
|
|
case ocIfError : ScIfError( false ); break;
|
|
case ocIfNA : ScIfError( true ); break;
|
|
case ocChose : ScChoseJump(); break;
|
|
case ocAdd : ScAdd(); break;
|
|
case ocSub : ScSub(); break;
|
|
case ocMul : ScMul(); break;
|
|
case ocDiv : ScDiv(); break;
|
|
case ocAmpersand : ScAmpersand(); break;
|
|
case ocPow : ScPow(); break;
|
|
case ocEqual : ScEqual(); break;
|
|
case ocNotEqual : ScNotEqual(); break;
|
|
case ocLess : ScLess(); break;
|
|
case ocGreater : ScGreater(); break;
|
|
case ocLessEqual : ScLessEqual(); break;
|
|
case ocGreaterEqual : ScGreaterEqual(); break;
|
|
case ocAnd : ScAnd(); break;
|
|
case ocOr : ScOr(); break;
|
|
case ocXor : ScXor(); break;
|
|
case ocIntersect : ScIntersect(); break;
|
|
case ocRange : ScRangeFunc(); break;
|
|
case ocUnion : ScUnionFunc(); break;
|
|
case ocNot : ScNot(); break;
|
|
case ocNegSub :
|
|
case ocNeg : ScNeg(); break;
|
|
case ocPercentSign : ScPercentSign(); break;
|
|
case ocPi : ScPi(); break;
|
|
case ocRandom : ScRandom(); break;
|
|
case ocTrue : ScTrue(); break;
|
|
case ocFalse : ScFalse(); break;
|
|
case ocGetActDate : ScGetActDate(); break;
|
|
case ocGetActTime : ScGetActTime(); break;
|
|
case ocNotAvail : PushError( NOTAVAILABLE); break;
|
|
case ocDeg : ScDeg(); break;
|
|
case ocRad : ScRad(); break;
|
|
case ocSin : ScSin(); break;
|
|
case ocCos : ScCos(); break;
|
|
case ocTan : ScTan(); break;
|
|
case ocCot : ScCot(); break;
|
|
case ocArcSin : ScArcSin(); break;
|
|
case ocArcCos : ScArcCos(); break;
|
|
case ocArcTan : ScArcTan(); break;
|
|
case ocArcCot : ScArcCot(); break;
|
|
case ocSinHyp : ScSinHyp(); break;
|
|
case ocCosHyp : ScCosHyp(); break;
|
|
case ocTanHyp : ScTanHyp(); break;
|
|
case ocCotHyp : ScCotHyp(); break;
|
|
case ocArcSinHyp : ScArcSinHyp(); break;
|
|
case ocArcCosHyp : ScArcCosHyp(); break;
|
|
case ocArcTanHyp : ScArcTanHyp(); break;
|
|
case ocArcCotHyp : ScArcCotHyp(); break;
|
|
case ocCosecant : ScCosecant(); break;
|
|
case ocSecant : ScSecant(); break;
|
|
case ocCosecantHyp : ScCosecantHyp(); break;
|
|
case ocSecantHyp : ScSecantHyp(); break;
|
|
case ocExp : ScExp(); break;
|
|
case ocLn : ScLn(); break;
|
|
case ocLog10 : ScLog10(); break;
|
|
case ocSqrt : ScSqrt(); break;
|
|
case ocFact : ScFact(); break;
|
|
case ocGetYear : ScGetYear(); break;
|
|
case ocGetMonth : ScGetMonth(); break;
|
|
case ocGetDay : ScGetDay(); break;
|
|
case ocGetDayOfWeek : ScGetDayOfWeek(); break;
|
|
case ocWeek : ScGetWeekOfYear(); break;
|
|
case ocEasterSunday : ScEasterSunday(); break;
|
|
case ocNetWorkdays_MS : ScNetWorkdays_MS(); break;
|
|
case ocWorkday_MS : ScWorkday_MS(); break;
|
|
case ocGetHour : ScGetHour(); break;
|
|
case ocGetMin : ScGetMin(); break;
|
|
case ocGetSec : ScGetSec(); break;
|
|
case ocPlusMinus : ScPlusMinus(); break;
|
|
case ocAbs : ScAbs(); break;
|
|
case ocInt : ScInt(); break;
|
|
case ocEven : ScEven(); break;
|
|
case ocOdd : ScOdd(); break;
|
|
case ocPhi : ScPhi(); break;
|
|
case ocGauss : ScGauss(); break;
|
|
case ocStdNormDist : ScStdNormDist(); break;
|
|
case ocStdNormDist_MS : ScStdNormDist_MS(); break;
|
|
case ocFisher : ScFisher(); break;
|
|
case ocFisherInv : ScFisherInv(); break;
|
|
case ocIsEmpty : ScIsEmpty(); break;
|
|
case ocIsString : ScIsString(); break;
|
|
case ocIsNonString : ScIsNonString(); break;
|
|
case ocIsLogical : ScIsLogical(); break;
|
|
case ocType : ScType(); break;
|
|
case ocCell : ScCell(); break;
|
|
case ocIsRef : ScIsRef(); break;
|
|
case ocIsValue : ScIsValue(); break;
|
|
case ocIsFormula : ScIsFormula(); break;
|
|
case ocFormula : ScFormula(); break;
|
|
case ocIsNA : ScIsNV(); break;
|
|
case ocIsErr : ScIsErr(); break;
|
|
case ocIsError : ScIsError(); break;
|
|
case ocIsEven : ScIsEven(); break;
|
|
case ocIsOdd : ScIsOdd(); break;
|
|
case ocN : ScN(); break;
|
|
case ocGetDateValue : ScGetDateValue(); break;
|
|
case ocGetTimeValue : ScGetTimeValue(); break;
|
|
case ocCode : ScCode(); break;
|
|
case ocTrim : ScTrim(); break;
|
|
case ocUpper : ScUpper(); break;
|
|
case ocPropper : ScPropper(); break;
|
|
case ocLower : ScLower(); break;
|
|
case ocLen : ScLen(); break;
|
|
case ocT : ScT(); break;
|
|
case ocClean : ScClean(); break;
|
|
case ocValue : ScValue(); break;
|
|
case ocNumberValue : ScNumberValue(); break;
|
|
case ocChar : ScChar(); break;
|
|
case ocArcTan2 : ScArcTan2(); break;
|
|
case ocMod : ScMod(); break;
|
|
case ocPower : ScPower(); break;
|
|
case ocRound : ScRound(); break;
|
|
case ocRoundUp : ScRoundUp(); break;
|
|
case ocTrunc :
|
|
case ocRoundDown : ScRoundDown(); break;
|
|
case ocCeil : ScCeil(); break;
|
|
case ocCeil_MS :
|
|
case ocCeil_ISO : ScCeil_MS(); break;
|
|
case ocFloor : ScFloor(); break;
|
|
case ocFloor_MS : ScFloor_MS(); break;
|
|
case ocSumProduct : ScSumProduct(); break;
|
|
case ocSumSQ : ScSumSQ(); break;
|
|
case ocSumX2MY2 : ScSumX2MY2(); break;
|
|
case ocSumX2DY2 : ScSumX2DY2(); break;
|
|
case ocSumXMY2 : ScSumXMY2(); break;
|
|
case ocLog : ScLog(); break;
|
|
case ocGCD : ScGCD(); break;
|
|
case ocLCM : ScLCM(); break;
|
|
case ocGetDate : ScGetDate(); break;
|
|
case ocGetTime : ScGetTime(); break;
|
|
case ocGetDiffDate : ScGetDiffDate(); break;
|
|
case ocGetDiffDate360 : ScGetDiffDate360(); break;
|
|
case ocGetDateDif : ScGetDateDif(); break;
|
|
case ocMin : ScMin( false ); break;
|
|
case ocMinA : ScMin( true ); break;
|
|
case ocMax : ScMax( false ); break;
|
|
case ocMaxA : ScMax( true ); break;
|
|
case ocSum : ScSum(); break;
|
|
case ocProduct : ScProduct(); break;
|
|
case ocNPV : ScNPV(); break;
|
|
case ocIRR : ScIRR(); break;
|
|
case ocMIRR : ScMIRR(); break;
|
|
case ocISPMT : ScISPMT(); break;
|
|
case ocAverage : ScAverage( false ); break;
|
|
case ocAverageA : ScAverage( true ); break;
|
|
case ocCount : ScCount(); break;
|
|
case ocCount2 : ScCount2(); break;
|
|
case ocVar :
|
|
case ocVarS : ScVar( false ); break;
|
|
case ocVarA : ScVar( true ); break;
|
|
case ocVarP :
|
|
case ocVarP_MS : ScVarP( false ); break;
|
|
case ocVarPA : ScVarP( true ); break;
|
|
case ocStDev :
|
|
case ocStDevS : ScStDev( false ); break;
|
|
case ocStDevA : ScStDev( true ); break;
|
|
case ocStDevP :
|
|
case ocStDevP_MS : ScStDevP( false ); break;
|
|
case ocStDevPA : ScStDevP( true ); break;
|
|
case ocBW : ScBW(); break;
|
|
case ocDIA : ScDIA(); break;
|
|
case ocGDA : ScGDA(); break;
|
|
case ocGDA2 : ScGDA2(); break;
|
|
case ocVBD : ScVDB(); break;
|
|
case ocLaufz : ScLaufz(); break;
|
|
case ocLIA : ScLIA(); break;
|
|
case ocRMZ : ScRMZ(); break;
|
|
case ocColumns : ScColumns(); break;
|
|
case ocRows : ScRows(); break;
|
|
case ocTables : ScTables(); break;
|
|
case ocColumn : ScColumn(); break;
|
|
case ocRow : ScRow(); break;
|
|
case ocTable : ScTable(); break;
|
|
case ocZGZ : ScZGZ(); break;
|
|
case ocZW : ScZW(); break;
|
|
case ocZZR : ScZZR(); break;
|
|
case ocZins : ScZins(); break;
|
|
case ocFilterXML : ScFilterXML(); break;
|
|
case ocWebservice : ScWebservice(); break;
|
|
case ocColor : ScColor(); break;
|
|
case ocErf_MS : ScErf(); break;
|
|
case ocErfc_MS : ScErfc(); break;
|
|
case ocZinsZ : ScZinsZ(); break;
|
|
case ocKapz : ScKapz(); break;
|
|
case ocKumZinsZ : ScKumZinsZ(); break;
|
|
case ocKumKapZ : ScKumKapZ(); break;
|
|
case ocEffektiv : ScEffektiv(); break;
|
|
case ocNominal : ScNominal(); break;
|
|
case ocSubTotal : ScSubTotal(); break;
|
|
case ocAggregate : ScAggregate(); break;
|
|
case ocDBSum : ScDBSum(); break;
|
|
case ocDBCount : ScDBCount(); break;
|
|
case ocDBCount2 : ScDBCount2(); break;
|
|
case ocDBAverage : ScDBAverage(); break;
|
|
case ocDBGet : ScDBGet(); break;
|
|
case ocDBMax : ScDBMax(); break;
|
|
case ocDBMin : ScDBMin(); break;
|
|
case ocDBProduct : ScDBProduct(); break;
|
|
case ocDBStdDev : ScDBStdDev(); break;
|
|
case ocDBStdDevP : ScDBStdDevP(); break;
|
|
case ocDBVar : ScDBVar(); break;
|
|
case ocDBVarP : ScDBVarP(); break;
|
|
case ocIndirect : ScIndirect(); break;
|
|
case ocAddress : ScAddressFunc(); break;
|
|
case ocMatch : ScMatch(); break;
|
|
case ocCountEmptyCells : ScCountEmptyCells(); break;
|
|
case ocCountIf : ScCountIf(); break;
|
|
case ocSumIf : ScSumIf(); break;
|
|
case ocAverageIf : ScAverageIf(); break;
|
|
case ocSumIfs : ScSumIfs(); break;
|
|
case ocAverageIfs : ScAverageIfs(); break;
|
|
case ocCountIfs : ScCountIfs(); break;
|
|
case ocLookup : ScLookup(); break;
|
|
case ocVLookup : ScVLookup(); break;
|
|
case ocHLookup : ScHLookup(); break;
|
|
case ocIndex : ScIndex(); break;
|
|
case ocMultiArea : ScMultiArea(); break;
|
|
case ocOffset : ScOffset(); break;
|
|
case ocAreas : ScAreas(); break;
|
|
case ocCurrency : ScCurrency(); break;
|
|
case ocReplace : ScReplace(); break;
|
|
case ocFixed : ScFixed(); break;
|
|
case ocFind : ScFind(); break;
|
|
case ocExact : ScExact(); break;
|
|
case ocLeft : ScLeft(); break;
|
|
case ocRight : ScRight(); break;
|
|
case ocSearch : ScSearch(); break;
|
|
case ocMid : ScMid(); break;
|
|
case ocText : ScText(); break;
|
|
case ocSubstitute : ScSubstitute(); break;
|
|
case ocRept : ScRept(); break;
|
|
case ocConcat : ScConcat(); break;
|
|
case ocMatValue : ScMatValue(); break;
|
|
case ocMatrixUnit : ScEMat(); break;
|
|
case ocMatDet : ScMatDet(); break;
|
|
case ocMatInv : ScMatInv(); break;
|
|
case ocMatMult : ScMatMult(); break;
|
|
case ocMatTrans : ScMatTrans(); break;
|
|
case ocMatRef : ScMatRef(); break;
|
|
case ocB : ScB(); break;
|
|
case ocNormDist : ScNormDist( 3 ); break;
|
|
case ocNormDist_MS : ScNormDist( 4 ); break;
|
|
case ocExpDist :
|
|
case ocExpDist_MS : ScExpDist(); break;
|
|
case ocBinomDist :
|
|
case ocBinomDist_MS : ScBinomDist(); break;
|
|
case ocPoissonDist :
|
|
case ocPoissonDist_MS : ScPoissonDist(); break;
|
|
case ocKombin : ScKombin(); break;
|
|
case ocKombin2 : ScKombin2(); break;
|
|
case ocVariationen : ScVariationen(); break;
|
|
case ocVariationen2 : ScVariationen2(); break;
|
|
case ocHypGeomDist : ScHypGeomDist(); break;
|
|
case ocHypGeomDist_MS : ScHypGeomDist_MS(); break;
|
|
case ocLogNormDist : ScLogNormDist( 1 ); break;
|
|
case ocLogNormDist_MS : ScLogNormDist( 4 ); break;
|
|
case ocTDist : ScTDist(); break;
|
|
case ocTDist_MS : ScTDist_MS(); break;
|
|
case ocTDist_RT : ScTDist_T( 1 ); break;
|
|
case ocTDist_2T : ScTDist_T( 2 ); break;
|
|
case ocFDist :
|
|
case ocFDist_RT : ScFDist(); break;
|
|
case ocFDist_LT : ScFDist_LT(); break;
|
|
case ocChiDist :
|
|
case ocChiDist_MS : ScChiDist(); break;
|
|
case ocChiSqDist : ScChiSqDist(); break;
|
|
case ocChiSqDist_MS : ScChiSqDist_MS(); break;
|
|
case ocStandard : ScStandard(); break;
|
|
case ocAveDev : ScAveDev(); break;
|
|
case ocDevSq : ScDevSq(); break;
|
|
case ocKurt : ScKurt(); break;
|
|
case ocSchiefe : ScSkew(); break;
|
|
case ocSkewp : ScSkewp(); break;
|
|
case ocModalValue : ScModalValue(); break;
|
|
case ocModalValue_MS : ScModalValue(); break;
|
|
case ocModalValue_Multi : ScModalValue_Multi(); break;
|
|
case ocMedian : ScMedian(); break;
|
|
case ocGeoMean : ScGeoMean(); break;
|
|
case ocHarMean : ScHarMean(); break;
|
|
case ocWeibull :
|
|
case ocWeibull_MS : ScWeibull(); break;
|
|
case ocBinomInv :
|
|
case ocKritBinom : ScCritBinom(); break;
|
|
case ocNegBinomVert : ScNegBinomDist(); break;
|
|
case ocNegBinomDist_MS : ScNegBinomDist_MS(); break;
|
|
case ocNoName : ScNoName(); break;
|
|
case ocBad : ScBadName(); break;
|
|
case ocZTest :
|
|
case ocZTest_MS : ScZTest(); break;
|
|
case ocTTest :
|
|
case ocTTest_MS : ScTTest(); break;
|
|
case ocFTest :
|
|
case ocFTest_MS : ScFTest(); break;
|
|
case ocRank :
|
|
case ocRank_Eq : ScRank( false ); break;
|
|
case ocRank_Avg : ScRank( true ); break;
|
|
case ocPercentile :
|
|
case ocPercentile_Inc : ScPercentile( true ); break;
|
|
case ocPercentile_Exc : ScPercentile( false ); break;
|
|
case ocPercentrank :
|
|
case ocPercentrank_Inc : ScPercentrank( true ); break;
|
|
case ocPercentrank_Exc : ScPercentrank( false ); break;
|
|
case ocLarge : ScLarge(); break;
|
|
case ocSmall : ScSmall(); break;
|
|
case ocFrequency : ScFrequency(); break;
|
|
case ocQuartile :
|
|
case ocQuartile_Inc : ScQuartile( true ); break;
|
|
case ocQuartile_Exc : ScQuartile( false ); break;
|
|
case ocNormInv :
|
|
case ocNormInv_MS : ScNormInv(); break;
|
|
case ocSNormInv :
|
|
case ocSNormInv_MS : ScSNormInv(); break;
|
|
case ocConfidence :
|
|
case ocConfidence_N : ScConfidence(); break;
|
|
case ocConfidence_T : ScConfidenceT(); break;
|
|
case ocTrimMean : ScTrimMean(); break;
|
|
case ocProb : ScProbability(); break;
|
|
case ocCorrel : ScCorrel(); break;
|
|
case ocCovar :
|
|
case ocCovarianceP : ScCovarianceP(); break;
|
|
case ocCovarianceS : ScCovarianceS(); break;
|
|
case ocPearson : ScPearson(); break;
|
|
case ocRSQ : ScRSQ(); break;
|
|
case ocSTEYX : ScSTEXY(); break;
|
|
case ocSlope : ScSlope(); break;
|
|
case ocIntercept : ScIntercept(); break;
|
|
case ocTrend : ScTrend(); break;
|
|
case ocGrowth : ScGrowth(); break;
|
|
case ocRGP : ScRGP(); break;
|
|
case ocRKP : ScRKP(); break;
|
|
case ocForecast : ScForecast(); break;
|
|
case ocGammaLn :
|
|
case ocGammaLn_MS : ScLogGamma(); break;
|
|
case ocGamma : ScGamma(); break;
|
|
case ocGammaDist : ScGammaDist( 3 ); break;
|
|
case ocGammaDist_MS : ScGammaDist( 4 ); break;
|
|
case ocGammaInv :
|
|
case ocGammaInv_MS : ScGammaInv(); break;
|
|
case ocChiTest :
|
|
case ocChiTest_MS : ScChiTest(); break;
|
|
case ocChiInv :
|
|
case ocChiInv_MS : ScChiInv(); break;
|
|
case ocChiSqInv :
|
|
case ocChiSqInv_MS : ScChiSqInv(); break;
|
|
case ocTInv :
|
|
case ocTInv_2T : ScTInv( 2 ); break;
|
|
case ocTInv_MS : ScTInv( 4 ); break;
|
|
case ocFInv :
|
|
case ocFInv_RT : ScFInv(); break;
|
|
case ocFInv_LT : ScFInv_LT(); break;
|
|
case ocLogInv :
|
|
case ocLogInv_MS : ScLogNormInv(); break;
|
|
case ocBetaDist : ScBetaDist(); break;
|
|
case ocBetaDist_MS : ScBetaDist_MS(); break;
|
|
case ocBetaInv :
|
|
case ocBetaInv_MS : ScBetaInv(); break;
|
|
case ocExternal : ScExternal(); break;
|
|
case ocTableOp : ScTableOp(); break;
|
|
case ocStop : break;
|
|
case ocErrorType : ScErrorType(); break;
|
|
case ocCurrent : ScCurrent(); break;
|
|
case ocStyle : ScStyle(); break;
|
|
case ocDde : ScDde(); break;
|
|
case ocBase : ScBase(); break;
|
|
case ocDecimal : ScDecimal(); break;
|
|
case ocConvert : ScConvert(); break;
|
|
case ocEuroConvert : ScEuroConvert(); break;
|
|
case ocRoman : ScRoman(); break;
|
|
case ocArabic : ScArabic(); break;
|
|
case ocInfo : ScInfo(); break;
|
|
case ocHyperLink : ScHyperLink(); break;
|
|
case ocBahtText : ScBahtText(); break;
|
|
case ocGetPivotData : ScGetPivotData(); break;
|
|
case ocJis : ScJis(); break;
|
|
case ocAsc : ScAsc(); break;
|
|
case ocLenB : ScLenB(); break;
|
|
case ocRightB : ScRightB(); break;
|
|
case ocLeftB : ScLeftB(); break;
|
|
case ocMidB : ScMidB(); break;
|
|
case ocUnicode : ScUnicode(); break;
|
|
case ocUnichar : ScUnichar(); break;
|
|
case ocBitAnd : ScBitAnd(); break;
|
|
case ocBitOr : ScBitOr(); break;
|
|
case ocBitXor : ScBitXor(); break;
|
|
case ocBitRshift : ScBitRshift(); break;
|
|
case ocBitLshift : ScBitLshift(); break;
|
|
case ocTTT : ScTTT(); break;
|
|
case ocDebugVar : ScDebugVar(); break;
|
|
case ocNone : nFuncFmtType = NUMBERFORMAT_UNDEFINED; break;
|
|
default : PushError( errUnknownOpCode); break;
|
|
}
|
|
|
|
// If the function pushed a subroutine as result, continue with
|
|
// execution of the subroutine.
|
|
if (sp > nStackBase && pStack[sp-1]->GetOpCode() == ocCall
|
|
/* && pStack[sp-1]->GetType() == svSubroutine */)
|
|
{
|
|
Pop(); continue;
|
|
}
|
|
|
|
if (FormulaCompiler::IsOpCodeVolatile(eOp))
|
|
meVolatileType = VOLATILE;
|
|
|
|
// Remember result matrix in case it could be reused.
|
|
if (pTokenMatrixMap && sp && GetStackType() == svMatrix)
|
|
pTokenMatrixMap->insert( ScTokenMatrixMap::value_type( pCur,
|
|
pStack[sp-1]));
|
|
|
|
// outer function determines format of an expression
|
|
if ( nFuncFmtType != NUMBERFORMAT_UNDEFINED )
|
|
{
|
|
nRetTypeExpr = nFuncFmtType;
|
|
// inherit the format index only for currency formats
|
|
nRetIndexExpr = ( nFuncFmtType == NUMBERFORMAT_CURRENCY ?
|
|
nFuncFmtIndex : 0 );
|
|
}
|
|
}
|
|
|
|
// Need a clean stack environment for the JumpMatrix to work.
|
|
if (nGlobalError && eOp != ocPush && sp > nStackBase + 1)
|
|
{
|
|
// Not all functions pop all parameters in case an error is
|
|
// generated. Clean up stack. Assumes that every function pushes a
|
|
// result, may be arbitrary in case of error.
|
|
const FormulaToken* pLocalResult = pStack[ sp - 1 ];
|
|
while (sp > nStackBase)
|
|
Pop();
|
|
PushTempToken( *pLocalResult );
|
|
}
|
|
|
|
bool bGotResult;
|
|
do
|
|
{
|
|
bGotResult = false;
|
|
sal_uInt8 nLevel = 0;
|
|
if ( GetStackType( ++nLevel ) == svJumpMatrix )
|
|
; // nothing
|
|
else if ( GetStackType( ++nLevel ) == svJumpMatrix )
|
|
; // nothing
|
|
else
|
|
nLevel = 0;
|
|
if ( nLevel == 1 || (nLevel == 2 && aCode.IsEndOfPath()) )
|
|
bGotResult = JumpMatrix( nLevel );
|
|
else
|
|
pJumpMatrix = NULL;
|
|
} while ( bGotResult );
|
|
|
|
// Functions that evaluate an error code and directly set nGlobalError to 0,
|
|
// usage: switch( OpCode ) { CASE_OCERRFUNC statements; }
|
|
#define CASE_OCERRFUNC \
|
|
case ocCount : \
|
|
case ocCount2 : \
|
|
case ocErrorType : \
|
|
case ocIsEmpty : \
|
|
case ocIsErr : \
|
|
case ocIsError : \
|
|
case ocIsFormula : \
|
|
case ocIsLogical : \
|
|
case ocIsNA : \
|
|
case ocIsNonString : \
|
|
case ocIsRef : \
|
|
case ocIsString : \
|
|
case ocIsValue : \
|
|
case ocN : \
|
|
case ocType : \
|
|
case ocIfError : \
|
|
case ocIfNA :
|
|
|
|
switch ( eOp )
|
|
{
|
|
CASE_OCERRFUNC
|
|
++ nErrorFunction;
|
|
default:
|
|
; // nothing
|
|
}
|
|
if ( nGlobalError )
|
|
{
|
|
if ( !nErrorFunctionCount )
|
|
{ // count of errorcode functions in formula
|
|
for ( FormulaToken* t = rArr.FirstRPN(); t; t = rArr.NextRPN() )
|
|
{
|
|
switch ( t->GetOpCode() )
|
|
{
|
|
CASE_OCERRFUNC
|
|
++nErrorFunctionCount;
|
|
default:
|
|
; // nothing
|
|
}
|
|
}
|
|
}
|
|
if ( nErrorFunction >= nErrorFunctionCount )
|
|
++nErrorFunction; // that's it, error => terminate
|
|
}
|
|
}
|
|
|
|
// End: obtain result
|
|
|
|
if( sp )
|
|
{
|
|
pCur = pStack[ sp-1 ];
|
|
if( pCur->GetOpCode() == ocPush )
|
|
{
|
|
switch( pCur->GetType() )
|
|
{
|
|
case svEmptyCell:
|
|
; // nothing
|
|
break;
|
|
case svError:
|
|
nGlobalError = pCur->GetError();
|
|
break;
|
|
case svDouble :
|
|
if ( nFuncFmtType == NUMBERFORMAT_UNDEFINED )
|
|
{
|
|
nRetTypeExpr = NUMBERFORMAT_NUMBER;
|
|
nRetIndexExpr = 0;
|
|
}
|
|
break;
|
|
case svString :
|
|
nRetTypeExpr = NUMBERFORMAT_TEXT;
|
|
nRetIndexExpr = 0;
|
|
break;
|
|
case svSingleRef :
|
|
{
|
|
ScAddress aAdr;
|
|
PopSingleRef( aAdr );
|
|
if( !nGlobalError )
|
|
PushCellResultToken( false, aAdr,
|
|
&nRetTypeExpr, &nRetIndexExpr);
|
|
}
|
|
break;
|
|
case svRefList :
|
|
PopError(); // maybe #REF! takes precedence over #VALUE!
|
|
PushError( errNoValue);
|
|
break;
|
|
case svDoubleRef :
|
|
{
|
|
if ( bMatrixFormula )
|
|
{ // create matrix for {=A1:A5}
|
|
PopDoubleRefPushMatrix();
|
|
ScMatrixRef xMat = PopMatrix();
|
|
QueryMatrixType(xMat, nRetTypeExpr, nRetIndexExpr);
|
|
}
|
|
else
|
|
{
|
|
ScRange aRange;
|
|
PopDoubleRef( aRange );
|
|
ScAddress aAdr;
|
|
if ( !nGlobalError && DoubleRefToPosSingleRef( aRange, aAdr))
|
|
PushCellResultToken( false, aAdr,
|
|
&nRetTypeExpr, &nRetIndexExpr);
|
|
}
|
|
}
|
|
break;
|
|
case svExternalDoubleRef:
|
|
{
|
|
ScMatrixRef xMat;
|
|
PopExternalDoubleRef(xMat);
|
|
QueryMatrixType(xMat, nRetTypeExpr, nRetIndexExpr);
|
|
}
|
|
break;
|
|
case svMatrix :
|
|
{
|
|
sc::RangeMatrix aMat = PopRangeMatrix();
|
|
if (aMat.isRangeValid())
|
|
{
|
|
// This matrix represents a range reference. Apply implicit intersection.
|
|
double fVal = applyImplicitIntersection(aMat, aPos);
|
|
if (rtl::math::isNan(fVal))
|
|
PushNoValue();
|
|
else
|
|
PushInt(fVal);
|
|
}
|
|
else
|
|
// This is a normal matrix.
|
|
QueryMatrixType(aMat.mpMat, nRetTypeExpr, nRetIndexExpr);
|
|
}
|
|
break;
|
|
case svExternalSingleRef:
|
|
{
|
|
ScExternalRefCache::TokenRef pToken;
|
|
ScExternalRefCache::CellFormat aFmt;
|
|
PopExternalSingleRef(pToken, &aFmt);
|
|
if (nGlobalError)
|
|
break;
|
|
|
|
PushTempToken(*pToken);
|
|
|
|
if (aFmt.mbIsSet)
|
|
{
|
|
nFuncFmtType = aFmt.mnType;
|
|
nFuncFmtIndex = aFmt.mnIndex;
|
|
}
|
|
}
|
|
break;
|
|
default :
|
|
SetError( errUnknownStackVariable);
|
|
}
|
|
}
|
|
else
|
|
SetError( errUnknownStackVariable);
|
|
}
|
|
else
|
|
SetError( errNoCode);
|
|
|
|
if( nRetTypeExpr != NUMBERFORMAT_UNDEFINED )
|
|
{
|
|
nRetFmtType = nRetTypeExpr;
|
|
nRetFmtIndex = nRetIndexExpr;
|
|
}
|
|
else if( nFuncFmtType != NUMBERFORMAT_UNDEFINED )
|
|
{
|
|
nRetFmtType = nFuncFmtType;
|
|
nRetFmtIndex = nFuncFmtIndex;
|
|
}
|
|
else
|
|
nRetFmtType = NUMBERFORMAT_NUMBER;
|
|
|
|
if (nGlobalError && GetStackType() != svError )
|
|
PushError( nGlobalError);
|
|
|
|
// THE final result.
|
|
xResult = PopToken();
|
|
if (!xResult)
|
|
xResult = new FormulaErrorToken( errUnknownStackVariable);
|
|
|
|
// release tokens in expression stack
|
|
FormulaToken** p = pStack;
|
|
while( maxsp-- )
|
|
(*p++)->DecRef();
|
|
|
|
StackVar eType = xResult->GetType();
|
|
if (eType == svMatrix)
|
|
// Results are immutable in case they would be reused as input for new
|
|
// interpreters.
|
|
static_cast<ScToken*>(xResult.get())->GetMatrix()->SetImmutable( true);
|
|
return eType;
|
|
}
|
|
|
|
svl::SharedString ScInterpreter::GetStringResult() const
|
|
{
|
|
return xResult->GetString();
|
|
}
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|