1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <interpre.hxx>
22 #include <scitems.hxx>
23 #include <editeng/langitem.hxx>
24 #include <editeng/justifyitem.hxx>
25 #include <o3tl/safeint.hxx>
26 #include <o3tl/temporary.hxx>
27 #include <osl/thread.h>
28 #include <unotools/textsearch.hxx>
29 #include <svl/numformat.hxx>
30 #include <svl/zforlist.hxx>
31 #include <svl/zformat.hxx>
32 #include <tools/urlobj.hxx>
33 #include <unotools/charclass.hxx>
34 #include <sfx2/docfile.hxx>
35 #include <sfx2/printer.hxx>
36 #include <unotools/collatorwrapper.hxx>
37 #include <unotools/transliterationwrapper.hxx>
38 #include <rtl/character.hxx>
39 #include <rtl/ustring.hxx>
40 #include <sal/log.hxx>
41 #include <osl/diagnose.h>
42 #include <unicode/uchar.h>
43 #include <unicode/regex.h>
44 #include <i18nlangtag/mslangid.hxx>
46 #include <patattr.hxx>
48 #include <document.hxx>
49 #include <dociter.hxx>
51 #include <sfx2/linkmgr.hxx>
52 #include <formulacell.hxx>
53 #include <scmatrix.hxx>
54 #include <docoptio.hxx>
56 #include <jumpmatrix.hxx>
57 #include <cellkeytranslator.hxx>
58 #include <lookupcache.hxx>
59 #include <rangenam.hxx>
60 #include <rangeutl.hxx>
61 #include <compiler.hxx>
62 #include <externalrefmgr.hxx>
63 #include <doubleref.hxx>
64 #include <queryparam.hxx>
65 #include <queryiter.hxx>
66 #include <tokenarray.hxx>
67 #include <compare.hxx>
68 #include <comphelper/lok.hxx>
69 #include <comphelper/processfactory.hxx>
70 #include <comphelper/string.hxx>
71 #include <svl/sharedstringpool.hxx>
77 #include <string_view>
80 const sal_uInt64 n2power48
= SAL_CONST_UINT64( 281474976710656); // 2^48
82 ScCalcConfig
*ScInterpreter::mpGlobalConfig
= nullptr;
84 using namespace formula
;
85 using ::std::unique_ptr
;
87 void ScInterpreter::ScIfJump()
89 const short* pJump
= pCur
->GetJump();
90 short nJumpCount
= pJump
[ 0 ];
91 MatrixJumpConditionToMatrix();
92 switch ( GetStackType() )
96 ScMatrixRef pMat
= PopMatrix();
98 PushIllegalParameter();
101 FormulaConstTokenRef xNew
;
102 ScTokenMatrixMap::const_iterator aMapIter
;
103 // DoubleError handled by JumpMatrix
104 pMat
->SetErrorInterpreter( nullptr);
106 pMat
->GetDimensions( nCols
, nRows
);
107 if ( nCols
== 0 || nRows
== 0 )
109 PushIllegalArgument();
112 else if ((aMapIter
= maTokenMatrixMap
.find( pCur
)) != maTokenMatrixMap
.end())
113 xNew
= (*aMapIter
).second
;
116 std::shared_ptr
<ScJumpMatrix
> pJumpMat( std::make_shared
<ScJumpMatrix
>(
117 pCur
->GetOpCode(), nCols
, nRows
));
118 for ( SCSIZE nC
=0; nC
< nCols
; ++nC
)
120 for ( SCSIZE nR
=0; nR
< nRows
; ++nR
)
124 bool bIsValue
= pMat
->IsValue(nC
, nR
);
127 fVal
= pMat
->GetDouble(nC
, nR
);
128 bIsValue
= std::isfinite(fVal
);
129 bTrue
= bIsValue
&& (fVal
!= 0.0);
135 // Treat empty and empty path as 0, but string
136 // as error. ScMatrix::IsValueOrEmpty() returns
137 // true for any empty, empty path, empty cell,
139 bIsValue
= pMat
->IsValueOrEmpty(nC
, nR
);
141 fVal
= (bIsValue
? 0.0 : CreateDoubleError( FormulaError::NoValue
));
145 if( nJumpCount
>= 2 )
147 pJumpMat
->SetJump( nC
, nR
, fVal
,
149 pJump
[ nJumpCount
]);
152 { // no parameter given for THEN
153 pJumpMat
->SetJump( nC
, nR
, fVal
,
155 pJump
[ nJumpCount
]);
160 if( nJumpCount
== 3 && bIsValue
)
162 pJumpMat
->SetJump( nC
, nR
, fVal
,
164 pJump
[ nJumpCount
]);
167 { // no parameter given for ELSE,
169 pJumpMat
->SetJump( nC
, nR
, fVal
,
171 pJump
[ nJumpCount
]);
176 xNew
= new ScJumpMatrixToken( pJumpMat
);
177 GetTokenMatrixMap().emplace(pCur
, xNew
);
181 PushIllegalArgument();
185 // set endpoint of path for main code line
186 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
192 const bool bCondition
= GetBool();
193 if (nGlobalError
!= FormulaError::NONE
)
194 { // Propagate error, not THEN- or ELSE-path, jump behind.
195 PushError(nGlobalError
);
196 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
198 else if ( bCondition
)
200 if( nJumpCount
>= 2 )
202 aCode
.Jump( pJump
[ 1 ], pJump
[ nJumpCount
] );
205 { // no parameter given for THEN
206 nFuncFmtType
= SvNumFormatType::LOGICAL
;
208 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
213 if( nJumpCount
== 3 )
215 aCode
.Jump( pJump
[ 2 ], pJump
[ nJumpCount
] );
218 { // no parameter given for ELSE
219 nFuncFmtType
= SvNumFormatType::LOGICAL
;
221 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
228 /** Store a matrix value in another matrix in the context of that other matrix
229 is the result matrix of a jump matrix. All arguments must be valid and are
231 static void lcl_storeJumpMatResult(
232 const ScMatrix
* pMat
, ScJumpMatrix
* pJumpMat
, SCSIZE nC
, SCSIZE nR
)
234 if ( pMat
->IsValue( nC
, nR
) )
236 double fVal
= pMat
->GetDouble( nC
, nR
);
237 pJumpMat
->PutResultDouble( fVal
, nC
, nR
);
239 else if ( pMat
->IsEmpty( nC
, nR
) )
241 pJumpMat
->PutResultEmpty( nC
, nR
);
245 pJumpMat
->PutResultString(pMat
->GetString(nC
, nR
), nC
, nR
);
249 void ScInterpreter::ScIfError( bool bNAonly
)
251 const short* pJump
= pCur
->GetJump();
252 short nJumpCount
= pJump
[ 0 ];
253 if (!sp
|| nJumpCount
!= 2)
255 // Reset nGlobalError here to not propagate the old error, if any.
256 nGlobalError
= (sp
? FormulaError::ParameterExpected
: FormulaError::UnknownStackVariable
);
257 PushError( nGlobalError
);
258 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
262 FormulaConstTokenRef
xToken( pStack
[ sp
- 1 ] );
264 FormulaError nOldGlobalError
= nGlobalError
;
265 nGlobalError
= FormulaError::NONE
;
267 MatrixJumpConditionToMatrix();
268 switch (GetStackType())
272 // Act on implicitly propagated error, if any.
273 if (nOldGlobalError
!= FormulaError::NONE
)
274 nGlobalError
= nOldGlobalError
;
275 if (nGlobalError
!= FormulaError::NONE
)
286 if (!PopDoubleRefOrSingleRef( aAdr
))
291 ScRefCellValue
aCell(mrDoc
, aAdr
);
292 nGlobalError
= GetCellErrCode(aCell
);
293 if (nGlobalError
!= FormulaError::NONE
)
298 case svExternalSingleRef
:
299 case svExternalDoubleRef
:
302 svl::SharedString aStr
;
303 // Handles also existing jump matrix case and sets error on
305 GetDoubleOrStringFromMatrix( fVal
, aStr
);
306 if (nGlobalError
!= FormulaError::NONE
)
312 const ScMatrixRef pMat
= PopMatrix();
313 if (!pMat
|| (nGlobalError
!= FormulaError::NONE
&& (!bNAonly
|| nGlobalError
== FormulaError::NotAvailable
)))
318 // If the matrix has no queried error at all we can simply use
319 // it as result and don't need to bother with jump matrix.
320 SCSIZE nErrorCol
= ::std::numeric_limits
<SCSIZE
>::max(),
321 nErrorRow
= ::std::numeric_limits
<SCSIZE
>::max();
323 pMat
->GetDimensions( nCols
, nRows
);
324 if (nCols
== 0 || nRows
== 0)
329 for (SCSIZE nC
=0; nC
< nCols
&& !bError
; ++nC
)
331 for (SCSIZE nR
=0; nR
< nRows
&& !bError
; ++nR
)
333 FormulaError nErr
= pMat
->GetError( nC
, nR
);
334 if (nErr
!= FormulaError::NONE
&& (!bNAonly
|| nErr
== FormulaError::NotAvailable
))
343 break; // switch, we're done and have the result
345 FormulaConstTokenRef xNew
;
346 ScTokenMatrixMap::const_iterator aMapIter
;
347 if ((aMapIter
= maTokenMatrixMap
.find( pCur
)) != maTokenMatrixMap
.end())
349 xNew
= (*aMapIter
).second
;
353 const ScMatrix
* pMatPtr
= pMat
.get();
354 std::shared_ptr
<ScJumpMatrix
> pJumpMat( std::make_shared
<ScJumpMatrix
>(
355 pCur
->GetOpCode(), nCols
, nRows
));
356 // Init all jumps to no error to save single calls. Error
357 // is the exceptional condition.
358 const double fFlagResult
= CreateDoubleError( FormulaError::JumpMatHasResult
);
359 pJumpMat
->SetAllJumps( fFlagResult
, pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
360 // Up to first error position simply store results, no need
361 // to evaluate error conditions again.
362 SCSIZE nC
= 0, nR
= 0;
363 for ( ; nC
< nCols
&& (nC
!= nErrorCol
|| nR
!= nErrorRow
); /*nop*/ )
365 for (nR
= 0 ; nR
< nRows
&& (nC
!= nErrorCol
|| nR
!= nErrorRow
); ++nR
)
367 lcl_storeJumpMatResult(pMatPtr
, pJumpMat
.get(), nC
, nR
);
369 if (nC
!= nErrorCol
&& nR
!= nErrorRow
)
372 // Now the mixed cases.
373 for ( ; nC
< nCols
; ++nC
)
375 for ( ; nR
< nRows
; ++nR
)
377 FormulaError nErr
= pMat
->GetError( nC
, nR
);
378 if (nErr
!= FormulaError::NONE
&& (!bNAonly
|| nErr
== FormulaError::NotAvailable
))
380 pJumpMat
->SetJump( nC
, nR
, 1.0, pJump
[ 1 ], pJump
[ nJumpCount
] );
383 { // FALSE, EMPTY path, store result instead
384 lcl_storeJumpMatResult(pMatPtr
, pJumpMat
.get(), nC
, nR
);
389 xNew
= new ScJumpMatrixToken( pJumpMat
);
390 GetTokenMatrixMap().emplace( pCur
, xNew
);
392 nGlobalError
= nOldGlobalError
;
393 PushTokenRef( xNew
);
394 // set endpoint of path for main code line
395 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
401 if (bError
&& (!bNAonly
|| nGlobalError
== FormulaError::NotAvailable
))
403 // error, calculate 2nd argument
404 nGlobalError
= FormulaError::NONE
;
405 aCode
.Jump( pJump
[ 1 ], pJump
[ nJumpCount
] );
409 // no error, push 1st argument and continue
410 nGlobalError
= nOldGlobalError
;
411 PushTokenRef( xToken
);
412 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
416 void ScInterpreter::ScChooseJump()
418 // We have to set a jump, if there was none chosen because of an error set
420 bool bHaveJump
= false;
421 const short* pJump
= pCur
->GetJump();
422 short nJumpCount
= pJump
[ 0 ];
423 MatrixJumpConditionToMatrix();
424 switch ( GetStackType() )
428 ScMatrixRef pMat
= PopMatrix();
430 PushIllegalParameter();
433 FormulaConstTokenRef xNew
;
434 ScTokenMatrixMap::const_iterator aMapIter
;
435 // DoubleError handled by JumpMatrix
436 pMat
->SetErrorInterpreter( nullptr);
438 pMat
->GetDimensions( nCols
, nRows
);
439 if ( nCols
== 0 || nRows
== 0 )
440 PushIllegalParameter();
441 else if ((aMapIter
= maTokenMatrixMap
.find(
442 pCur
)) != maTokenMatrixMap
.end())
443 xNew
= (*aMapIter
).second
;
446 std::shared_ptr
<ScJumpMatrix
> pJumpMat( std::make_shared
<ScJumpMatrix
>(
447 pCur
->GetOpCode(), nCols
, nRows
));
448 for ( SCSIZE nC
=0; nC
< nCols
; ++nC
)
450 for ( SCSIZE nR
=0; nR
< nRows
; ++nR
)
453 bool bIsValue
= pMat
->IsValue(nC
, nR
);
456 fVal
= pMat
->GetDouble(nC
, nR
);
457 bIsValue
= std::isfinite( fVal
);
460 fVal
= ::rtl::math::approxFloor( fVal
);
461 if ( (fVal
< 1) || (fVal
>= nJumpCount
))
464 fVal
= CreateDoubleError(
465 FormulaError::IllegalArgument
);
471 fVal
= CreateDoubleError( FormulaError::NoValue
);
475 pJumpMat
->SetJump( nC
, nR
, fVal
,
476 pJump
[ static_cast<short>(fVal
) ],
477 pJump
[ nJumpCount
]);
481 pJumpMat
->SetJump( nC
, nR
, fVal
,
483 pJump
[ nJumpCount
]);
487 xNew
= new ScJumpMatrixToken( pJumpMat
);
488 GetTokenMatrixMap().emplace(pCur
, xNew
);
493 // set endpoint of path for main code line
494 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
502 sal_Int16 nJumpIndex
= GetInt16();
503 if (nGlobalError
== FormulaError::NONE
&& (nJumpIndex
>= 1) && (nJumpIndex
< nJumpCount
))
505 aCode
.Jump( pJump
[ static_cast<short>(nJumpIndex
) ], pJump
[ nJumpCount
] );
509 PushIllegalArgument();
513 aCode
.Jump( pJump
[ nJumpCount
], pJump
[ nJumpCount
] );
516 static void lcl_AdjustJumpMatrix( ScJumpMatrix
* pJumpM
, SCSIZE nParmCols
, SCSIZE nParmRows
)
518 SCSIZE nJumpCols
, nJumpRows
;
519 SCSIZE nResCols
, nResRows
;
520 SCSIZE nAdjustCols
, nAdjustRows
;
521 pJumpM
->GetDimensions( nJumpCols
, nJumpRows
);
522 pJumpM
->GetResMatDimensions( nResCols
, nResRows
);
523 if (!(( nJumpCols
== 1 && nParmCols
> nResCols
) ||
524 ( nJumpRows
== 1 && nParmRows
> nResRows
)))
527 if ( nJumpCols
== 1 && nJumpRows
== 1 )
529 nAdjustCols
= std::max(nParmCols
, nResCols
);
530 nAdjustRows
= std::max(nParmRows
, nResRows
);
532 else if ( nJumpCols
== 1 )
534 nAdjustCols
= nParmCols
;
535 nAdjustRows
= nResRows
;
539 nAdjustCols
= nResCols
;
540 nAdjustRows
= nParmRows
;
542 pJumpM
->SetNewResMat( nAdjustCols
, nAdjustRows
);
545 bool ScInterpreter::JumpMatrix( short nStackLevel
)
547 pJumpMatrix
= pStack
[sp
-nStackLevel
]->GetJumpMatrix();
548 bool bHasResMat
= pJumpMatrix
->HasResultMatrix();
550 if ( nStackLevel
== 2 )
552 if ( aCode
.HasStacked() )
553 aCode
.Pop(); // pop what Jump() pushed
556 assert(!"pop goes the weasel");
562 SetError( FormulaError::UnknownStackVariable
);
566 pJumpMatrix
->GetPos( nC
, nR
);
567 switch ( GetStackType() )
571 double fVal
= GetDouble();
572 if ( nGlobalError
!= FormulaError::NONE
)
574 fVal
= CreateDoubleError( nGlobalError
);
575 nGlobalError
= FormulaError::NONE
;
577 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
582 svl::SharedString aStr
= GetString();
583 if ( nGlobalError
!= FormulaError::NONE
)
585 pJumpMatrix
->PutResultDouble( CreateDoubleError( nGlobalError
),
587 nGlobalError
= FormulaError::NONE
;
590 pJumpMatrix
->PutResultString(aStr
, nC
, nR
);
595 FormulaConstTokenRef xRef
= pStack
[sp
-1];
597 PopSingleRef( aAdr
);
598 if ( nGlobalError
!= FormulaError::NONE
)
600 pJumpMatrix
->PutResultDouble( CreateDoubleError( nGlobalError
),
602 nGlobalError
= FormulaError::NONE
;
606 ScRefCellValue
aCell(mrDoc
, aAdr
);
607 if (aCell
.hasEmptyValue())
608 pJumpMatrix
->PutResultEmpty( nC
, nR
);
609 else if (aCell
.hasNumeric())
611 double fVal
= GetCellValue(aAdr
, aCell
);
612 if ( nGlobalError
!= FormulaError::NONE
)
614 fVal
= CreateDoubleError(
616 nGlobalError
= FormulaError::NONE
;
618 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
622 svl::SharedString aStr
;
623 GetCellString(aStr
, aCell
);
624 if ( nGlobalError
!= FormulaError::NONE
)
626 pJumpMatrix
->PutResultDouble( CreateDoubleError(
627 nGlobalError
), nC
, nR
);
628 nGlobalError
= FormulaError::NONE
;
631 pJumpMatrix
->PutResultString(aStr
, nC
, nR
);
635 formula::ParamClass eReturnType
= ScParameterClassification::GetParameterType( pCur
, SAL_MAX_UINT16
);
636 if (eReturnType
== ParamClass::Reference
)
638 /* TODO: What about error handling and do we actually
639 * need the result matrix above at all in this case? */
640 ScComplexRefData aRef
;
641 aRef
.Ref1
= aRef
.Ref2
= *(xRef
->GetSingleRef());
642 pJumpMatrix
->GetRefList().push_back( aRef
);
647 { // upper left plus offset within matrix
648 FormulaConstTokenRef xRef
= pStack
[sp
-1];
651 PopDoubleRef( aRange
);
652 if ( nGlobalError
!= FormulaError::NONE
)
654 fVal
= CreateDoubleError( nGlobalError
);
655 nGlobalError
= FormulaError::NONE
;
656 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
660 // Do not modify the original range because we use it
661 // to adjust the size of the result matrix if necessary.
662 ScAddress
aAdr( aRange
.aStart
);
663 sal_uLong nCol
= static_cast<sal_uLong
>(aAdr
.Col()) + nC
;
664 sal_uLong nRow
= static_cast<sal_uLong
>(aAdr
.Row()) + nR
;
665 if ((nCol
> o3tl::make_unsigned(aRange
.aEnd
.Col()) &&
666 aRange
.aEnd
.Col() != aRange
.aStart
.Col())
667 || (nRow
> o3tl::make_unsigned(aRange
.aEnd
.Row()) &&
668 aRange
.aEnd
.Row() != aRange
.aStart
.Row()))
670 fVal
= CreateDoubleError( FormulaError::NotAvailable
);
671 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
675 // Replicate column and/or row of a vector if it is
676 // one. Note that this could be a range reference
677 // that in fact consists of only one cell, e.g. A1:A1
678 if (aRange
.aEnd
.Col() == aRange
.aStart
.Col())
679 nCol
= aRange
.aStart
.Col();
680 if (aRange
.aEnd
.Row() == aRange
.aStart
.Row())
681 nRow
= aRange
.aStart
.Row();
682 aAdr
.SetCol( static_cast<SCCOL
>(nCol
) );
683 aAdr
.SetRow( static_cast<SCROW
>(nRow
) );
684 ScRefCellValue
aCell(mrDoc
, aAdr
);
685 if (aCell
.hasEmptyValue())
686 pJumpMatrix
->PutResultEmpty( nC
, nR
);
687 else if (aCell
.hasNumeric())
689 double fCellVal
= GetCellValue(aAdr
, aCell
);
690 if ( nGlobalError
!= FormulaError::NONE
)
692 fCellVal
= CreateDoubleError(
694 nGlobalError
= FormulaError::NONE
;
696 pJumpMatrix
->PutResultDouble( fCellVal
, nC
, nR
);
700 svl::SharedString aStr
;
701 GetCellString(aStr
, aCell
);
702 if ( nGlobalError
!= FormulaError::NONE
)
704 pJumpMatrix
->PutResultDouble( CreateDoubleError(
705 nGlobalError
), nC
, nR
);
706 nGlobalError
= FormulaError::NONE
;
709 pJumpMatrix
->PutResultString(aStr
, nC
, nR
);
712 SCSIZE nParmCols
= aRange
.aEnd
.Col() - aRange
.aStart
.Col() + 1;
713 SCSIZE nParmRows
= aRange
.aEnd
.Row() - aRange
.aStart
.Row() + 1;
714 lcl_AdjustJumpMatrix( pJumpMatrix
, nParmCols
, nParmRows
);
717 formula::ParamClass eReturnType
= ScParameterClassification::GetParameterType( pCur
, SAL_MAX_UINT16
);
718 if (eReturnType
== ParamClass::Reference
)
720 /* TODO: What about error handling and do we actually
721 * need the result matrix above at all in this case? */
722 pJumpMatrix
->GetRefList().push_back( *(xRef
->GetDoubleRef()));
726 case svExternalSingleRef
:
728 ScExternalRefCache::TokenRef pToken
;
729 PopExternalSingleRef(pToken
);
730 if (nGlobalError
!= FormulaError::NONE
)
732 pJumpMatrix
->PutResultDouble( CreateDoubleError( nGlobalError
), nC
, nR
);
733 nGlobalError
= FormulaError::NONE
;
737 switch (pToken
->GetType())
740 pJumpMatrix
->PutResultDouble( pToken
->GetDouble(), nC
, nR
);
743 pJumpMatrix
->PutResultString( pToken
->GetString(), nC
, nR
);
746 pJumpMatrix
->PutResultEmpty( nC
, nR
);
749 // svError was already handled (set by
750 // PopExternalSingleRef()) with nGlobalError
752 assert(!"unhandled svExternalSingleRef case");
753 pJumpMatrix
->PutResultDouble( CreateDoubleError(
754 FormulaError::UnknownStackVariable
), nC
, nR
);
759 case svExternalDoubleRef
:
761 { // match matrix offsets
763 ScMatrixRef pMat
= GetMatrix();
764 if ( nGlobalError
!= FormulaError::NONE
)
766 fVal
= CreateDoubleError( nGlobalError
);
767 nGlobalError
= FormulaError::NONE
;
768 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
772 fVal
= CreateDoubleError( FormulaError::UnknownVariable
);
773 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
778 pMat
->GetDimensions( nCols
, nRows
);
779 if ((nCols
<= nC
&& nCols
!= 1) ||
780 (nRows
<= nR
&& nRows
!= 1))
782 fVal
= CreateDoubleError( FormulaError::NotAvailable
);
783 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
787 // GetMatrix() does SetErrorInterpreter() at the
788 // matrix, do not propagate an error from
789 // matrix->GetValue() as global error.
790 pMat
->SetErrorInterpreter(nullptr);
791 lcl_storeJumpMatResult(pMat
.get(), pJumpMatrix
, nC
, nR
);
793 lcl_AdjustJumpMatrix( pJumpMatrix
, nCols
, nRows
);
800 double fVal
= CreateDoubleError( nGlobalError
);
801 nGlobalError
= FormulaError::NONE
;
802 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
808 double fVal
= CreateDoubleError( FormulaError::IllegalArgument
);
809 pJumpMatrix
->PutResultDouble( fVal
, nC
, nR
);
814 bool bCont
= pJumpMatrix
->Next( nC
, nR
);
818 short nStart
, nNext
, nStop
;
819 pJumpMatrix
->GetJump( nC
, nR
, fBool
, nStart
, nNext
, nStop
);
820 while ( bCont
&& nStart
== nNext
)
821 { // push all results that have no jump path
822 if ( bHasResMat
&& (GetDoubleErrorValue( fBool
) != FormulaError::JumpMatHasResult
) )
824 // a false without path results in an empty path value
826 pJumpMatrix
->PutResultEmptyPath( nC
, nR
);
828 pJumpMatrix
->PutResultDouble( fBool
, nC
, nR
);
830 bCont
= pJumpMatrix
->Next( nC
, nR
);
832 pJumpMatrix
->GetJump( nC
, nR
, fBool
, nStart
, nNext
, nStop
);
834 if ( bCont
&& nStart
!= nNext
)
836 const ScTokenVec
& rParams
= pJumpMatrix
->GetJumpParameters();
837 for ( auto const & i
: rParams
)
839 // This is not the current state of the interpreter, so
840 // push without error, and elements' errors are coded into
842 PushWithoutError(*i
);
844 aCode
.Jump( nStart
, nNext
, nStop
);
848 { // We're done with it, throw away jump matrix, keep result.
849 // For an intermediate result of Reference use the array of references
850 // if there are more than one reference and the current ForceArray
851 // context is ReferenceOrRefArray.
852 // Else (also for a final result of Reference) use the matrix.
853 // Treat the result of a jump command as final and use the matrix (see
854 // tdf#115493 for why).
855 if (pCur
->GetInForceArray() == ParamClass::ReferenceOrRefArray
&&
856 pJumpMatrix
->GetRefList().size() > 1 &&
857 ScParameterClassification::GetParameterType( pCur
, SAL_MAX_UINT16
) == ParamClass::Reference
&&
858 !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix
->GetOpCode()) &&
859 aCode
.PeekNextOperator())
861 FormulaTokenRef xRef
= new ScRefListToken(true);
862 *(xRef
->GetRefList()) = pJumpMatrix
->GetRefList();
863 pJumpMatrix
= nullptr;
866 maTokenMatrixMap
.erase( pCur
);
867 // There's no result matrix to remember in this case.
871 ScMatrix
* pResMat
= pJumpMatrix
->GetResultMatrix();
872 pJumpMatrix
= nullptr;
874 PushMatrix( pResMat
);
875 // Remove jump matrix from map and remember result matrix in case it
876 // could be reused in another path of the same condition.
877 maTokenMatrixMap
.erase( pCur
);
878 maTokenMatrixMap
.emplace(pCur
, pStack
[sp
-1]);
885 double ScInterpreter::Compare( ScQueryOp eOp
)
889 aComp
.mbIgnoreCase
= mrDoc
.GetDocOptions().IsIgnoreCase();
890 for( short i
= 1; i
>= 0; i
-- )
892 sc::Compare::Cell
& rCell
= aComp
.maCells
[i
];
894 switch ( GetRawStackType() )
898 rCell
.mbEmpty
= true;
902 rCell
.mfValue
= GetDouble();
903 rCell
.mbValue
= true;
906 rCell
.maStr
= GetString();
907 rCell
.mbValue
= false;
913 if ( !PopDoubleRefOrSingleRef( aAdr
) )
915 ScRefCellValue
aCell(mrDoc
, aAdr
);
916 if (aCell
.hasEmptyValue())
917 rCell
.mbEmpty
= true;
918 else if (aCell
.hasString())
920 svl::SharedString aStr
;
921 GetCellString(aStr
, aCell
);
923 rCell
.mbValue
= false;
927 rCell
.mfValue
= GetCellValue(aAdr
, aCell
);
928 rCell
.mbValue
= true;
932 case svExternalSingleRef
:
934 ScMatrixRef pMat
= GetMatrix();
937 SetError( FormulaError::IllegalParameter
);
942 pMat
->GetDimensions(nC
, nR
);
945 SetError( FormulaError::IllegalParameter
);
948 if (pMat
->IsEmpty(0, 0))
949 rCell
.mbEmpty
= true;
950 else if (pMat
->IsStringOrEmpty(0, 0))
952 rCell
.maStr
= pMat
->GetString(0, 0);
953 rCell
.mbValue
= false;
957 rCell
.mfValue
= pMat
->GetDouble(0, 0);
958 rCell
.mbValue
= true;
962 case svExternalDoubleRef
:
963 // TODO: Find out how to handle this...
964 // Xcl generates a position dependent intersection using
965 // col/row, as it seems to do for all range references, not
966 // only in compare context. We'd need a general implementation
967 // for that behavior similar to svDoubleRef in scalar and array
968 // mode. Which also means we'd have to change all places where
969 // it currently is handled along with svMatrix.
972 SetError( FormulaError::IllegalParameter
);
976 if( nGlobalError
!= FormulaError::NONE
)
978 nCurFmtType
= nFuncFmtType
= SvNumFormatType::LOGICAL
;
979 return sc::CompareFunc(aComp
);
982 sc::RangeMatrix
ScInterpreter::CompareMat( ScQueryOp eOp
, sc::CompareOptions
* pOptions
)
986 aComp
.mbIgnoreCase
= mrDoc
.GetDocOptions().IsIgnoreCase();
987 sc::RangeMatrix aMat
[2];
989 for( short i
= 1; i
>= 0; i
-- )
991 sc::Compare::Cell
& rCell
= aComp
.maCells
[i
];
993 switch (GetRawStackType())
997 rCell
.mbEmpty
= true;
1001 rCell
.mfValue
= GetDouble();
1002 rCell
.mbValue
= true;
1005 rCell
.maStr
= GetString();
1006 rCell
.mbValue
= false;
1010 PopSingleRef( aAdr
);
1011 ScRefCellValue
aCell(mrDoc
, aAdr
);
1012 if (aCell
.hasEmptyValue())
1013 rCell
.mbEmpty
= true;
1014 else if (aCell
.hasString())
1016 svl::SharedString aStr
;
1017 GetCellString(aStr
, aCell
);
1019 rCell
.mbValue
= false;
1023 rCell
.mfValue
= GetCellValue(aAdr
, aCell
);
1024 rCell
.mbValue
= true;
1028 case svExternalSingleRef
:
1029 case svExternalDoubleRef
:
1032 aMat
[i
] = GetRangeMatrix();
1034 SetError( FormulaError::IllegalParameter
);
1036 aMat
[i
].mpMat
->SetErrorInterpreter(nullptr);
1037 // errors are transported as DoubleError inside matrix
1041 SetError( FormulaError::IllegalParameter
);
1046 sc::RangeMatrix aRes
;
1048 if (nGlobalError
!= FormulaError::NONE
)
1050 nCurFmtType
= nFuncFmtType
= SvNumFormatType::LOGICAL
;
1054 if (aMat
[0].mpMat
&& aMat
[1].mpMat
)
1058 aMat
[0].mpMat
->GetDimensions(nC0
, nR0
);
1059 aMat
[1].mpMat
->GetDimensions(nC1
, nR1
);
1060 SCSIZE nC
= std::max( nC0
, nC1
);
1061 SCSIZE nR
= std::max( nR0
, nR1
);
1062 aRes
.mpMat
= GetNewMat( nC
, nR
, /*bEmpty*/true );
1065 for ( SCSIZE j
=0; j
<nC
; j
++ )
1067 for ( SCSIZE k
=0; k
<nR
; k
++ )
1069 SCSIZE nCol
= j
, nRow
= k
;
1070 if (aMat
[0].mpMat
->ValidColRowOrReplicated(nCol
, nRow
) &&
1071 aMat
[1].mpMat
->ValidColRowOrReplicated(nCol
, nRow
))
1073 for ( short i
=1; i
>=0; i
-- )
1075 sc::Compare::Cell
& rCell
= aComp
.maCells
[i
];
1077 if (aMat
[i
].mpMat
->IsStringOrEmpty(j
, k
))
1079 rCell
.mbValue
= false;
1080 rCell
.maStr
= aMat
[i
].mpMat
->GetString(j
, k
);
1081 rCell
.mbEmpty
= aMat
[i
].mpMat
->IsEmpty(j
, k
);
1085 rCell
.mbValue
= true;
1086 rCell
.mfValue
= aMat
[i
].mpMat
->GetDouble(j
, k
);
1087 rCell
.mbEmpty
= false;
1090 aRes
.mpMat
->PutDouble( sc::CompareFunc( aComp
, pOptions
), j
, k
);
1093 aRes
.mpMat
->PutError( FormulaError::NoValue
, j
, k
);
1100 aRes
.mpMat
->CompareEqual();
1103 aRes
.mpMat
->CompareLess();
1106 aRes
.mpMat
->CompareGreater();
1109 aRes
.mpMat
->CompareLessEqual();
1111 case SC_GREATER_EQUAL
:
1112 aRes
.mpMat
->CompareGreaterEqual();
1115 aRes
.mpMat
->CompareNotEqual();
1118 SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp
));
1123 else if (aMat
[0].mpMat
|| aMat
[1].mpMat
)
1125 size_t i
= ( aMat
[0].mpMat
? 0 : 1);
1127 aRes
.mnCol1
= aMat
[i
].mnCol1
;
1128 aRes
.mnRow1
= aMat
[i
].mnRow1
;
1129 aRes
.mnTab1
= aMat
[i
].mnTab1
;
1130 aRes
.mnCol2
= aMat
[i
].mnCol2
;
1131 aRes
.mnRow2
= aMat
[i
].mnRow2
;
1132 aRes
.mnTab2
= aMat
[i
].mnTab2
;
1134 ScMatrix
& rMat
= *aMat
[i
].mpMat
;
1135 aRes
.mpMat
= rMat
.CompareMatrix(aComp
, i
, pOptions
);
1140 nCurFmtType
= nFuncFmtType
= SvNumFormatType::LOGICAL
;
1144 ScMatrixRef
ScInterpreter::QueryMat( const ScMatrixRef
& pMat
, sc::CompareOptions
& rOptions
)
1146 SvNumFormatType nSaveCurFmtType
= nCurFmtType
;
1147 SvNumFormatType nSaveFuncFmtType
= nFuncFmtType
;
1149 const ScQueryEntry::Item
& rItem
= rOptions
.aQueryEntry
.GetQueryItem();
1150 if (rItem
.meType
== ScQueryEntry::ByString
)
1151 PushString(rItem
.maString
.getString());
1153 PushDouble(rItem
.mfVal
);
1154 ScMatrixRef pResultMatrix
= CompareMat(rOptions
.aQueryEntry
.eOp
, &rOptions
).mpMat
;
1155 nCurFmtType
= nSaveCurFmtType
;
1156 nFuncFmtType
= nSaveFuncFmtType
;
1157 if (nGlobalError
!= FormulaError::NONE
|| !pResultMatrix
)
1159 SetError( FormulaError::IllegalParameter
);
1160 return pResultMatrix
;
1163 return pResultMatrix
;
1166 void ScInterpreter::ScEqual()
1168 if ( GetStackType(1) == svMatrix
|| GetStackType(2) == svMatrix
)
1170 sc::RangeMatrix aMat
= CompareMat(SC_EQUAL
);
1173 PushIllegalParameter();
1180 PushInt( int(Compare( SC_EQUAL
) == 0) );
1183 void ScInterpreter::ScNotEqual()
1185 if ( GetStackType(1) == svMatrix
|| GetStackType(2) == svMatrix
)
1187 sc::RangeMatrix aMat
= CompareMat(SC_NOT_EQUAL
);
1190 PushIllegalParameter();
1197 PushInt( int(Compare( SC_NOT_EQUAL
) != 0) );
1200 void ScInterpreter::ScLess()
1202 if ( GetStackType(1) == svMatrix
|| GetStackType(2) == svMatrix
)
1204 sc::RangeMatrix aMat
= CompareMat(SC_LESS
);
1207 PushIllegalParameter();
1214 PushInt( int(Compare( SC_LESS
) < 0) );
1217 void ScInterpreter::ScGreater()
1219 if ( GetStackType(1) == svMatrix
|| GetStackType(2) == svMatrix
)
1221 sc::RangeMatrix aMat
= CompareMat(SC_GREATER
);
1224 PushIllegalParameter();
1231 PushInt( int(Compare( SC_GREATER
) > 0) );
1234 void ScInterpreter::ScLessEqual()
1236 if ( GetStackType(1) == svMatrix
|| GetStackType(2) == svMatrix
)
1238 sc::RangeMatrix aMat
= CompareMat(SC_LESS_EQUAL
);
1241 PushIllegalParameter();
1248 PushInt( int(Compare( SC_LESS_EQUAL
) <= 0) );
1251 void ScInterpreter::ScGreaterEqual()
1253 if ( GetStackType(1) == svMatrix
|| GetStackType(2) == svMatrix
)
1255 sc::RangeMatrix aMat
= CompareMat(SC_GREATER_EQUAL
);
1258 PushIllegalParameter();
1265 PushInt( int(Compare( SC_GREATER_EQUAL
) >= 0) );
1268 void ScInterpreter::ScAnd()
1270 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1271 short nParamCount
= GetByte();
1272 if ( !MustHaveParamCountMin( nParamCount
, 1 ) )
1275 bool bHaveValue
= false;
1277 size_t nRefInList
= 0;
1278 while( nParamCount
-- > 0)
1280 if ( nGlobalError
== FormulaError::NONE
)
1282 switch ( GetStackType() )
1286 bRes
&= ( PopDouble() != 0.0 );
1290 SetError( FormulaError::NoValue
);
1295 PopSingleRef( aAdr
);
1296 if ( nGlobalError
== FormulaError::NONE
)
1298 ScRefCellValue
aCell(mrDoc
, aAdr
);
1299 if (aCell
.hasNumeric())
1302 bRes
&= ( GetCellValue(aAdr
, aCell
) != 0.0 );
1304 // else: Xcl raises no error here
1312 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
1313 if ( nGlobalError
== FormulaError::NONE
)
1316 FormulaError nErr
= FormulaError::NONE
;
1317 ScValueIterator
aValIter( mrContext
, aRange
);
1318 if ( aValIter
.GetFirst( fVal
, nErr
) && nErr
== FormulaError::NONE
)
1323 bRes
&= ( fVal
!= 0.0 );
1324 } while ( (nErr
== FormulaError::NONE
) &&
1325 aValIter
.GetNext( fVal
, nErr
) );
1331 case svExternalSingleRef
:
1332 case svExternalDoubleRef
:
1335 ScMatrixRef pMat
= GetMatrix();
1339 double fVal
= pMat
->And();
1340 FormulaError nErr
= GetDoubleErrorValue( fVal
);
1341 if ( nErr
!= FormulaError::NONE
)
1347 bRes
&= (fVal
!= 0.0);
1349 // else: GetMatrix did set FormulaError::IllegalParameter
1354 SetError( FormulaError::IllegalParameter
);
1361 PushInt( int(bRes
) );
1366 void ScInterpreter::ScOr()
1368 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1369 short nParamCount
= GetByte();
1370 if ( !MustHaveParamCountMin( nParamCount
, 1 ) )
1373 bool bHaveValue
= false;
1375 size_t nRefInList
= 0;
1376 while( nParamCount
-- > 0)
1378 if ( nGlobalError
== FormulaError::NONE
)
1380 switch ( GetStackType() )
1384 bRes
|= ( PopDouble() != 0.0 );
1388 SetError( FormulaError::NoValue
);
1393 PopSingleRef( aAdr
);
1394 if ( nGlobalError
== FormulaError::NONE
)
1396 ScRefCellValue
aCell(mrDoc
, aAdr
);
1397 if (aCell
.hasNumeric())
1400 bRes
|= ( GetCellValue(aAdr
, aCell
) != 0.0 );
1402 // else: Xcl raises no error here
1410 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
1411 if ( nGlobalError
== FormulaError::NONE
)
1414 FormulaError nErr
= FormulaError::NONE
;
1415 ScValueIterator
aValIter( mrContext
, aRange
);
1416 if ( aValIter
.GetFirst( fVal
, nErr
) )
1421 bRes
|= ( fVal
!= 0.0 );
1422 } while ( (nErr
== FormulaError::NONE
) &&
1423 aValIter
.GetNext( fVal
, nErr
) );
1429 case svExternalSingleRef
:
1430 case svExternalDoubleRef
:
1434 ScMatrixRef pMat
= GetMatrix();
1438 double fVal
= pMat
->Or();
1439 FormulaError nErr
= GetDoubleErrorValue( fVal
);
1440 if ( nErr
!= FormulaError::NONE
)
1446 bRes
|= (fVal
!= 0.0);
1448 // else: GetMatrix did set FormulaError::IllegalParameter
1453 SetError( FormulaError::IllegalParameter
);
1460 PushInt( int(bRes
) );
1465 void ScInterpreter::ScXor()
1468 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1469 short nParamCount
= GetByte();
1470 if ( !MustHaveParamCountMin( nParamCount
, 1 ) )
1473 bool bHaveValue
= false;
1475 size_t nRefInList
= 0;
1476 while( nParamCount
-- > 0)
1478 if ( nGlobalError
== FormulaError::NONE
)
1480 switch ( GetStackType() )
1484 bRes
^= ( PopDouble() != 0.0 );
1488 SetError( FormulaError::NoValue
);
1493 PopSingleRef( aAdr
);
1494 if ( nGlobalError
== FormulaError::NONE
)
1496 ScRefCellValue
aCell(mrDoc
, aAdr
);
1497 if (aCell
.hasNumeric())
1500 bRes
^= ( GetCellValue(aAdr
, aCell
) != 0.0 );
1502 /* TODO: set error? Excel doesn't have XOR, but
1503 * doesn't set an error in this case for AND and
1512 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
1513 if ( nGlobalError
== FormulaError::NONE
)
1516 FormulaError nErr
= FormulaError::NONE
;
1517 ScValueIterator
aValIter( mrContext
, aRange
);
1518 if ( aValIter
.GetFirst( fVal
, nErr
) )
1523 bRes
^= ( fVal
!= 0.0 );
1524 } while ( (nErr
== FormulaError::NONE
) &&
1525 aValIter
.GetNext( fVal
, nErr
) );
1531 case svExternalSingleRef
:
1532 case svExternalDoubleRef
:
1536 ScMatrixRef pMat
= GetMatrix();
1540 double fVal
= pMat
->Xor();
1541 FormulaError nErr
= GetDoubleErrorValue( fVal
);
1542 if ( nErr
!= FormulaError::NONE
)
1548 bRes
^= ( fVal
!= 0.0 );
1550 // else: GetMatrix did set FormulaError::IllegalParameter
1555 SetError( FormulaError::IllegalParameter
);
1562 PushInt( int(bRes
) );
1567 void ScInterpreter::ScNeg()
1569 // Simple negation doesn't change current format type to number, keep
1571 nFuncFmtType
= nCurFmtType
;
1572 switch ( GetStackType() )
1576 ScMatrixRef pMat
= GetMatrix();
1578 PushIllegalParameter();
1582 pMat
->GetDimensions( nC
, nR
);
1583 ScMatrixRef pResMat
= GetNewMat( nC
, nR
, /*bEmpty*/true );
1585 PushIllegalArgument();
1588 pMat
->NegOp( *pResMat
);
1589 PushMatrix( pResMat
);
1595 PushDouble( -GetDouble() );
1599 void ScInterpreter::ScPercentSign()
1601 nFuncFmtType
= SvNumFormatType::PERCENT
;
1602 const FormulaToken
* pSaveCur
= pCur
;
1603 sal_uInt8 nSavePar
= cPar
;
1606 FormulaByteToken
aDivOp( ocDiv
, cPar
);
1613 void ScInterpreter::ScNot()
1615 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1616 switch ( GetStackType() )
1620 ScMatrixRef pMat
= GetMatrix();
1622 PushIllegalParameter();
1626 pMat
->GetDimensions( nC
, nR
);
1627 ScMatrixRef pResMat
= GetNewMat( nC
, nR
, /*bEmpty*/true);
1629 PushIllegalArgument();
1632 pMat
->NotOp( *pResMat
);
1633 PushMatrix( pResMat
);
1639 PushInt( int(GetDouble() == 0.0) );
1643 void ScInterpreter::ScBitAnd()
1646 if ( !MustHaveParamCount( GetByte(), 2 ) )
1649 double num1
= ::rtl::math::approxFloor( GetDouble());
1650 double num2
= ::rtl::math::approxFloor( GetDouble());
1651 if ( (num1
>= n2power48
) || (num1
< 0) ||
1652 (num2
>= n2power48
) || (num2
< 0))
1653 PushIllegalArgument();
1655 PushDouble (static_cast<sal_uInt64
>(num1
) & static_cast<sal_uInt64
>(num2
));
1658 void ScInterpreter::ScBitOr()
1661 if ( !MustHaveParamCount( GetByte(), 2 ) )
1664 double num1
= ::rtl::math::approxFloor( GetDouble());
1665 double num2
= ::rtl::math::approxFloor( GetDouble());
1666 if ( (num1
>= n2power48
) || (num1
< 0) ||
1667 (num2
>= n2power48
) || (num2
< 0))
1668 PushIllegalArgument();
1670 PushDouble (static_cast<sal_uInt64
>(num1
) | static_cast<sal_uInt64
>(num2
));
1673 void ScInterpreter::ScBitXor()
1676 if ( !MustHaveParamCount( GetByte(), 2 ) )
1679 double num1
= ::rtl::math::approxFloor( GetDouble());
1680 double num2
= ::rtl::math::approxFloor( GetDouble());
1681 if ( (num1
>= n2power48
) || (num1
< 0) ||
1682 (num2
>= n2power48
) || (num2
< 0))
1683 PushIllegalArgument();
1685 PushDouble (static_cast<sal_uInt64
>(num1
) ^ static_cast<sal_uInt64
>(num2
));
1688 void ScInterpreter::ScBitLshift()
1691 if ( !MustHaveParamCount( GetByte(), 2 ) )
1694 double fShift
= ::rtl::math::approxFloor( GetDouble());
1695 double num
= ::rtl::math::approxFloor( GetDouble());
1696 if ((num
>= n2power48
) || (num
< 0))
1697 PushIllegalArgument();
1702 fRes
= ::rtl::math::approxFloor( num
/ pow( 2.0, -fShift
));
1703 else if (fShift
== 0)
1706 fRes
= num
* pow( 2.0, fShift
);
1711 void ScInterpreter::ScBitRshift()
1714 if ( !MustHaveParamCount( GetByte(), 2 ) )
1717 double fShift
= ::rtl::math::approxFloor( GetDouble());
1718 double num
= ::rtl::math::approxFloor( GetDouble());
1719 if ((num
>= n2power48
) || (num
< 0))
1720 PushIllegalArgument();
1725 fRes
= num
* pow( 2.0, -fShift
);
1726 else if (fShift
== 0)
1729 fRes
= ::rtl::math::approxFloor( num
/ pow( 2.0, fShift
));
1734 void ScInterpreter::ScPi()
1739 void ScInterpreter::ScRandomImpl( const std::function
<double( double fFirst
, double fLast
)>& RandomFunc
,
1740 double fFirst
, double fLast
)
1746 // In JumpMatrix context use its dimensions for the return matrix; the
1747 // formula cell range selected may differ, for example if the result is
1748 // to be transposed.
1749 if (GetStackType(1) == svJumpMatrix
)
1752 pStack
[sp
-1]->GetJumpMatrix()->GetDimensions( nC
, nR
);
1753 nCols
= std::max
<SCCOL
>(0, static_cast<SCCOL
>(nC
));
1754 nRows
= std::max
<SCROW
>(0, static_cast<SCROW
>(nR
));
1756 else if (pMyFormulaCell
)
1757 pMyFormulaCell
->GetMatColsRows( nCols
, nRows
);
1759 if (nCols
== 1 && nRows
== 1)
1761 // For compatibility with existing
1762 // com.sun.star.sheet.FunctionAccess.callFunction() calls that per
1763 // default are executed in array context unless
1764 // FA.setPropertyValue("IsArrayFunction",False) was set, return a
1765 // scalar double instead of a 1x1 matrix object. tdf#128218
1766 PushDouble( RandomFunc( fFirst
, fLast
));
1770 // ScViewFunc::EnterMatrix() might be asking for
1771 // ScFormulaCell::GetResultDimensions(), which here are none so create
1772 // a 1x1 matrix at least which exactly is the case when EnterMatrix()
1773 // asks for a not selected range.
1778 ScMatrixRef pResMat
= GetNewMat( static_cast<SCSIZE
>(nCols
), static_cast<SCSIZE
>(nRows
), /*bEmpty*/true );
1780 PushError( FormulaError::MatrixSize
);
1783 for (SCCOL i
=0; i
< nCols
; ++i
)
1785 for (SCROW j
=0; j
< nRows
; ++j
)
1787 pResMat
->PutDouble( RandomFunc( fFirst
, fLast
),
1788 static_cast<SCSIZE
>(i
), static_cast<SCSIZE
>(j
));
1791 PushMatrix( pResMat
);
1796 PushDouble( RandomFunc( fFirst
, fLast
));
1800 void ScInterpreter::ScRandom()
1802 auto RandomFunc
= [this]( double, double )
1804 std::uniform_real_distribution
<double> dist(0.0, 1.0);
1805 return dist(mrContext
.aRNG
);
1807 ScRandomImpl( RandomFunc
, 0.0, 0.0 );
1810 void ScInterpreter::ScRandArray()
1812 sal_uInt8 nParamCount
= GetByte();
1813 // optional 5th para:
1814 // TRUE for a whole number
1815 // FALSE for a decimal number - default.
1816 bool bWholeNumber
= false;
1817 if (nParamCount
== 5)
1818 bWholeNumber
= GetBoolWithDefault(false);
1820 // optional 4th para: The maximum value of the random numbers
1822 if (nParamCount
>= 4)
1823 fMax
= GetDoubleWithDefault(1.0);
1825 // optional 3rd para: The minimum value of the random numbers
1827 if (nParamCount
>= 3)
1828 fMin
= GetDoubleWithDefault(0.0);
1830 // optional 2nd para: The number of columns of the return array
1832 if (nParamCount
>= 2)
1833 nCols
= static_cast<SCCOL
>(GetInt32WithDefault(1));
1835 // optional 1st para: The number of rows of the return array
1837 if (nParamCount
>= 1)
1838 nRows
= static_cast<SCROW
>(GetInt32WithDefault(1));
1842 fMax
= rtl::math::round(fMax
, 0, rtl_math_RoundingMode_Up
);
1843 fMin
= rtl::math::round(fMin
, 0, rtl_math_RoundingMode_Up
);
1846 if (nGlobalError
!= FormulaError::NONE
|| fMin
> fMax
|| nCols
<= 0 || nRows
<= 0)
1848 PushIllegalArgument();
1853 fMax
= std::nextafter(fMax
+ 1, -DBL_MAX
);
1855 fMax
= std::nextafter(fMax
, DBL_MAX
);
1857 auto RandomFunc
= [this](double fFirst
, double fLast
, bool bWholeNum
)
1859 std::uniform_real_distribution
<double> dist(fFirst
, fLast
);
1861 return floor(dist(mrContext
.aRNG
));
1863 return dist(mrContext
.aRNG
);
1866 if (nCols
== 1 && nRows
== 1)
1868 PushDouble(RandomFunc(fMin
, fMax
, bWholeNumber
));
1872 ScMatrixRef pResMat
= GetNewMat(static_cast<SCSIZE
>(nCols
), static_cast<SCSIZE
>(nRows
), /*bEmpty*/true);
1874 PushError(FormulaError::MatrixSize
);
1877 for (SCCOL i
= 0; i
< nCols
; ++i
)
1879 for (SCROW j
= 0; j
< nRows
; ++j
)
1881 pResMat
->PutDouble(RandomFunc(fMin
, fMax
, bWholeNumber
),
1882 static_cast<SCSIZE
>(i
), static_cast<SCSIZE
>(j
));
1885 PushMatrix(pResMat
);
1889 void ScInterpreter::ScRandbetween()
1891 if (!MustHaveParamCount( GetByte(), 2))
1894 // Same like scaddins/source/analysis/analysis.cxx
1895 // AnalysisAddIn::getRandbetween()
1896 double fMax
= rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up
);
1897 double fMin
= rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up
);
1898 if (nGlobalError
!= FormulaError::NONE
|| fMin
> fMax
)
1900 PushIllegalArgument();
1903 fMax
= std::nextafter( fMax
+1, -DBL_MAX
);
1904 auto RandomFunc
= [this]( double fFirst
, double fLast
)
1906 std::uniform_real_distribution
<double> dist(fFirst
, fLast
);
1907 return floor(dist(mrContext
.aRNG
));
1909 ScRandomImpl( RandomFunc
, fMin
, fMax
);
1912 void ScInterpreter::ScTrue()
1914 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1918 void ScInterpreter::ScFalse()
1920 nFuncFmtType
= SvNumFormatType::LOGICAL
;
1924 void ScInterpreter::ScDeg()
1926 PushDouble(basegfx::rad2deg(GetDouble()));
1929 void ScInterpreter::ScRad()
1931 PushDouble(basegfx::deg2rad(GetDouble()));
1934 void ScInterpreter::ScSin()
1936 PushDouble(::rtl::math::sin(GetDouble()));
1939 void ScInterpreter::ScCos()
1941 PushDouble(::rtl::math::cos(GetDouble()));
1944 void ScInterpreter::ScTan()
1946 PushDouble(::rtl::math::tan(GetDouble()));
1949 void ScInterpreter::ScCot()
1951 PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1954 void ScInterpreter::ScArcSin()
1956 PushDouble(asin(GetDouble()));
1959 void ScInterpreter::ScArcCos()
1961 PushDouble(acos(GetDouble()));
1964 void ScInterpreter::ScArcTan()
1966 PushDouble(atan(GetDouble()));
1969 void ScInterpreter::ScArcCot()
1971 PushDouble((M_PI_2
) - atan(GetDouble()));
1974 void ScInterpreter::ScSinHyp()
1976 PushDouble(sinh(GetDouble()));
1979 void ScInterpreter::ScCosHyp()
1981 PushDouble(cosh(GetDouble()));
1984 void ScInterpreter::ScTanHyp()
1986 PushDouble(tanh(GetDouble()));
1989 void ScInterpreter::ScCotHyp()
1991 PushDouble(1.0 / tanh(GetDouble()));
1994 void ScInterpreter::ScArcSinHyp()
1996 PushDouble( ::rtl::math::asinh( GetDouble()));
1999 void ScInterpreter::ScArcCosHyp()
2001 double fVal
= GetDouble();
2003 PushIllegalArgument();
2005 PushDouble( ::rtl::math::acosh( fVal
));
2008 void ScInterpreter::ScArcTanHyp()
2010 double fVal
= GetDouble();
2011 if (fabs(fVal
) >= 1.0)
2012 PushIllegalArgument();
2014 PushDouble(::atanh(fVal
));
2017 void ScInterpreter::ScArcCotHyp()
2019 double nVal
= GetDouble();
2020 if (fabs(nVal
) <= 1.0)
2021 PushIllegalArgument();
2023 PushDouble(0.5 * log((nVal
+ 1.0) / (nVal
- 1.0)));
2026 void ScInterpreter::ScCosecant()
2028 PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
2031 void ScInterpreter::ScSecant()
2033 PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
2036 void ScInterpreter::ScCosecantHyp()
2038 PushDouble(1.0 / sinh(GetDouble()));
2041 void ScInterpreter::ScSecantHyp()
2043 PushDouble(1.0 / cosh(GetDouble()));
2046 void ScInterpreter::ScExp()
2048 PushDouble(exp(GetDouble()));
2051 void ScInterpreter::ScSqrt()
2053 double fVal
= GetDouble();
2055 PushDouble(sqrt(fVal
));
2057 PushIllegalArgument();
2060 void ScInterpreter::ScIsEmpty()
2063 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2064 switch ( GetRawStackType() )
2068 FormulaConstTokenRef p
= PopToken();
2069 if (!static_cast<const ScEmptyCellToken
*>(p
.get())->IsInherited())
2077 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2079 // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that
2080 // may treat ="" in the referenced cell as blank for Excel
2081 // interoperability.
2082 ScRefCellValue
aCell(mrDoc
, aAdr
);
2083 if (aCell
.getType() == CELLTYPE_NONE
)
2087 case svExternalSingleRef
:
2088 case svExternalDoubleRef
:
2091 ScMatrixRef pMat
= GetMatrix();
2094 else if ( !pJumpMatrix
)
2095 nRes
= pMat
->IsEmptyCell( 0, 0) ? 1 : 0;
2098 SCSIZE nCols
, nRows
, nC
, nR
;
2099 pMat
->GetDimensions( nCols
, nRows
);
2100 pJumpMatrix
->GetPos( nC
, nR
);
2101 if ( nC
< nCols
&& nR
< nRows
)
2102 nRes
= pMat
->IsEmptyCell( nC
, nR
) ? 1 : 0;
2103 // else: false, not empty (which is what Xcl does)
2110 nGlobalError
= FormulaError::NONE
;
2114 bool ScInterpreter::IsString()
2116 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2118 switch ( GetRawStackType() )
2128 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2131 ScRefCellValue
aCell(mrDoc
, aAdr
);
2132 if (GetCellErrCode(aCell
) == FormulaError::NONE
)
2134 switch (aCell
.getType())
2136 case CELLTYPE_STRING
:
2137 case CELLTYPE_EDIT
:
2140 case CELLTYPE_FORMULA
:
2141 bRes
= (!aCell
.getFormula()->IsValue() && !aCell
.getFormula()->IsEmpty());
2149 case svExternalSingleRef
:
2151 ScExternalRefCache::TokenRef pToken
;
2152 PopExternalSingleRef(pToken
);
2153 if (nGlobalError
== FormulaError::NONE
&& pToken
->GetType() == svString
)
2157 case svExternalDoubleRef
:
2160 ScMatrixRef pMat
= GetMatrix();
2163 else if ( !pJumpMatrix
)
2164 bRes
= pMat
->IsStringOrEmpty(0, 0) && !pMat
->IsEmpty(0, 0);
2167 SCSIZE nCols
, nRows
, nC
, nR
;
2168 pMat
->GetDimensions( nCols
, nRows
);
2169 pJumpMatrix
->GetPos( nC
, nR
);
2170 if ( nC
< nCols
&& nR
< nRows
)
2171 bRes
= pMat
->IsStringOrEmpty( nC
, nR
) && !pMat
->IsEmpty( nC
, nR
);
2178 nGlobalError
= FormulaError::NONE
;
2182 void ScInterpreter::ScIsString()
2184 PushInt( int(IsString()) );
2187 void ScInterpreter::ScIsNonString()
2189 PushInt( int(!IsString()) );
2192 void ScInterpreter::ScIsLogical()
2195 switch ( GetStackType() )
2201 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2204 ScRefCellValue
aCell(mrDoc
, aAdr
);
2205 if (GetCellErrCode(aCell
) == FormulaError::NONE
)
2207 if (aCell
.hasNumeric())
2209 sal_uInt32 nFormat
= GetCellNumberFormat(aAdr
, aCell
);
2210 bRes
= (mrContext
.NFGetType(nFormat
) == SvNumFormatType::LOGICAL
);
2218 svl::SharedString aStr
;
2219 ScMatValType nMatValType
= GetDoubleOrStringFromMatrix( fVal
, aStr
);
2220 bRes
= (nMatValType
== ScMatValType::Boolean
);
2225 if ( nGlobalError
== FormulaError::NONE
)
2226 bRes
= ( nCurFmtType
== SvNumFormatType::LOGICAL
);
2228 nCurFmtType
= nFuncFmtType
= SvNumFormatType::LOGICAL
;
2229 nGlobalError
= FormulaError::NONE
;
2230 PushInt( int(bRes
) );
2233 void ScInterpreter::ScType()
2236 switch ( GetStackType() )
2242 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2245 ScRefCellValue
aCell(mrDoc
, aAdr
);
2246 if (GetCellErrCode(aCell
) == FormulaError::NONE
)
2248 switch (aCell
.getType())
2250 // NOTE: this is Xcl nonsense!
2251 case CELLTYPE_STRING
:
2252 case CELLTYPE_EDIT
:
2255 case CELLTYPE_VALUE
:
2257 sal_uInt32 nFormat
= GetCellNumberFormat(aAdr
, aCell
);
2258 if (mrContext
.NFGetType(nFormat
) == SvNumFormatType::LOGICAL
)
2265 // always 1, s. tdf#73078
2268 case CELLTYPE_FORMULA
:
2272 PushIllegalArgument();
2281 if ( nGlobalError
!= FormulaError::NONE
)
2284 nGlobalError
= FormulaError::NONE
;
2291 if ( nGlobalError
!= FormulaError::NONE
)
2294 nGlobalError
= FormulaError::NONE
;
2298 // we could return the type of one element if in JumpMatrix or
2299 // ForceArray mode, but Xcl doesn't ...
2303 if ( nGlobalError
!= FormulaError::NONE
)
2306 nGlobalError
= FormulaError::NONE
;
2314 static bool lcl_FormatHasNegColor( const SvNumberformat
* pFormat
)
2316 return pFormat
&& pFormat
->GetColor( 1 );
2319 static bool lcl_FormatHasOpenPar( const SvNumberformat
* pFormat
)
2321 return pFormat
&& (pFormat
->GetFormatstring().indexOf('(') != -1);
2326 void getFormatString(const ScInterpreterContext
& rContext
, sal_uLong nFormat
, OUString
& rFmtStr
)
2328 rFmtStr
= rContext
.NFGetCalcCellReturn(nFormat
);
2333 void ScInterpreter::ScCell()
2334 { // ATTRIBUTE ; [REF]
2335 sal_uInt8 nParamCount
= GetByte();
2336 if( !MustHaveParamCount( nParamCount
, 1, 2 ) )
2339 ScAddress
aCellPos( aPos
);
2340 if( nParamCount
== 2 )
2342 switch (GetStackType())
2344 case svExternalSingleRef
:
2345 case svExternalDoubleRef
:
2347 // Let's handle external reference separately...
2353 // Exceptionally not an intersecting position but top left.
2354 // See ODF v1.3 part 4 OpenFormula 6.13.3 CELL
2356 PopDoubleRef( aRange
);
2357 aCellPos
= aRange
.aStart
;
2361 PopSingleRef( aCellPos
);
2365 SetError( FormulaError::NoRef
);
2368 OUString aInfoType
= GetString().getString();
2369 if (nGlobalError
!= FormulaError::NONE
)
2370 PushIllegalParameter();
2373 ScRefCellValue
aCell(mrDoc
, aCellPos
);
2375 ScCellKeywordTranslator::transKeyword(aInfoType
, &ScGlobal::GetLocale(), ocCell
);
2377 // *** ADDRESS INFO ***
2378 if( aInfoType
== "COL" )
2379 { // column number (1-based)
2380 PushInt( aCellPos
.Col() + 1 );
2382 else if( aInfoType
== "ROW" )
2383 { // row number (1-based)
2384 PushInt( aCellPos
.Row() + 1 );
2386 else if( aInfoType
== "SHEET" )
2387 { // table number (1-based)
2388 PushInt( aCellPos
.Tab() + 1 );
2390 else if( aInfoType
== "ADDRESS" )
2391 { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
2393 // Follow the configurable string reference address syntax as also
2394 // used by INDIRECT() (and ADDRESS() for the sheet separator).
2395 FormulaGrammar::AddressConvention eConv
= maCalcConfig
.meStringRefAddressSyntax
;
2399 // Use the current address syntax if unspecified or says
2400 // one or the other or one we don't explicitly handle.
2401 eConv
= mrDoc
.GetAddressConvention();
2403 case FormulaGrammar::CONV_OOO
:
2404 case FormulaGrammar::CONV_XL_A1
:
2405 case FormulaGrammar::CONV_XL_R1C1
:
2410 ScRefFlags nFlags
= (aCellPos
.Tab() == aPos
.Tab()) ? ScRefFlags::ADDR_ABS
: ScRefFlags::ADDR_ABS_3D
;
2411 OUString
aStr(aCellPos
.Format(nFlags
, &mrDoc
, eConv
));
2414 else if( aInfoType
== "FILENAME" )
2416 SCTAB nTab
= aCellPos
.Tab();
2417 OUString aFuncResult
;
2418 if( nTab
< mrDoc
.GetTableCount() )
2420 if( mrDoc
.GetLinkMode( nTab
) == ScLinkMode::VALUE
)
2421 mrDoc
.GetName( nTab
, aFuncResult
);
2424 ScDocShell
* pShell
= mrDoc
.GetDocumentShell();
2425 if( pShell
&& pShell
->GetMedium() )
2427 const INetURLObject
& rURLObj
= pShell
->GetMedium()->GetURLObject();
2429 mrDoc
.GetName( nTab
, aTabName
);
2431 FormulaGrammar::AddressConvention eConv
= maCalcConfig
.meStringRefAddressSyntax
;
2432 if (eConv
== FormulaGrammar::CONV_UNSPECIFIED
)
2433 eConv
= mrDoc
.GetAddressConvention();
2435 if (eConv
== FormulaGrammar::CONV_XL_A1
||
2436 eConv
== FormulaGrammar::CONV_XL_R1C1
||
2437 eConv
== FormulaGrammar::CONV_XL_OOX
)
2439 // file name and table name: FILEPATH/[FILENAME]TABLE
2440 if (!comphelper::LibreOfficeKit::isActive())
2441 aFuncResult
= rURLObj
.GetPartBeforeLastName();
2442 aFuncResult
+= "[" + rURLObj
.GetLastName(INetURLObject::DecodeMechanism::Unambiguous
) +
2447 // file name and table name: 'FILEPATH/FILENAME'#$TABLE
2449 if (!comphelper::LibreOfficeKit::isActive())
2450 aFuncResult
+= rURLObj
.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous
);
2452 aFuncResult
+= rURLObj
.GetLastName(INetURLObject::DecodeMechanism::Unambiguous
);
2453 aFuncResult
+= "'#$" + aTabName
;
2458 PushString( aFuncResult
);
2460 else if( aInfoType
== "COORD" )
2461 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2462 // Yes, passing tab as col is intentional!
2463 OUString aCellStr1
=
2464 ScAddress( static_cast<SCCOL
>(aCellPos
.Tab()), 0, 0 ).Format(
2465 (ScRefFlags::COL_ABS
|ScRefFlags::COL_VALID
), nullptr, mrDoc
.GetAddressConvention() );
2466 OUString aCellStr2
=
2467 aCellPos
.Format((ScRefFlags::COL_ABS
|ScRefFlags::COL_VALID
|ScRefFlags::ROW_ABS
|ScRefFlags::ROW_VALID
),
2468 nullptr, mrDoc
.GetAddressConvention());
2469 OUString aFuncResult
= aCellStr1
+ ":" + aCellStr2
;
2470 PushString( aFuncResult
);
2473 // *** CELL PROPERTIES ***
2474 else if( aInfoType
== "CONTENTS" )
2475 { // contents of the cell, no formatting
2476 if (aCell
.hasString())
2478 svl::SharedString aStr
;
2479 GetCellString(aStr
, aCell
);
2483 PushDouble(GetCellValue(aCellPos
, aCell
));
2485 else if( aInfoType
== "TYPE" )
2486 { // b = blank; l = string (label); v = otherwise (value)
2488 if (aCell
.hasString())
2491 c
= aCell
.hasNumeric() ? 'v' : 'b';
2492 PushString( OUString(c
) );
2494 else if( aInfoType
== "WIDTH" )
2495 { // column width (rounded off as count of zero characters in standard font and size)
2496 Printer
* pPrinter
= mrDoc
.GetPrinter();
2497 MapMode
aOldMode( pPrinter
->GetMapMode() );
2498 vcl::Font
aOldFont( pPrinter
->GetFont() );
2501 pPrinter
->SetMapMode(MapMode(MapUnit::MapTwip
));
2502 // font color doesn't matter here
2503 mrDoc
.getCellAttributeHelper().getDefaultCellAttribute().fillFontOnly(aDefFont
, pPrinter
);
2504 pPrinter
->SetFont(aDefFont
);
2505 tools::Long nZeroWidth
= pPrinter
->GetTextWidth( OUString( '0' ) );
2506 assert(nZeroWidth
!= 0);
2507 pPrinter
->SetFont( aOldFont
);
2508 pPrinter
->SetMapMode( aOldMode
);
2509 int nZeroCount
= static_cast<int>(mrDoc
.GetColWidth( aCellPos
.Col(), aCellPos
.Tab() ) / nZeroWidth
);
2510 PushInt( nZeroCount
);
2512 else if( aInfoType
== "PREFIX" )
2513 { // ' = left; " = right; ^ = centered
2515 if (aCell
.hasString())
2517 const SvxHorJustifyItem
* pJustAttr
= mrDoc
.GetAttr( aCellPos
, ATTR_HOR_JUSTIFY
);
2518 switch( pJustAttr
->GetValue() )
2520 case SvxCellHorJustify::Standard
:
2521 case SvxCellHorJustify::Left
:
2522 case SvxCellHorJustify::Block
: c
= '\''; break;
2523 case SvxCellHorJustify::Center
: c
= '^'; break;
2524 case SvxCellHorJustify::Right
: c
= '"'; break;
2525 case SvxCellHorJustify::Repeat
: c
= '\\'; break;
2528 PushString( OUString(c
) );
2530 else if( aInfoType
== "PROTECT" )
2531 { // 1 = cell locked
2532 const ScProtectionAttr
* pProtAttr
= mrDoc
.GetAttr( aCellPos
, ATTR_PROTECTION
);
2533 PushInt( pProtAttr
->GetProtection() ? 1 : 0 );
2536 // *** FORMATTING ***
2537 else if( aInfoType
== "FORMAT" )
2538 { // specific format code for standard formats
2539 OUString aFuncResult
;
2540 sal_uInt32 nFormat
= mrDoc
.GetNumberFormat( aCellPos
);
2541 getFormatString(mrContext
, nFormat
, aFuncResult
);
2542 PushString( aFuncResult
);
2544 else if( aInfoType
== "COLOR" )
2545 { // 1 = negative values are colored, otherwise 0
2546 const SvNumberformat
* pFormat
= mrContext
.NFGetFormatEntry( mrDoc
.GetNumberFormat( aCellPos
) );
2547 PushInt( lcl_FormatHasNegColor( pFormat
) ? 1 : 0 );
2549 else if( aInfoType
== "PARENTHESES" )
2550 { // 1 = format string contains a '(' character, otherwise 0
2551 const SvNumberformat
* pFormat
= mrContext
.NFGetFormatEntry( mrDoc
.GetNumberFormat( aCellPos
) );
2552 PushInt( lcl_FormatHasOpenPar( pFormat
) ? 1 : 0 );
2555 PushIllegalArgument();
2559 void ScInterpreter::ScCellExternal()
2563 ScSingleRefData aRef
;
2564 ScExternalRefCache::TokenRef pToken
;
2565 ScExternalRefCache::CellFormat aFmt
;
2566 PopExternalSingleRef(nFileId
, aTabName
, aRef
, pToken
, &aFmt
);
2567 if (nGlobalError
!= FormulaError::NONE
)
2569 PushError( nGlobalError
);
2573 OUString aInfoType
= GetString().getString();
2574 if (nGlobalError
!= FormulaError::NONE
)
2576 PushError( nGlobalError
);
2583 aRef
.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2584 SingleRefToVars(aRef
, nCol
, nRow
, nTab
);
2585 if (nGlobalError
!= FormulaError::NONE
)
2587 PushIllegalParameter();
2590 aRef
.SetAbsTab(-1); // revert the value.
2592 ScCellKeywordTranslator::transKeyword(aInfoType
, &ScGlobal::GetLocale(), ocCell
);
2593 ScExternalRefManager
* pRefMgr
= mrDoc
.GetExternalRefManager();
2595 if ( aInfoType
== "COL" )
2597 else if ( aInfoType
== "ROW" )
2599 else if ( aInfoType
== "SHEET" )
2601 // For SHEET, No idea what number we should set, but let's always set
2602 // 1 if the external sheet exists, no matter what sheet. Excel does
2604 if (pRefMgr
->getCacheTable(nFileId
, aTabName
, false))
2607 SetError(FormulaError::NoName
);
2609 else if ( aInfoType
== "ADDRESS" )
2611 // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2612 ScTokenArray
aArray(mrDoc
);
2613 aArray
.AddExternalSingleReference(nFileId
, svl::SharedString( aTabName
), aRef
); // string not interned
2614 ScCompiler
aComp(mrDoc
, aPos
, aArray
, formula::FormulaGrammar::GRAM_ODFF_A1
);
2616 aComp
.CreateStringFromTokenArray(aStr
);
2619 else if ( aInfoType
== "FILENAME" )
2621 const OUString
* p
= pRefMgr
->getExternalFileName(nFileId
);
2624 // In theory this should never happen...
2625 SetError(FormulaError::NoName
);
2630 FormulaGrammar::AddressConvention eConv
= maCalcConfig
.meStringRefAddressSyntax
;
2631 if (eConv
== FormulaGrammar::CONV_UNSPECIFIED
)
2632 eConv
= mrDoc
.GetAddressConvention();
2634 if (eConv
== FormulaGrammar::CONV_XL_A1
||
2635 eConv
== FormulaGrammar::CONV_XL_R1C1
||
2636 eConv
== FormulaGrammar::CONV_XL_OOX
)
2638 // 'file URI/[FileName]SheetName
2639 sal_Int32 nPos
= p
->lastIndexOf('/');
2640 aBuf
= OUString::Concat(p
->subView(0, nPos
+ 1))
2641 + "[" + p
->subView(nPos
+ 1) + "]"
2646 // 'file URI'#$SheetName
2647 aBuf
= "'" + *p
+ "'#$" + aTabName
;
2652 else if ( aInfoType
== "CONTENTS" )
2654 switch (pToken
->GetType())
2657 PushString(pToken
->GetString());
2660 PushString(OUString::number(pToken
->GetDouble()));
2663 PushString(ScGlobal::GetErrorString(pToken
->GetError()));
2666 PushString(OUString());
2669 else if ( aInfoType
== "TYPE" )
2671 sal_Unicode c
= 'v';
2672 switch (pToken
->GetType())
2683 PushString(OUString(c
));
2685 else if ( aInfoType
== "FORMAT" )
2688 sal_uLong nFmt
= aFmt
.mbIsSet
? aFmt
.mnIndex
: 0;
2689 getFormatString(mrContext
, nFmt
, aFmtStr
);
2690 PushString(aFmtStr
);
2692 else if ( aInfoType
== "COLOR" )
2694 // 1 = negative values are colored, otherwise 0
2698 const SvNumberformat
* pFormat
= mrContext
.NFGetFormatEntry(aFmt
.mnIndex
);
2699 nVal
= lcl_FormatHasNegColor(pFormat
) ? 1 : 0;
2703 else if ( aInfoType
== "PARENTHESES" )
2705 // 1 = format string contains a '(' character, otherwise 0
2709 const SvNumberformat
* pFormat
= mrContext
.NFGetFormatEntry(aFmt
.mnIndex
);
2710 nVal
= lcl_FormatHasOpenPar(pFormat
) ? 1 : 0;
2715 PushIllegalParameter();
2718 void ScInterpreter::ScIsRef()
2720 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2722 switch ( GetStackType() )
2727 PopSingleRef( aAdr
);
2728 if ( nGlobalError
== FormulaError::NONE
)
2735 PopDoubleRef( aRange
);
2736 if ( nGlobalError
== FormulaError::NONE
)
2742 FormulaConstTokenRef x
= PopToken();
2743 if ( nGlobalError
== FormulaError::NONE
)
2744 bRes
= !x
->GetRefList()->empty();
2747 case svExternalSingleRef
:
2749 ScExternalRefCache::TokenRef pToken
;
2750 PopExternalSingleRef(pToken
);
2751 if (nGlobalError
== FormulaError::NONE
)
2755 case svExternalDoubleRef
:
2757 ScExternalRefCache::TokenArrayRef pArray
;
2758 PopExternalDoubleRef(pArray
);
2759 if (nGlobalError
== FormulaError::NONE
)
2766 nGlobalError
= FormulaError::NONE
;
2767 PushInt( int(bRes
) );
2770 void ScInterpreter::ScIsValue()
2772 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2774 switch ( GetRawStackType() )
2784 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2787 ScRefCellValue
aCell(mrDoc
, aAdr
);
2788 if (GetCellErrCode(aCell
) == FormulaError::NONE
)
2790 switch (aCell
.getType())
2792 case CELLTYPE_VALUE
:
2795 case CELLTYPE_FORMULA
:
2796 bRes
= (aCell
.getFormula()->IsValue() && !aCell
.getFormula()->IsEmpty());
2804 case svExternalSingleRef
:
2806 ScExternalRefCache::TokenRef pToken
;
2807 PopExternalSingleRef(pToken
);
2808 if (nGlobalError
== FormulaError::NONE
&& pToken
->GetType() == svDouble
)
2812 case svExternalDoubleRef
:
2815 ScMatrixRef pMat
= GetMatrix();
2818 else if ( !pJumpMatrix
)
2820 if (pMat
->GetErrorIfNotString( 0, 0) == FormulaError::NONE
)
2821 bRes
= pMat
->IsValue( 0, 0);
2825 SCSIZE nCols
, nRows
, nC
, nR
;
2826 pMat
->GetDimensions( nCols
, nRows
);
2827 pJumpMatrix
->GetPos( nC
, nR
);
2828 if ( nC
< nCols
&& nR
< nRows
)
2829 if (pMat
->GetErrorIfNotString( nC
, nR
) == FormulaError::NONE
)
2830 bRes
= pMat
->IsValue( nC
, nR
);
2837 nGlobalError
= FormulaError::NONE
;
2838 PushInt( int(bRes
) );
2841 void ScInterpreter::ScIsFormula()
2843 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2845 switch ( GetStackType() )
2848 if (IsInArrayContext())
2853 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
2854 if (nGlobalError
!= FormulaError::NONE
)
2856 PushError( nGlobalError
);
2861 PushIllegalArgument();
2865 ScMatrixRef pResMat
= GetNewMat( static_cast<SCSIZE
>(nCol2
- nCol1
+ 1),
2866 static_cast<SCSIZE
>(nRow2
- nRow1
+ 1), true);
2869 PushError( FormulaError::MatrixSize
);
2873 /* TODO: we really should have a gap-aware cell iterator. */
2875 ScAddress
aAdr( 0, 0, nTab1
);
2876 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
2879 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
2882 ScRefCellValue
aCell(mrDoc
, aAdr
);
2883 pResMat
->PutBoolean( (aCell
.getType() == CELLTYPE_FORMULA
), i
,j
);
2890 PushMatrix( pResMat
);
2897 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2900 bRes
= (mrDoc
.GetCellType(aAdr
) == CELLTYPE_FORMULA
);
2906 nGlobalError
= FormulaError::NONE
;
2907 PushInt( int(bRes
) );
2910 void ScInterpreter::ScFormula()
2913 switch ( GetStackType() )
2916 if (IsInArrayContext())
2921 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
2922 if (nGlobalError
!= FormulaError::NONE
)
2927 SetError( FormulaError::IllegalArgument
);
2931 ScMatrixRef pResMat
= GetNewMat( nCol2
- nCol1
+ 1, nRow2
- nRow1
+ 1, true);
2935 /* TODO: use a column iterator instead? */
2937 ScAddress
aAdr(0,0,nTab1
);
2938 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
2941 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
2944 ScRefCellValue
aCell(mrDoc
, aAdr
);
2945 switch (aCell
.getType())
2947 case CELLTYPE_FORMULA
:
2948 aFormula
= aCell
.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED
, &mrContext
);
2949 pResMat
->PutString( mrStrPool
.intern( aFormula
), i
,j
);
2952 pResMat
->PutError( FormulaError::NotAvailable
, i
,j
);
2960 PushMatrix( pResMat
);
2967 if ( !PopDoubleRefOrSingleRef( aAdr
) )
2970 ScRefCellValue
aCell(mrDoc
, aAdr
);
2971 switch (aCell
.getType())
2973 case CELLTYPE_FORMULA
:
2974 aFormula
= aCell
.getFormula()->GetFormula(formula::FormulaGrammar::GRAM_UNSPECIFIED
, &mrContext
);
2977 SetError( FormulaError::NotAvailable
);
2983 SetError( FormulaError::NotAvailable
);
2985 PushString( aFormula
);
2988 void ScInterpreter::ScIsNV()
2990 nFuncFmtType
= SvNumFormatType::LOGICAL
;
2992 switch ( GetStackType() )
2998 bool bOk
= PopDoubleRefOrSingleRef( aAdr
);
2999 if ( nGlobalError
== FormulaError::NotAvailable
)
3003 ScRefCellValue
aCell(mrDoc
, aAdr
);
3004 FormulaError nErr
= GetCellErrCode(aCell
);
3005 bRes
= (nErr
== FormulaError::NotAvailable
);
3009 case svExternalSingleRef
:
3011 ScExternalRefCache::TokenRef pToken
;
3012 PopExternalSingleRef(pToken
);
3013 if (nGlobalError
== FormulaError::NotAvailable
||
3014 (pToken
&& pToken
->GetType() == svError
&& pToken
->GetError() == FormulaError::NotAvailable
))
3018 case svExternalDoubleRef
:
3021 ScMatrixRef pMat
= GetMatrix();
3024 else if ( !pJumpMatrix
)
3025 bRes
= (pMat
->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable
);
3028 SCSIZE nCols
, nRows
, nC
, nR
;
3029 pMat
->GetDimensions( nCols
, nRows
);
3030 pJumpMatrix
->GetPos( nC
, nR
);
3031 if ( nC
< nCols
&& nR
< nRows
)
3032 bRes
= (pMat
->GetErrorIfNotString( nC
, nR
) == FormulaError::NotAvailable
);
3038 if ( nGlobalError
== FormulaError::NotAvailable
)
3041 nGlobalError
= FormulaError::NONE
;
3042 PushInt( int(bRes
) );
3045 void ScInterpreter::ScIsErr()
3047 nFuncFmtType
= SvNumFormatType::LOGICAL
;
3049 switch ( GetStackType() )
3055 bool bOk
= PopDoubleRefOrSingleRef( aAdr
);
3056 if ( !bOk
|| (nGlobalError
!= FormulaError::NONE
&& nGlobalError
!= FormulaError::NotAvailable
) )
3060 ScRefCellValue
aCell(mrDoc
, aAdr
);
3061 FormulaError nErr
= GetCellErrCode(aCell
);
3062 bRes
= (nErr
!= FormulaError::NONE
&& nErr
!= FormulaError::NotAvailable
);
3066 case svExternalSingleRef
:
3068 ScExternalRefCache::TokenRef pToken
;
3069 PopExternalSingleRef(pToken
);
3070 if ((nGlobalError
!= FormulaError::NONE
&& nGlobalError
!= FormulaError::NotAvailable
) || !pToken
||
3071 (pToken
->GetType() == svError
&& pToken
->GetError() != FormulaError::NotAvailable
))
3075 case svExternalDoubleRef
:
3078 ScMatrixRef pMat
= GetMatrix();
3079 if ( nGlobalError
!= FormulaError::NONE
|| !pMat
)
3080 bRes
= ((nGlobalError
!= FormulaError::NONE
&& nGlobalError
!= FormulaError::NotAvailable
) || !pMat
);
3081 else if ( !pJumpMatrix
)
3083 FormulaError nErr
= pMat
->GetErrorIfNotString( 0, 0);
3084 bRes
= (nErr
!= FormulaError::NONE
&& nErr
!= FormulaError::NotAvailable
);
3088 SCSIZE nCols
, nRows
, nC
, nR
;
3089 pMat
->GetDimensions( nCols
, nRows
);
3090 pJumpMatrix
->GetPos( nC
, nR
);
3091 if ( nC
< nCols
&& nR
< nRows
)
3093 FormulaError nErr
= pMat
->GetErrorIfNotString( nC
, nR
);
3094 bRes
= (nErr
!= FormulaError::NONE
&& nErr
!= FormulaError::NotAvailable
);
3101 if ( nGlobalError
!= FormulaError::NONE
&& nGlobalError
!= FormulaError::NotAvailable
)
3104 nGlobalError
= FormulaError::NONE
;
3105 PushInt( int(bRes
) );
3108 void ScInterpreter::ScIsError()
3110 nFuncFmtType
= SvNumFormatType::LOGICAL
;
3112 switch ( GetStackType() )
3118 if ( !PopDoubleRefOrSingleRef( aAdr
) )
3123 if ( nGlobalError
!= FormulaError::NONE
)
3127 ScRefCellValue
aCell(mrDoc
, aAdr
);
3128 bRes
= (GetCellErrCode(aCell
) != FormulaError::NONE
);
3132 case svExternalSingleRef
:
3134 ScExternalRefCache::TokenRef pToken
;
3135 PopExternalSingleRef(pToken
);
3136 if (nGlobalError
!= FormulaError::NONE
|| pToken
->GetType() == svError
)
3140 case svExternalDoubleRef
:
3143 ScMatrixRef pMat
= GetMatrix();
3144 if ( nGlobalError
!= FormulaError::NONE
|| !pMat
)
3146 else if ( !pJumpMatrix
)
3147 bRes
= (pMat
->GetErrorIfNotString( 0, 0) != FormulaError::NONE
);
3150 SCSIZE nCols
, nRows
, nC
, nR
;
3151 pMat
->GetDimensions( nCols
, nRows
);
3152 pJumpMatrix
->GetPos( nC
, nR
);
3153 if ( nC
< nCols
&& nR
< nRows
)
3154 bRes
= (pMat
->GetErrorIfNotString( nC
, nR
) != FormulaError::NONE
);
3160 if ( nGlobalError
!= FormulaError::NONE
)
3163 nGlobalError
= FormulaError::NONE
;
3164 PushInt( int(bRes
) );
3167 bool ScInterpreter::IsEven()
3169 nFuncFmtType
= SvNumFormatType::LOGICAL
;
3172 switch ( GetStackType() )
3178 if ( !PopDoubleRefOrSingleRef( aAdr
) )
3181 ScRefCellValue
aCell(mrDoc
, aAdr
);
3182 FormulaError nErr
= GetCellErrCode(aCell
);
3183 if (nErr
!= FormulaError::NONE
)
3187 switch (aCell
.getType())
3189 case CELLTYPE_VALUE
:
3190 fVal
= GetCellValue(aAdr
, aCell
);
3193 case CELLTYPE_FORMULA
:
3194 if (aCell
.getFormula()->IsValue())
3196 fVal
= GetCellValue(aAdr
, aCell
);
3212 case svExternalSingleRef
:
3214 ScExternalRefCache::TokenRef pToken
;
3215 PopExternalSingleRef(pToken
);
3216 if (nGlobalError
== FormulaError::NONE
&& pToken
->GetType() == svDouble
)
3218 fVal
= pToken
->GetDouble();
3223 case svExternalDoubleRef
:
3226 ScMatrixRef pMat
= GetMatrix();
3229 else if ( !pJumpMatrix
)
3231 bRes
= pMat
->IsValue( 0, 0);
3233 fVal
= pMat
->GetDouble( 0, 0);
3237 SCSIZE nCols
, nRows
, nC
, nR
;
3238 pMat
->GetDimensions( nCols
, nRows
);
3239 pJumpMatrix
->GetPos( nC
, nR
);
3240 if ( nC
< nCols
&& nR
< nRows
)
3242 bRes
= pMat
->IsValue( nC
, nR
);
3244 fVal
= pMat
->GetDouble( nC
, nR
);
3247 SetError( FormulaError::NoValue
);
3255 SetError( FormulaError::IllegalParameter
);
3257 bRes
= ( fmod( ::rtl::math::approxFloor( fabs( fVal
) ), 2.0 ) < 0.5 );
3261 void ScInterpreter::ScIsEven()
3263 PushInt( int(IsEven()) );
3266 void ScInterpreter::ScIsOdd()
3268 PushInt( int(!IsEven()) );
3271 void ScInterpreter::ScN()
3273 FormulaError nErr
= nGlobalError
;
3274 nGlobalError
= FormulaError::NONE
;
3275 // Temporarily override the ConvertStringToValue() error for
3276 // GetCellValue() / GetCellValueOrZero()
3277 FormulaError nSErr
= mnStringNoValueError
;
3278 mnStringNoValueError
= FormulaError::CellNoValue
;
3279 double fVal
= GetDouble();
3280 mnStringNoValueError
= nSErr
;
3281 if (nErr
!= FormulaError::NONE
)
3282 nGlobalError
= nErr
; // preserve previous error if any
3283 else if (nGlobalError
== FormulaError::CellNoValue
)
3284 nGlobalError
= FormulaError::NONE
; // reset temporary detection error
3288 void ScInterpreter::ScTrim()
3290 // Doesn't only trim but also removes duplicated blanks within!
3291 OUString aVal
= comphelper::string::strip(GetString().getString(), ' ');
3292 OUStringBuffer aStr
;
3293 const sal_Unicode
* p
= aVal
.getStr();
3294 const sal_Unicode
* const pEnd
= p
+ aVal
.getLength();
3297 if ( *p
!= ' ' || p
[-1] != ' ' ) // first can't be ' ', so -1 is fine
3301 PushString(aStr
.makeStringAndClear());
3304 void ScInterpreter::ScUpper()
3306 OUString aString
= ScGlobal::getCharClass().uppercase(GetString().getString());
3307 PushString(aString
);
3310 void ScInterpreter::ScProper()
3312 //2do: what to do with I18N-CJK ?!?
3313 OUStringBuffer
aStr(GetString().getString());
3314 const sal_Int32 nLen
= aStr
.getLength();
3317 OUString
aUpr(ScGlobal::getCharClass().uppercase(aStr
.toString()));
3318 OUString
aLwr(ScGlobal::getCharClass().lowercase(aStr
.toString()));
3321 while( nPos
< nLen
)
3323 OUString
aTmpStr( aStr
[nPos
-1] );
3324 if ( !ScGlobal::getCharClass().isLetter( aTmpStr
, 0 ) )
3325 aStr
[nPos
] = aUpr
[nPos
];
3327 aStr
[nPos
] = aLwr
[nPos
];
3331 PushString(aStr
.makeStringAndClear());
3334 void ScInterpreter::ScLower()
3336 OUString aString
= ScGlobal::getCharClass().lowercase(GetString().getString());
3337 PushString(aString
);
3340 void ScInterpreter::ScLen()
3342 OUString aStr
= GetString().getString();
3345 while ( nIdx
< aStr
.getLength() )
3347 aStr
.iterateCodePoints( &nIdx
);
3353 void ScInterpreter::ScT()
3355 switch ( GetStackType() )
3361 if ( !PopDoubleRefOrSingleRef( aAdr
) )
3366 bool bValue
= false;
3367 ScRefCellValue
aCell(mrDoc
, aAdr
);
3368 if (GetCellErrCode(aCell
) == FormulaError::NONE
)
3370 switch (aCell
.getType())
3372 case CELLTYPE_VALUE
:
3375 case CELLTYPE_FORMULA
:
3376 bValue
= aCell
.getFormula()->IsValue();
3383 PushString(OUString());
3387 svl::SharedString aStr
;
3388 GetCellString(aStr
, aCell
);
3394 case svExternalSingleRef
:
3395 case svExternalDoubleRef
:
3398 svl::SharedString aStr
;
3399 ScMatValType nMatValType
= GetDoubleOrStringFromMatrix( fVal
, aStr
);
3400 if (ScMatrix::IsValueType( nMatValType
))
3401 PushString(svl::SharedString::getEmptyString());
3409 PushString( OUString() );
3416 PushError( FormulaError::UnknownOpCode
);
3420 void ScInterpreter::ScValue()
3422 OUString aInputString
;
3425 switch ( GetRawStackType() )
3433 return; // leave on stack
3439 if ( !PopDoubleRefOrSingleRef( aAdr
) )
3444 ScRefCellValue
aCell(mrDoc
, aAdr
);
3445 if (aCell
.hasString())
3447 svl::SharedString aSS
;
3448 GetCellString(aSS
, aCell
);
3449 aInputString
= aSS
.getString();
3451 else if (aCell
.hasNumeric())
3453 PushDouble( GetCellValue(aAdr
, aCell
) );
3465 svl::SharedString aSS
;
3466 ScMatValType nType
= GetDoubleOrStringFromMatrix( fVal
,
3468 aInputString
= aSS
.getString();
3471 case ScMatValType::Empty
:
3474 case ScMatValType::Value
:
3475 case ScMatValType::Boolean
:
3478 case ScMatValType::String
:
3482 PushIllegalArgument();
3487 aInputString
= GetString().getString();
3491 sal_uInt32 nFIndex
= 0; // 0 for default locale
3492 if (mrContext
.NFIsNumberFormat(aInputString
, nFIndex
, fVal
))
3495 PushIllegalArgument();
3499 void ScInterpreter::ScNumberValue()
3502 sal_uInt8 nParamCount
= GetByte();
3503 if ( !MustHaveParamCount( nParamCount
, 1, 3 ) )
3506 OUString aInputString
;
3507 OUString aGroupSeparator
;
3508 sal_Unicode cDecimalSeparator
= 0;
3510 if ( nParamCount
== 3 )
3511 aGroupSeparator
= GetString().getString();
3513 if ( nParamCount
>= 2 )
3515 OUString aDecimalSeparator
= GetString().getString();
3516 if ( aDecimalSeparator
.getLength() == 1 )
3517 cDecimalSeparator
= aDecimalSeparator
[ 0 ];
3520 PushIllegalArgument(); //if given, separator length must be 1
3525 if ( cDecimalSeparator
&& aGroupSeparator
.indexOf( cDecimalSeparator
) != -1 )
3527 PushIllegalArgument(); //decimal separator cannot appear in group separator
3531 switch (GetStackType())
3534 return; // leave on stack
3536 aInputString
= GetString().getString();
3538 if ( nGlobalError
!= FormulaError::NONE
)
3540 PushError( nGlobalError
);
3543 if ( aInputString
.isEmpty() )
3545 if ( maCalcConfig
.mbEmptyStringAsZero
)
3552 sal_Int32 nDecSep
= aInputString
.indexOf( cDecimalSeparator
);
3555 OUString
aTemporary( nDecSep
>= 0 ? aInputString
.copy( 0, nDecSep
) : aInputString
);
3556 sal_Int32 nIndex
= 0;
3557 while (nIndex
< aGroupSeparator
.getLength())
3559 sal_uInt32 nChar
= aGroupSeparator
.iterateCodePoints( &nIndex
);
3560 aTemporary
= aTemporary
.replaceAll( OUString( &nChar
, 1 ), "" );
3563 aInputString
= aTemporary
+ aInputString
.subView( nDecSep
);
3565 aInputString
= aTemporary
;
3568 for ( sal_Int32 i
= aInputString
.getLength(); --i
>= 0; )
3570 sal_Unicode c
= aInputString
[ i
];
3571 if ( c
== 0x0020 || c
== 0x0009 || c
== 0x000A || c
== 0x000D )
3572 aInputString
= aInputString
.replaceAt( i
, 1, u
"" ); // remove spaces etc.
3574 sal_Int32 nPercentCount
= 0;
3575 for ( sal_Int32 i
= aInputString
.getLength() - 1; i
>= 0 && aInputString
[ i
] == 0x0025; i
-- )
3577 aInputString
= aInputString
.replaceAt( i
, 1, u
"" ); // remove and count trailing '%'
3581 rtl_math_ConversionStatus eStatus
;
3582 sal_Int32 nParseEnd
;
3583 double fVal
= ::rtl::math::stringToDouble( aInputString
, cDecimalSeparator
, 0, &eStatus
, &nParseEnd
);
3584 if ( eStatus
== rtl_math_ConversionStatus_Ok
&& nParseEnd
== aInputString
.getLength() )
3587 fVal
*= pow( 10.0, -(nPercentCount
* 2)); // process '%' from input string
3594 static bool lcl_ScInterpreter_IsPrintable( sal_uInt32 nCodePoint
)
3596 return ( !u_isISOControl(nCodePoint
) /*not in Cc*/
3597 && u_isdefined(nCodePoint
) /*not in Cn*/ );
3601 void ScInterpreter::ScClean()
3603 OUString aStr
= GetString().getString();
3605 OUStringBuffer
aBuf( aStr
.getLength() );
3607 while ( nIdx
< aStr
.getLength() )
3609 sal_uInt32 c
= aStr
.iterateCodePoints( &nIdx
);
3610 if ( lcl_ScInterpreter_IsPrintable( c
) )
3611 aBuf
.appendUtf32( c
);
3613 PushString( aBuf
.makeStringAndClear() );
3617 void ScInterpreter::ScCode()
3619 //2do: make it full range unicode?
3620 OUString aStr
= GetString().getString();
3625 //"classic" ByteString conversion flags
3626 const sal_uInt32 convertFlags
=
3627 RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE
|
3628 RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE
|
3629 RTL_UNICODETOTEXT_FLAGS_FLUSH
|
3630 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT
|
3631 RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT
|
3632 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE
;
3633 PushInt( static_cast<unsigned char>(OUStringToOString(OUStringChar(aStr
[0]), osl_getThreadTextEncoding(), convertFlags
).toChar()) );
3637 void ScInterpreter::ScChar()
3639 //2do: make it full range unicode?
3640 double fVal
= GetDouble();
3641 if (fVal
< 0.0 || fVal
>= 256.0)
3642 PushIllegalArgument();
3645 //"classic" ByteString conversion flags
3646 const sal_uInt32 convertFlags
=
3647 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT
|
3648 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT
|
3649 RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT
;
3651 char cEncodedChar
= static_cast<char>(fVal
);
3652 OUString
aStr(&cEncodedChar
, 1, osl_getThreadTextEncoding(), convertFlags
);
3657 /* #i70213# fullwidth/halfwidth conversion provided by
3658 * Takashi Nakamoto <bluedwarf@ooo>
3659 * erAck: added Excel compatibility conversions as seen in issue's test case. */
3661 static OUString
lcl_convertIntoHalfWidth( const OUString
& rStr
)
3663 // Make the initialization thread-safe. Since another function needs to be called, move it all to another
3664 // function and thread-safely initialize a static reference in this function.
3665 auto init
= []() -> utl::TransliterationWrapper
&
3667 static utl::TransliterationWrapper
trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE
);
3668 trans
.loadModuleByImplName( u
"FULLWIDTH_HALFWIDTH_LIKE_ASC"_ustr
, LANGUAGE_SYSTEM
);
3671 static utl::TransliterationWrapper
& aTrans( init());
3672 return aTrans
.transliterate( rStr
, 0, sal_uInt16( rStr
.getLength() ) );
3675 static OUString
lcl_convertIntoFullWidth( const OUString
& rStr
)
3677 auto init
= []() -> utl::TransliterationWrapper
&
3679 static utl::TransliterationWrapper
trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE
);
3680 trans
.loadModuleByImplName( u
"HALFWIDTH_FULLWIDTH_LIKE_JIS"_ustr
, LANGUAGE_SYSTEM
);
3683 static utl::TransliterationWrapper
& aTrans( init());
3684 return aTrans
.transliterate( rStr
, 0, sal_uInt16( rStr
.getLength() ) );
3688 * Summary: Converts half-width to full-width ASCII and katakana characters.
3689 * Semantics: Conversion is done for half-width ASCII and katakana characters,
3690 * other characters are simply copied from T to the result. This is the
3691 * complementary function to ASC.
3692 * For references regarding halfwidth and fullwidth characters see
3693 * http://www.unicode.org/reports/tr11/
3694 * http://www.unicode.org/charts/charindex2.html#H
3695 * http://www.unicode.org/charts/charindex2.html#F
3697 void ScInterpreter::ScJis()
3699 if (MustHaveParamCount( GetByte(), 1))
3700 PushString( lcl_convertIntoFullWidth( GetString().getString()));
3704 * Summary: Converts full-width to half-width ASCII and katakana characters.
3705 * Semantics: Conversion is done for full-width ASCII and katakana characters,
3706 * other characters are simply copied from T to the result. This is the
3707 * complementary function to JIS.
3709 void ScInterpreter::ScAsc()
3711 if (MustHaveParamCount( GetByte(), 1))
3712 PushString( lcl_convertIntoHalfWidth( GetString().getString()));
3715 void ScInterpreter::ScUnicode()
3717 if ( MustHaveParamCount( GetByte(), 1 ) )
3719 OUString aStr
= GetString().getString();
3721 PushIllegalParameter();
3724 PushDouble(aStr
.iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
3729 void ScInterpreter::ScUnichar()
3731 if ( MustHaveParamCount( GetByte(), 1 ) )
3733 sal_uInt32 nCodePoint
= GetUInt32();
3734 if (nGlobalError
!= FormulaError::NONE
|| !rtl::isUnicodeCodePoint(nCodePoint
))
3735 PushIllegalArgument();
3738 OUString
aStr( &nCodePoint
, 1 );
3744 bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef
& xResMat
, SCSIZE nMatRows
, double fCurrent
,
3745 const std::function
<void( SCSIZE i
, double fCurrent
)>& MatOpFunc
, bool bDoMatOp
)
3747 const ScRefListToken
* p
= dynamic_cast<const ScRefListToken
*>(pStack
[sp
-1]);
3748 if (!p
|| !p
->IsArrayResult())
3753 // Create and init all elements with current value.
3754 assert(nMatRows
> 0);
3755 xResMat
= GetNewMat( 1, nMatRows
, true);
3756 xResMat
->FillDouble( fCurrent
, 0,0, 0,nMatRows
-1);
3760 // Current value and values from vector are operands
3761 // for each vector position.
3762 for (SCSIZE i
=0; i
< nMatRows
; ++i
)
3764 MatOpFunc( i
, fCurrent
);
3770 void ScInterpreter::ScMin( bool bTextAsZero
)
3772 short nParamCount
= GetByte();
3773 if (!MustHaveParamCountMin( nParamCount
, 1))
3776 ScMatrixRef xResMat
;
3777 double nMin
= ::std::numeric_limits
<double>::max();
3778 auto MatOpFunc
= [&xResMat
]( SCSIZE i
, double fCurMin
)
3780 double fVecRes
= xResMat
->GetDouble(0,i
);
3781 if (fVecRes
> fCurMin
)
3782 xResMat
->PutDouble( fCurMin
, 0,i
);
3784 const SCSIZE nMatRows
= GetRefListArrayMaxSize( nParamCount
);
3785 size_t nRefArrayPos
= std::numeric_limits
<size_t>::max();
3790 size_t nRefInList
= 0;
3791 while (nParamCount
-- > 0)
3793 switch (GetStackType())
3798 if (nMin
> nVal
) nMin
= nVal
;
3799 nFuncFmtType
= SvNumFormatType::NUMBER
;
3804 PopSingleRef( aAdr
);
3805 ScRefCellValue
aCell(mrDoc
, aAdr
);
3806 if (aCell
.hasNumeric())
3808 nVal
= GetCellValue(aAdr
, aCell
);
3810 if (nMin
> nVal
) nMin
= nVal
;
3812 else if (bTextAsZero
&& aCell
.hasString())
3821 // bDoMatOp only for non-array value when switching to
3823 if (SwitchToArrayRefList( xResMat
, nMatRows
, nMin
, MatOpFunc
,
3824 nRefArrayPos
== std::numeric_limits
<size_t>::max()))
3826 nRefArrayPos
= nRefInList
;
3832 FormulaError nErr
= FormulaError::NONE
;
3833 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
3834 ScValueIterator
aValIter( mrContext
, aRange
, mnSubTotalFlags
, bTextAsZero
);
3835 if (aValIter
.GetFirst(nVal
, nErr
))
3839 aValIter
.GetCurNumFmtInfo( nFuncFmtType
, nFuncFmtIndex
);
3840 while ((nErr
== FormulaError::NONE
) && aValIter
.GetNext(nVal
, nErr
))
3847 if (nRefArrayPos
!= std::numeric_limits
<size_t>::max())
3849 // Update vector element with current value.
3850 MatOpFunc( nRefArrayPos
, nMin
);
3853 nMin
= std::numeric_limits
<double>::max();
3855 nRefArrayPos
= std::numeric_limits
<size_t>::max();
3860 case svExternalSingleRef
:
3861 case svExternalDoubleRef
:
3863 ScMatrixRef pMat
= GetMatrix();
3866 nFuncFmtType
= SvNumFormatType::NUMBER
;
3867 nVal
= pMat
->GetMinValue(bTextAsZero
, bool(mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
));
3882 SetError(FormulaError::IllegalParameter
);
3887 SetError(FormulaError::IllegalParameter
);
3893 // Include value of last non-references-array type and calculate final result.
3894 if (nMin
< std::numeric_limits
<double>::max())
3896 for (SCSIZE i
=0; i
< nMatRows
; ++i
)
3898 MatOpFunc( i
, nMin
);
3903 /* TODO: the awkward "no value is minimum 0.0" is likely the case
3904 * if a value is numeric_limits::max. Still, that could be a valid
3905 * minimum value as well, but nVal and nMin had been reset after
3906 * the last svRefList... so we may lie here. */
3907 for (SCSIZE i
=0; i
< nMatRows
; ++i
)
3909 double fVecRes
= xResMat
->GetDouble(0,i
);
3910 if (fVecRes
== std::numeric_limits
<double>::max())
3911 xResMat
->PutDouble( 0.0, 0,i
);
3914 PushMatrix( xResMat
);
3918 if (!std::isfinite(nVal
))
3919 PushError( GetDoubleErrorValue( nVal
));
3920 else if ( nVal
< nMin
)
3921 PushDouble(0.0); // zero or only empty arguments
3927 void ScInterpreter::ScMax( bool bTextAsZero
)
3929 short nParamCount
= GetByte();
3930 if (!MustHaveParamCountMin( nParamCount
, 1))
3933 ScMatrixRef xResMat
;
3934 double nMax
= std::numeric_limits
<double>::lowest();
3935 auto MatOpFunc
= [&xResMat
]( SCSIZE i
, double fCurMax
)
3937 double fVecRes
= xResMat
->GetDouble(0,i
);
3938 if (fVecRes
< fCurMax
)
3939 xResMat
->PutDouble( fCurMax
, 0,i
);
3941 const SCSIZE nMatRows
= GetRefListArrayMaxSize( nParamCount
);
3942 size_t nRefArrayPos
= std::numeric_limits
<size_t>::max();
3947 size_t nRefInList
= 0;
3948 while (nParamCount
-- > 0)
3950 switch (GetStackType())
3955 if (nMax
< nVal
) nMax
= nVal
;
3956 nFuncFmtType
= SvNumFormatType::NUMBER
;
3961 PopSingleRef( aAdr
);
3962 ScRefCellValue
aCell(mrDoc
, aAdr
);
3963 if (aCell
.hasNumeric())
3965 nVal
= GetCellValue(aAdr
, aCell
);
3967 if (nMax
< nVal
) nMax
= nVal
;
3969 else if (bTextAsZero
&& aCell
.hasString())
3978 // bDoMatOp only for non-array value when switching to
3980 if (SwitchToArrayRefList( xResMat
, nMatRows
, nMax
, MatOpFunc
,
3981 nRefArrayPos
== std::numeric_limits
<size_t>::max()))
3983 nRefArrayPos
= nRefInList
;
3989 FormulaError nErr
= FormulaError::NONE
;
3990 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
3991 ScValueIterator
aValIter( mrContext
, aRange
, mnSubTotalFlags
, bTextAsZero
);
3992 if (aValIter
.GetFirst(nVal
, nErr
))
3996 aValIter
.GetCurNumFmtInfo( nFuncFmtType
, nFuncFmtIndex
);
3997 while ((nErr
== FormulaError::NONE
) && aValIter
.GetNext(nVal
, nErr
))
4004 if (nRefArrayPos
!= std::numeric_limits
<size_t>::max())
4006 // Update vector element with current value.
4007 MatOpFunc( nRefArrayPos
, nMax
);
4010 nMax
= std::numeric_limits
<double>::lowest();
4012 nRefArrayPos
= std::numeric_limits
<size_t>::max();
4017 case svExternalSingleRef
:
4018 case svExternalDoubleRef
:
4020 ScMatrixRef pMat
= GetMatrix();
4023 nFuncFmtType
= SvNumFormatType::NUMBER
;
4024 nVal
= pMat
->GetMaxValue(bTextAsZero
, bool(mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
));
4039 SetError(FormulaError::IllegalParameter
);
4044 SetError(FormulaError::IllegalParameter
);
4050 // Include value of last non-references-array type and calculate final result.
4051 if (nMax
> std::numeric_limits
<double>::lowest())
4053 for (SCSIZE i
=0; i
< nMatRows
; ++i
)
4055 MatOpFunc( i
, nMax
);
4060 /* TODO: the awkward "no value is maximum 0.0" is likely the case
4061 * if a value is numeric_limits::lowest. Still, that could be a
4062 * valid maximum value as well, but nVal and nMax had been reset
4063 * after the last svRefList... so we may lie here. */
4064 for (SCSIZE i
=0; i
< nMatRows
; ++i
)
4066 double fVecRes
= xResMat
->GetDouble(0,i
);
4067 if (fVecRes
== -std::numeric_limits
<double>::max())
4068 xResMat
->PutDouble( 0.0, 0,i
);
4071 PushMatrix( xResMat
);
4075 if (!std::isfinite(nVal
))
4076 PushError( GetDoubleErrorValue( nVal
));
4077 else if ( nVal
> nMax
)
4078 PushDouble(0.0); // zero or only empty arguments
4084 void ScInterpreter::GetStVarParams( bool bTextAsZero
, double(*VarResult
)( double fVal
, size_t nValCount
) )
4086 short nParamCount
= GetByte();
4087 const SCSIZE nMatRows
= GetRefListArrayMaxSize( nParamCount
);
4089 struct ArrayRefListValue
4091 std::vector
<double> mvValues
;
4093 ArrayRefListValue() = default;
4094 double get() const { return mfSum
.get(); }
4096 std::vector
<ArrayRefListValue
> vArrayValues
;
4098 std::vector
<double> values
;
4099 KahanSum fSum
= 0.0;
4103 size_t nRefInList
= 0;
4104 while (nGlobalError
== FormulaError::NONE
&& nParamCount
-- > 0)
4106 switch (GetStackType())
4111 if (nGlobalError
== FormulaError::NONE
)
4113 values
.push_back(fVal
);
4120 PopSingleRef( aAdr
);
4121 ScRefCellValue
aCell(mrDoc
, aAdr
);
4122 if (aCell
.hasNumeric())
4124 fVal
= GetCellValue(aAdr
, aCell
);
4125 if (nGlobalError
== FormulaError::NONE
)
4127 values
.push_back(fVal
);
4131 else if (bTextAsZero
&& aCell
.hasString())
4133 values
.push_back(0.0);
4139 const ScRefListToken
* p
= dynamic_cast<const ScRefListToken
*>(pStack
[sp
-1]);
4140 if (p
&& p
->IsArrayResult())
4142 size_t nRefArrayPos
= nRefInList
;
4143 if (vArrayValues
.empty())
4145 // Create and init all elements with current value.
4146 assert(nMatRows
> 0);
4147 vArrayValues
.resize(nMatRows
);
4148 for (ArrayRefListValue
& it
: vArrayValues
)
4150 it
.mvValues
= values
;
4156 // Current value and values from vector are operands
4157 // for each vector position.
4158 for (ArrayRefListValue
& it
: vArrayValues
)
4160 it
.mvValues
.insert( it
.mvValues
.end(), values
.begin(), values
.end());
4164 ArrayRefListValue
& rArrayValue
= vArrayValues
[nRefArrayPos
];
4165 FormulaError nErr
= FormulaError::NONE
;
4166 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
4167 ScValueIterator
aValIter( mrContext
, aRange
, mnSubTotalFlags
, bTextAsZero
);
4168 if (aValIter
.GetFirst(fVal
, nErr
))
4172 rArrayValue
.mvValues
.push_back(fVal
);
4173 rArrayValue
.mfSum
+= fVal
;
4175 while ((nErr
== FormulaError::NONE
) && aValIter
.GetNext(fVal
, nErr
));
4177 if ( nErr
!= FormulaError::NONE
)
4179 rArrayValue
.mfSum
= CreateDoubleError( nErr
);
4182 std::vector
<double>().swap(values
);
4190 FormulaError nErr
= FormulaError::NONE
;
4191 PopDoubleRef( aRange
, nParamCount
, nRefInList
);
4192 ScValueIterator
aValIter( mrContext
, aRange
, mnSubTotalFlags
, bTextAsZero
);
4193 if (aValIter
.GetFirst(fVal
, nErr
))
4197 values
.push_back(fVal
);
4200 while ((nErr
== FormulaError::NONE
) && aValIter
.GetNext(fVal
, nErr
));
4202 if ( nErr
!= FormulaError::NONE
)
4208 case svExternalSingleRef
:
4209 case svExternalDoubleRef
:
4212 ScMatrixRef pMat
= GetMatrix();
4215 const bool bIgnoreErrVal
= bool(mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
);
4217 pMat
->GetDimensions(nC
, nR
);
4218 for (SCSIZE nMatCol
= 0; nMatCol
< nC
; nMatCol
++)
4220 for (SCSIZE nMatRow
= 0; nMatRow
< nR
; nMatRow
++)
4222 if (!pMat
->IsStringOrEmpty(nMatCol
,nMatRow
))
4224 fVal
= pMat
->GetDouble(nMatCol
,nMatRow
);
4225 if (nGlobalError
== FormulaError::NONE
)
4227 values
.push_back(fVal
);
4230 else if (bIgnoreErrVal
)
4231 nGlobalError
= FormulaError::NONE
;
4233 else if ( bTextAsZero
)
4235 values
.push_back(0.0);
4247 values
.push_back(0.0);
4250 SetError(FormulaError::IllegalParameter
);
4255 SetError(FormulaError::IllegalParameter
);
4259 if (!vArrayValues
.empty())
4261 // Include value of last non-references-array type and calculate final result.
4262 if (!values
.empty())
4264 for (auto & it
: vArrayValues
)
4266 it
.mvValues
.insert( it
.mvValues
.end(), values
.begin(), values
.end());
4270 ScMatrixRef xResMat
= GetNewMat( 1, nMatRows
, true);
4271 for (SCSIZE r
=0; r
< nMatRows
; ++r
)
4273 ::std::vector
<double>::size_type n
= vArrayValues
[r
].mvValues
.size();
4275 xResMat
->PutError( FormulaError::DivisionByZero
, 0, r
);
4278 ArrayRefListValue
& rArrayValue
= vArrayValues
[r
];
4280 const double vMean
= rArrayValue
.get() / n
;
4281 for (::std::vector
<double>::size_type i
= 0; i
< n
; i
++)
4282 vSum
+= ::rtl::math::approxSub( rArrayValue
.mvValues
[i
], vMean
) *
4283 ::rtl::math::approxSub( rArrayValue
.mvValues
[i
], vMean
);
4284 xResMat
->PutDouble( VarResult( vSum
, n
), 0, r
);
4287 PushMatrix( xResMat
);
4291 ::std::vector
<double>::size_type n
= values
.size();
4293 SetError( FormulaError::DivisionByZero
);
4295 if (nGlobalError
== FormulaError::NONE
)
4297 const double vMean
= fSum
.get() / n
;
4298 for (::std::vector
<double>::size_type i
= 0; i
< n
; i
++)
4299 vSum
+= ::rtl::math::approxSub( values
[i
], vMean
) * ::rtl::math::approxSub( values
[i
], vMean
);
4301 PushDouble( VarResult( vSum
, n
));
4305 void ScInterpreter::ScVar( bool bTextAsZero
)
4307 auto VarResult
= []( double fVal
, size_t nValCount
)
4310 return CreateDoubleError( FormulaError::DivisionByZero
);
4312 return fVal
/ (nValCount
- 1);
4314 GetStVarParams( bTextAsZero
, VarResult
);
4317 void ScInterpreter::ScVarP( bool bTextAsZero
)
4319 auto VarResult
= []( double fVal
, size_t nValCount
)
4321 return sc::div( fVal
, nValCount
);
4323 GetStVarParams( bTextAsZero
, VarResult
);
4327 void ScInterpreter::ScStDev( bool bTextAsZero
)
4329 auto VarResult
= []( double fVal
, size_t nValCount
)
4332 return CreateDoubleError( FormulaError::DivisionByZero
);
4334 return sqrt( fVal
/ (nValCount
- 1));
4336 GetStVarParams( bTextAsZero
, VarResult
);
4339 void ScInterpreter::ScStDevP( bool bTextAsZero
)
4341 auto VarResult
= []( double fVal
, size_t nValCount
)
4344 return CreateDoubleError( FormulaError::DivisionByZero
);
4346 return sqrt( fVal
/ nValCount
);
4348 GetStVarParams( bTextAsZero
, VarResult
);
4350 /* this was: PushDouble( sqrt( div( nVal, nValCount)));
4352 * Besides that the special NAN gets lost in the call through sqrt(),
4353 * unxlngi6.pro then looped back and forth somewhere between div() and
4354 * ::rtl::math::setNan(). Tests showed that
4356 * sqrt( div( 1, 0));
4358 * produced a loop, but
4360 * double f1 = div( 1, 0);
4363 * was fine. There seems to be some compiler optimization problem. It does
4364 * not occur when compiled with debug=t.
4368 void ScInterpreter::ScColumns()
4370 sal_uInt8 nParamCount
= GetByte();
4378 while (nParamCount
-- > 0)
4380 switch ( GetStackType() )
4387 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
4388 nVal
+= static_cast<sal_uLong
>(nTab2
- nTab1
+ 1) *
4389 static_cast<sal_uLong
>(nCol2
- nCol1
+ 1);
4393 ScMatrixRef pMat
= PopMatrix();
4397 pMat
->GetDimensions(nC
, nR
);
4402 case svExternalSingleRef
:
4406 case svExternalDoubleRef
:
4410 ScComplexRefData aRef
;
4411 PopExternalDoubleRef( nFileId
, aTabName
, aRef
);
4412 ScRange aAbs
= aRef
.toAbs(mrDoc
, aPos
);
4413 nVal
+= static_cast<sal_uLong
>(aAbs
.aEnd
.Tab() - aAbs
.aStart
.Tab() + 1) *
4414 static_cast<sal_uLong
>(aAbs
.aEnd
.Col() - aAbs
.aStart
.Col() + 1);
4419 SetError(FormulaError::IllegalParameter
);
4422 PushDouble(static_cast<double>(nVal
));
4425 void ScInterpreter::ScRows()
4427 sal_uInt8 nParamCount
= GetByte();
4435 while (nParamCount
-- > 0)
4437 switch ( GetStackType() )
4444 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
4445 nVal
+= static_cast<sal_uLong
>(nTab2
- nTab1
+ 1) *
4446 static_cast<sal_uLong
>(nRow2
- nRow1
+ 1);
4450 ScMatrixRef pMat
= PopMatrix();
4454 pMat
->GetDimensions(nC
, nR
);
4459 case svExternalSingleRef
:
4463 case svExternalDoubleRef
:
4467 ScComplexRefData aRef
;
4468 PopExternalDoubleRef( nFileId
, aTabName
, aRef
);
4469 ScRange aAbs
= aRef
.toAbs(mrDoc
, aPos
);
4470 nVal
+= static_cast<sal_uLong
>(aAbs
.aEnd
.Tab() - aAbs
.aStart
.Tab() + 1) *
4471 static_cast<sal_uLong
>(aAbs
.aEnd
.Row() - aAbs
.aStart
.Row() + 1);
4476 SetError(FormulaError::IllegalParameter
);
4479 PushDouble(static_cast<double>(nVal
));
4482 void ScInterpreter::ScSheets()
4484 sal_uInt8 nParamCount
= GetByte();
4486 if ( nParamCount
== 0 )
4487 nVal
= mrDoc
.GetTableCount();
4497 while (nGlobalError
== FormulaError::NONE
&& nParamCount
-- > 0)
4499 switch ( GetStackType() )
4502 case svExternalSingleRef
:
4507 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
4508 nVal
+= static_cast<sal_uLong
>(nTab2
- nTab1
+ 1);
4510 case svExternalDoubleRef
:
4514 ScComplexRefData aRef
;
4515 PopExternalDoubleRef( nFileId
, aTabName
, aRef
);
4516 ScRange aAbs
= aRef
.toAbs(mrDoc
, aPos
);
4517 nVal
+= static_cast<sal_uLong
>(aAbs
.aEnd
.Tab() - aAbs
.aStart
.Tab() + 1);
4522 SetError( FormulaError::IllegalParameter
);
4526 PushDouble( static_cast<double>(nVal
) );
4529 void ScInterpreter::ScColumn()
4531 sal_uInt8 nParamCount
= GetByte();
4532 if ( !MustHaveParamCount( nParamCount
, 0, 1 ) )
4536 if (nParamCount
== 0)
4538 nVal
= aPos
.Col() + 1;
4544 pMyFormulaCell
->GetMatColsRows( nCols
, nRows
);
4548 // Happens if called via ScViewFunc::EnterMatrix()
4549 // ScFormulaCell::GetResultDimensions() as of course a
4550 // matrix result is not available yet.
4552 bMayBeScalar
= false;
4556 bMayBeScalar
= true;
4558 if (!bMayBeScalar
|| nCols
!= 1 || nRows
!= 1)
4560 ScMatrixRef pResMat
= GetNewMat( static_cast<SCSIZE
>(nCols
), 1, /*bEmpty*/true );
4563 for (SCCOL i
=0; i
< nCols
; ++i
)
4564 pResMat
->PutDouble( nVal
+ i
, static_cast<SCSIZE
>(i
), 0);
4565 PushMatrix( pResMat
);
4573 switch ( GetStackType() )
4580 PopSingleRef( nCol1
, nRow1
, nTab1
);
4581 nVal
= static_cast<double>(nCol1
+ 1);
4584 case svExternalSingleRef
:
4588 ScSingleRefData aRef
;
4589 PopExternalSingleRef( nFileId
, aTabName
, aRef
);
4590 ScAddress aAbsRef
= aRef
.toAbs(mrDoc
, aPos
);
4591 nVal
= static_cast<double>( aAbsRef
.Col() + 1 );
4596 case svExternalDoubleRef
:
4600 if ( GetStackType() == svDoubleRef
)
4606 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
4612 ScComplexRefData aRef
;
4613 PopExternalDoubleRef( nFileId
, aTabName
, aRef
);
4614 ScRange aAbs
= aRef
.toAbs(mrDoc
, aPos
);
4615 nCol1
= aAbs
.aStart
.Col();
4616 nCol2
= aAbs
.aEnd
.Col();
4620 ScMatrixRef pResMat
= GetNewMat(
4621 static_cast<SCSIZE
>(nCol2
-nCol1
+1), 1, /*bEmpty*/true);
4624 for (SCCOL i
= nCol1
; i
<= nCol2
; i
++)
4625 pResMat
->PutDouble(static_cast<double>(i
+1),
4626 static_cast<SCSIZE
>(i
-nCol1
), 0);
4627 PushMatrix(pResMat
);
4632 nVal
= static_cast<double>(nCol1
+ 1);
4636 SetError( FormulaError::IllegalParameter
);
4642 void ScInterpreter::ScRow()
4644 sal_uInt8 nParamCount
= GetByte();
4645 if ( !MustHaveParamCount( nParamCount
, 0, 1 ) )
4649 if (nParamCount
== 0)
4651 nVal
= aPos
.Row() + 1;
4657 pMyFormulaCell
->GetMatColsRows( nCols
, nRows
);
4661 // Happens if called via ScViewFunc::EnterMatrix()
4662 // ScFormulaCell::GetResultDimensions() as of course a
4663 // matrix result is not available yet.
4665 bMayBeScalar
= false;
4669 bMayBeScalar
= true;
4671 if (!bMayBeScalar
|| nCols
!= 1 || nRows
!= 1)
4673 ScMatrixRef pResMat
= GetNewMat( 1, static_cast<SCSIZE
>(nRows
), /*bEmpty*/true);
4676 for (SCROW i
=0; i
< nRows
; i
++)
4677 pResMat
->PutDouble( nVal
+ i
, 0, static_cast<SCSIZE
>(i
));
4678 PushMatrix( pResMat
);
4686 switch ( GetStackType() )
4693 PopSingleRef( nCol1
, nRow1
, nTab1
);
4694 nVal
= static_cast<double>(nRow1
+ 1);
4697 case svExternalSingleRef
:
4701 ScSingleRefData aRef
;
4702 PopExternalSingleRef( nFileId
, aTabName
, aRef
);
4703 ScAddress aAbsRef
= aRef
.toAbs(mrDoc
, aPos
);
4704 nVal
= static_cast<double>( aAbsRef
.Row() + 1 );
4708 case svExternalDoubleRef
:
4712 if ( GetStackType() == svDoubleRef
)
4718 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
4724 ScComplexRefData aRef
;
4725 PopExternalDoubleRef( nFileId
, aTabName
, aRef
);
4726 ScRange aAbs
= aRef
.toAbs(mrDoc
, aPos
);
4727 nRow1
= aAbs
.aStart
.Row();
4728 nRow2
= aAbs
.aEnd
.Row();
4732 ScMatrixRef pResMat
= GetNewMat( 1,
4733 static_cast<SCSIZE
>(nRow2
-nRow1
+1), /*bEmpty*/true);
4736 for (SCROW i
= nRow1
; i
<= nRow2
; i
++)
4737 pResMat
->PutDouble(static_cast<double>(i
+1), 0,
4738 static_cast<SCSIZE
>(i
-nRow1
));
4739 PushMatrix(pResMat
);
4744 nVal
= static_cast<double>(nRow1
+ 1);
4748 SetError( FormulaError::IllegalParameter
);
4754 void ScInterpreter::ScSheet()
4756 sal_uInt8 nParamCount
= GetByte();
4757 if ( !MustHaveParamCount( nParamCount
, 0, 1 ) )
4761 if ( nParamCount
== 0 )
4762 nVal
= aPos
.Tab() + 1;
4765 switch ( GetStackType() )
4769 svl::SharedString aStr
= PopString();
4770 if ( mrDoc
.GetTable(aStr
.getString(), nVal
))
4773 SetError( FormulaError::IllegalArgument
);
4781 PopSingleRef(nCol1
, nRow1
, nTab1
);
4793 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
4798 SetError( FormulaError::IllegalParameter
);
4800 if ( nGlobalError
!= FormulaError::NONE
)
4803 PushDouble( static_cast<double>(nVal
) );
4808 class VectorMatrixAccessor
4811 VectorMatrixAccessor(const ScMatrix
& rMat
, bool bColVec
) :
4812 mrMat(rMat
), mbColVec(bColVec
) {}
4814 bool IsEmpty(SCSIZE i
) const
4816 return mbColVec
? mrMat
.IsEmpty(0, i
) : mrMat
.IsEmpty(i
, 0);
4819 bool IsEmptyPath(SCSIZE i
) const
4821 return mbColVec
? mrMat
.IsEmptyPath(0, i
) : mrMat
.IsEmptyPath(i
, 0);
4824 bool IsValue(SCSIZE i
) const
4826 return mbColVec
? mrMat
.IsValue(0, i
) : mrMat
.IsValue(i
, 0);
4829 bool IsStringOrEmpty(SCSIZE i
) const
4831 return mbColVec
? mrMat
.IsStringOrEmpty(0, i
) : mrMat
.IsStringOrEmpty(i
, 0);
4834 double GetDouble(SCSIZE i
) const
4836 return mbColVec
? mrMat
.GetDouble(0, i
) : mrMat
.GetDouble(i
, 0);
4839 OUString
GetString(SCSIZE i
) const
4841 return mbColVec
? mrMat
.GetString(0, i
).getString() : mrMat
.GetString(i
, 0).getString();
4844 SCSIZE
GetElementCount() const
4847 mrMat
.GetDimensions(nC
, nR
);
4848 return mbColVec
? nR
: nC
;
4852 const ScMatrix
& mrMat
;
4856 /** returns -1 when the matrix value is smaller than the query value, 0 when
4857 they are equal, and 1 when the matrix value is larger than the query
4859 sal_Int32
lcl_CompareMatrix2Query(
4860 SCSIZE i
, const VectorMatrixAccessor
& rMat
, const ScQueryParam
& rParam
, const ScQueryEntry
& rEntry
, bool bMatchWholeCell
)
4862 if (rMat
.IsEmpty(i
))
4864 /* TODO: in case we introduced query for real empty this would have to
4866 return -1; // empty always less than anything else
4869 /* FIXME: what is an empty path (result of IF(false;true_path) in
4872 bool bByString
= rEntry
.GetQueryItem().meType
== ScQueryEntry::ByString
;
4873 if (rMat
.IsValue(i
))
4875 const double nVal1
= rMat
.GetDouble(i
);
4876 if (!std::isfinite(nVal1
))
4878 // XXX Querying for error values is not required, otherwise we'd
4879 // need to check here.
4880 return 1; // error always greater than numeric or string
4884 return -1; // numeric always less than string
4886 const double nVal2
= rEntry
.GetQueryItem().mfVal
;
4887 // XXX Querying for error values is not required, otherwise we'd need
4888 // to check here and move that check before the bByString check.
4892 return nVal1
< nVal2
? -1 : 1;
4896 return 1; // string always greater than numeric
4898 OUString aStr1
= rMat
.GetString(i
);
4899 OUString aStr2
= rEntry
.GetQueryItem().maString
.getString();
4901 // bRealWildOrRegExp
4902 if (rParam
.eSearchType
!= utl::SearchParam::SearchType::Normal
&&
4903 ((rEntry
.eOp
== SC_EQUAL
) || (rEntry
.eOp
== SC_NOT_EQUAL
)))
4905 sal_Int32 nStart
= 0;
4906 sal_Int32 nEnd
= aStr1
.getLength();
4908 bool bMatch
= rEntry
.GetSearchTextPtr(rParam
.eSearchType
, rParam
.bCaseSens
, bMatchWholeCell
)
4909 ->SearchForward(aStr1
, &nStart
, &nEnd
);
4910 // from 614 on, nEnd is behind the found text
4911 if (bMatch
&& bMatchWholeCell
4912 && (nStart
!= 0 || nEnd
!= aStr1
.getLength()))
4913 bMatch
= false; // RegExp must match entire cell string
4915 bool bOk
= ((rEntry
.eOp
== SC_NOT_EQUAL
) ? !bMatch
: bMatch
);
4918 return 0; // we have a WildOrRegExp match
4921 CollatorWrapper
& rCollator
= ScGlobal::GetCollator(rParam
.bCaseSens
);
4922 return rCollator
.compareString(aStr1
, aStr2
);
4925 /** returns -1 when matrix(i) value is smaller than matrix(j) value, 0 when
4926 they are equal, and 1 when larger */
4927 sal_Int32
lcl_Compare2MatrixCells( SCSIZE i
, const VectorMatrixAccessor
& rMat
, SCSIZE j
)
4929 // empty always less than anything else
4930 if (rMat
.IsEmpty(i
))
4931 return ( rMat
.IsEmpty(j
) ? 0 : -1 );
4932 else if (rMat
.IsEmpty(j
))
4935 bool bByString
= rMat
.IsStringOrEmpty(j
); // string, empty has already been handled
4936 if (rMat
.IsValue(i
))
4938 const double nVal1
= rMat
.GetDouble(i
);
4939 if (!std::isfinite(nVal1
))
4940 return 1; // error always greater than numeric or string
4943 return -1; // numeric always less than string
4945 const double nVal2
= rMat
.GetDouble(j
);
4949 return ( nVal1
< nVal2
? -1 : 1 );
4953 return 1; // string always greater than numeric
4955 return ScGlobal::GetCollator().compareString(rMat
.GetString(i
), rMat
.GetString(j
)); // case-insensitive
4958 /** returns the last item with the identical value as the original item
4960 void lcl_GetLastMatch( SCSIZE
& rIndex
, const VectorMatrixAccessor
& rMat
,
4963 if (rMat
.IsValue(rIndex
))
4965 double nVal
= rMat
.GetDouble(rIndex
);
4966 while (rIndex
< nMatCount
-1 && rMat
.IsValue(rIndex
+1) &&
4967 nVal
== rMat
.GetDouble(rIndex
+1))
4970 // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
4971 else if (rMat
.IsEmptyPath(rIndex
))
4973 while (rIndex
< nMatCount
-1 && rMat
.IsEmptyPath(rIndex
+1))
4976 else if (rMat
.IsEmpty(rIndex
))
4978 while (rIndex
< nMatCount
-1 && rMat
.IsEmpty(rIndex
+1))
4981 else if (rMat
.IsStringOrEmpty(rIndex
))
4983 OUString
aStr( rMat
.GetString(rIndex
));
4984 while (rIndex
< nMatCount
-1 && rMat
.IsStringOrEmpty(rIndex
+1) &&
4985 aStr
== rMat
.GetString(rIndex
+1))
4990 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4996 void ScInterpreter::ScMatch()
4998 sal_uInt8 nParamCount
= GetByte();
4999 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
5002 VectorSearchArguments vsa
;
5003 vsa
.nSearchOpCode
= SC_OPCODE_MATCH
;
5006 double fType
= ( nParamCount
== 3 ? GetDouble() : 1.0 );
5007 switch ( static_cast<int>(fType
) )
5010 vsa
.eMatchMode
= exactorG
;
5011 vsa
.eSearchMode
= searchbdesc
;
5014 vsa
.eMatchMode
= exactorNA
;
5015 vsa
.eSearchMode
= searchfwd
;
5019 vsa
.eMatchMode
= exactorS
;
5020 vsa
.eSearchMode
= searchbasc
;
5023 PushIllegalParameter();
5027 // get vector to be searched
5028 switch (GetStackType())
5031 PopSingleRef( vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
);
5032 vsa
.nCol2
= vsa
.nCol1
;
5033 vsa
.nRow2
= vsa
.nRow1
;
5034 vsa
.pMatSrc
= nullptr;
5038 vsa
.pMatSrc
= nullptr;
5040 PopDoubleRef(vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
, vsa
.nCol2
, vsa
.nRow2
, nTab2
);
5041 if (vsa
.nTab1
!= nTab2
|| (vsa
.nCol1
!= vsa
.nCol2
&& vsa
.nRow1
!= vsa
.nRow2
))
5043 PushIllegalParameter();
5049 case svExternalDoubleRef
:
5051 if (GetStackType() == svMatrix
)
5052 vsa
.pMatSrc
= PopMatrix();
5054 PopExternalDoubleRef(vsa
.pMatSrc
);
5058 PushIllegalParameter();
5064 PushIllegalParameter();
5069 if (nGlobalError
== FormulaError::NONE
)
5071 switch ( GetStackType() )
5075 vsa
.isStringSearch
= false;
5076 vsa
.fSearchVal
= GetDouble();
5081 vsa
.isStringSearch
= true;
5082 vsa
.sSearchStr
= GetString();
5089 if ( !PopDoubleRefOrSingleRef( aAdr
) )
5094 ScRefCellValue
aCell(mrDoc
, aAdr
);
5095 if (aCell
.hasNumeric())
5097 vsa
.isStringSearch
= false;
5098 vsa
.fSearchVal
= GetCellValue(aAdr
, aCell
);
5102 vsa
.isStringSearch
= true;
5103 GetCellString(vsa
.sSearchStr
, aCell
);
5107 case svExternalSingleRef
:
5109 ScExternalRefCache::TokenRef pToken
;
5110 PopExternalSingleRef(pToken
);
5111 if (nGlobalError
!= FormulaError::NONE
)
5113 PushError( nGlobalError
);
5116 if (pToken
->GetType() == svDouble
)
5118 vsa
.isStringSearch
= false;
5119 vsa
.fSearchVal
= pToken
->GetDouble();
5123 vsa
.isStringSearch
= true;
5124 vsa
.sSearchStr
= pToken
->GetString();
5128 case svExternalDoubleRef
:
5131 ScMatValType nType
= GetDoubleOrStringFromMatrix(
5132 vsa
.fSearchVal
, vsa
.sSearchStr
);
5133 vsa
.isStringSearch
= ScMatrix::IsNonValueType(nType
);
5138 PushIllegalParameter();
5144 if ( SearchVectorForValue( vsa
) )
5145 PushDouble( vsa
.nIndex
);
5148 if ( vsa
.isResultNA
)
5151 return; // error occurred and has already been pushed
5155 PushIllegalParameter();
5158 void ScInterpreter::ScXMatch()
5160 sal_uInt8 nParamCount
= GetByte();
5161 if (!MustHaveParamCount(nParamCount
, 2, 4))
5164 VectorSearchArguments vsa
;
5165 vsa
.nSearchOpCode
= SC_OPCODE_X_MATCH
;
5168 if (nParamCount
== 4)
5170 sal_Int16 k
= GetInt16();
5171 if (k
>= -2 && k
<= 2 && k
!= 0)
5172 vsa
.eSearchMode
= static_cast<SearchMode
>(k
);
5175 PushIllegalParameter();
5180 vsa
.eSearchMode
= searchfwd
;
5183 if (nParamCount
>= 3)
5185 sal_Int16 k
= GetInt16();
5186 if (k
>= -1 && k
<= 3)
5187 vsa
.eMatchMode
= static_cast<MatchMode
>(k
);
5190 PushIllegalParameter();
5195 vsa
.eMatchMode
= exactorNA
;
5197 // get vector to be searched
5198 switch (GetStackType())
5202 PopSingleRef(vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
);
5203 vsa
.nCol2
= vsa
.nCol1
;
5204 vsa
.nRow2
= vsa
.nRow1
;
5205 vsa
.pMatSrc
= nullptr;
5210 vsa
.pMatSrc
= nullptr;
5212 PopDoubleRef(vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
, vsa
.nCol2
, vsa
.nRow2
, nTab2
);
5213 if (vsa
.nTab1
!= nTab2
|| (vsa
.nCol1
!= vsa
.nCol2
&& vsa
.nRow1
!= vsa
.nRow2
))
5215 PushIllegalParameter();
5221 case svExternalDoubleRef
:
5223 if (GetStackType() == svMatrix
)
5224 vsa
.pMatSrc
= PopMatrix();
5226 PopExternalDoubleRef(vsa
.pMatSrc
);
5230 PushIllegalParameter();
5236 PushIllegalParameter();
5241 if (nGlobalError
== FormulaError::NONE
)
5243 switch (GetRawStackType())
5248 vsa
.isEmptySearch
= true;
5249 vsa
.isStringSearch
= false;
5250 vsa
.sSearchStr
= GetString();
5255 vsa
.isStringSearch
= false;
5256 vsa
.fSearchVal
= GetDouble();
5261 vsa
.isStringSearch
= true;
5262 vsa
.sSearchStr
= GetString();
5269 if (!PopDoubleRefOrSingleRef(aAdr
))
5274 ScRefCellValue
aCell(mrDoc
, aAdr
);
5275 if (aCell
.hasNumeric())
5277 vsa
.isStringSearch
= false;
5278 vsa
.fSearchVal
= GetCellValue(aAdr
, aCell
);
5282 vsa
.isStringSearch
= true;
5283 GetCellString(vsa
.sSearchStr
, aCell
);
5287 case svExternalSingleRef
:
5289 ScExternalRefCache::TokenRef pToken
;
5290 PopExternalSingleRef(pToken
);
5291 if (nGlobalError
!= FormulaError::NONE
)
5293 PushError(nGlobalError
);
5296 if (pToken
->GetType() == svDouble
)
5298 vsa
.isStringSearch
= false;
5299 vsa
.fSearchVal
= pToken
->GetDouble();
5303 vsa
.isStringSearch
= true;
5304 vsa
.sSearchStr
= pToken
->GetString();
5308 case svExternalDoubleRef
:
5311 ScMatValType nType
= GetDoubleOrStringFromMatrix(
5312 vsa
.fSearchVal
, vsa
.sSearchStr
);
5313 vsa
.isStringSearch
= ScMatrix::IsNonValueType(nType
);
5318 PushIllegalParameter();
5324 if (SearchVectorForValue(vsa
))
5325 PushDouble(vsa
.nIndex
);
5331 return; // error occurred and has already been pushed
5335 PushIllegalParameter();
5340 bool isCellContentEmpty( const ScRefCellValue
& rCell
)
5342 switch (rCell
.getType())
5344 case CELLTYPE_VALUE
:
5345 case CELLTYPE_STRING
:
5348 case CELLTYPE_FORMULA
:
5350 // NOTE: Excel treats ="" in a referenced cell as blank in
5351 // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
5352 // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
5353 // the cell content.
5354 // ODFF allows both for COUNTBLANK().
5355 // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
5356 // COUNTBLANK(), we now do for Excel interoperability.
5357 /* TODO: introduce yet another compatibility option? */
5358 sc::FormulaResultValue aRes
= rCell
.getFormula()->GetResult();
5359 if (aRes
.meType
!= sc::FormulaResultValue::String
)
5361 if (!aRes
.maString
.isEmpty())
5374 void ScInterpreter::ScCountEmptyCells()
5376 if ( !MustHaveParamCount( GetByte(), 1 ) )
5379 const SCSIZE nMatRows
= GetRefListArrayMaxSize(1);
5380 // There's either one RefList and nothing else, or none.
5381 ScMatrixRef xResMat
= (nMatRows
? GetNewMat( 1, nMatRows
, /*bEmpty*/true ) : nullptr);
5382 sal_uLong nMaxCount
= 0, nCount
= 0;
5383 switch (GetStackType())
5389 PopSingleRef( aAdr
);
5390 ScRefCellValue
aCell(mrDoc
, aAdr
);
5391 if (!isCellContentEmpty(aCell
))
5400 SCSIZE nRefListArrayPos
= 0;
5401 size_t nRefInList
= 0;
5402 while (nParam
-- > 0)
5404 nRefListArrayPos
= nRefInList
;
5405 PopDoubleRef( aRange
, nParam
, nRefInList
);
5407 static_cast<sal_uLong
>(aRange
.aEnd
.Row() - aRange
.aStart
.Row() + 1) *
5408 static_cast<sal_uLong
>(aRange
.aEnd
.Col() - aRange
.aStart
.Col() + 1) *
5409 static_cast<sal_uLong
>(aRange
.aEnd
.Tab() - aRange
.aStart
.Tab() + 1);
5411 ScCellIterator
aIter( mrDoc
, aRange
, mnSubTotalFlags
);
5412 for (bool bHas
= aIter
.first(); bHas
; bHas
= aIter
.next())
5414 const ScRefCellValue
& rCell
= aIter
.getRefCellValue();
5415 if (!isCellContentEmpty(rCell
))
5420 xResMat
->PutDouble( nMaxCount
- nCount
, 0, nRefListArrayPos
);
5421 nMaxCount
= nCount
= 0;
5427 case svExternalSingleRef
:
5428 case svExternalDoubleRef
:
5430 ScMatrixRef xMat
= GetMatrix();
5432 SetError( FormulaError::IllegalParameter
);
5436 xMat
->GetDimensions( nC
, nR
);
5437 nMaxCount
= nC
* nR
;
5438 // Numbers (implicit), strings and error values, ignore empty
5439 // strings as those if not entered in an inline array are the
5440 // result of a formula, to be par with a reference to formula
5441 // cell as *visual* blank, see isCellContentEmpty() above.
5442 nCount
= xMat
->Count( true, true, true);
5446 default : SetError(FormulaError::IllegalParameter
); break;
5449 PushMatrix( xResMat
);
5451 PushDouble(nMaxCount
- nCount
);
5454 void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc
)
5456 sal_uInt8 nParamCount
= GetByte();
5457 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
5464 ScMatrixRef pSumExtraMatrix
;
5465 bool bSumExtraRange
= (nParamCount
== 3);
5468 // Save only the upperleft cell in case of cell range. The geometry
5469 // of the 3rd parameter is taken from the 1st parameter.
5471 switch ( GetStackType() )
5478 PopDoubleRef( nCol3
, nRow3
, nTab3
, nColJunk
, nRowJunk
, nTabJunk
);
5479 if ( nTabJunk
!= nTab3
)
5481 PushError( FormulaError::IllegalParameter
);
5487 PopSingleRef( nCol3
, nRow3
, nTab3
);
5490 pSumExtraMatrix
= PopMatrix();
5491 // nCol3, nRow3, nTab3 remain 0
5493 case svExternalSingleRef
:
5495 pSumExtraMatrix
= GetNewMat(1,1);
5496 ScExternalRefCache::TokenRef pToken
;
5497 PopExternalSingleRef(pToken
);
5498 if (nGlobalError
!= FormulaError::NONE
)
5500 PushError( nGlobalError
);
5504 if (pToken
->GetType() == svDouble
)
5505 pSumExtraMatrix
->PutDouble(pToken
->GetDouble(), 0, 0);
5507 pSumExtraMatrix
->PutString(pToken
->GetString(), 0, 0);
5510 case svExternalDoubleRef
:
5511 PopExternalDoubleRef(pSumExtraMatrix
);
5514 PushError( FormulaError::IllegalParameter
);
5519 svl::SharedString aString
;
5521 bool bIsString
= true;
5522 switch ( GetStackType() )
5528 if ( !PopDoubleRefOrSingleRef( aAdr
) )
5530 PushError( nGlobalError
);
5534 ScRefCellValue
aCell(mrDoc
, aAdr
);
5535 switch (aCell
.getType())
5537 case CELLTYPE_VALUE
:
5538 fVal
= GetCellValue(aAdr
, aCell
);
5541 case CELLTYPE_FORMULA
:
5542 if (aCell
.getFormula()->IsValue())
5544 fVal
= GetCellValue(aAdr
, aCell
);
5548 GetCellString(aString
, aCell
);
5550 case CELLTYPE_STRING
:
5551 case CELLTYPE_EDIT
:
5552 GetCellString(aString
, aCell
);
5561 aString
= GetString();
5564 case svExternalDoubleRef
:
5566 ScMatValType nType
= GetDoubleOrStringFromMatrix( fVal
, aString
);
5567 bIsString
= ScMatrix::IsRealStringType( nType
);
5570 case svExternalSingleRef
:
5572 ScExternalRefCache::TokenRef pToken
;
5573 PopExternalSingleRef(pToken
);
5574 if (nGlobalError
== FormulaError::NONE
)
5576 if (pToken
->GetType() == svDouble
)
5578 fVal
= pToken
->GetDouble();
5582 aString
= pToken
->GetString();
5593 KahanSum fSum
= 0.0;
5595 double fCount
= 0.0;
5597 const SCSIZE nMatRows
= GetRefListArrayMaxSize( nParam
);
5598 // There's either one RefList and nothing else, or none.
5599 ScMatrixRef xResMat
= (nMatRows
? GetNewMat( 1, nMatRows
, /*bEmpty*/true ) : nullptr);
5600 SCSIZE nRefListArrayPos
= 0;
5601 size_t nRefInList
= 0;
5602 while (nParam
-- > 0)
5610 ScMatrixRef pQueryMatrix
;
5611 switch ( GetStackType() )
5616 /* TODO: this could resolve if all refs are of the same size */
5617 SetError( FormulaError::IllegalParameter
);
5621 nRefListArrayPos
= nRefInList
;
5623 PopDoubleRef( aRange
, nParam
, nRefInList
);
5624 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
5628 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
5631 PopSingleRef( nCol1
, nRow1
, nTab1
);
5637 case svExternalSingleRef
:
5638 case svExternalDoubleRef
:
5640 pQueryMatrix
= GetMatrix();
5643 PushError( FormulaError::IllegalParameter
);
5650 pQueryMatrix
->GetDimensions( nC
, nR
);
5651 nCol2
= static_cast<SCCOL
>(nC
- 1);
5652 nRow2
= static_cast<SCROW
>(nR
- 1);
5657 SetError( FormulaError::IllegalParameter
);
5659 if ( nTab1
!= nTab2
)
5661 SetError( FormulaError::IllegalParameter
);
5666 // Take the range geometry of the 1st parameter and apply it to
5667 // the 3rd. If parts of the resulting range would point outside
5668 // the sheet, don't complain but silently ignore and simply cut
5669 // them away, this is what Xcl does :-/
5671 // For the cut-away part we also don't need to determine the
5672 // criteria match, so shrink the source range accordingly,
5673 // instead of the result range.
5674 SCCOL nColDelta
= nCol2
- nCol1
;
5675 SCROW nRowDelta
= nRow2
- nRow1
;
5678 if (pSumExtraMatrix
)
5681 pSumExtraMatrix
->GetDimensions( nC
, nR
);
5682 nMaxCol
= static_cast<SCCOL
>(nC
- 1);
5683 nMaxRow
= static_cast<SCROW
>(nR
- 1);
5687 nMaxCol
= mrDoc
.MaxCol();
5688 nMaxRow
= mrDoc
.MaxRow();
5690 if (nCol3
+ nColDelta
> nMaxCol
)
5692 SCCOL nNewDelta
= nMaxCol
- nCol3
;
5693 nCol2
= nCol1
+ nNewDelta
;
5696 if (nRow3
+ nRowDelta
> nMaxRow
)
5698 SCROW nNewDelta
= nMaxRow
- nRow3
;
5699 nRow2
= nRow1
+ nNewDelta
;
5709 if (nGlobalError
== FormulaError::NONE
)
5711 ScQueryParam rParam
;
5712 rParam
.nRow1
= nRow1
;
5713 rParam
.nRow2
= nRow2
;
5715 ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
5716 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
5717 rEntry
.bDoQuery
= true;
5720 rItem
.meType
= ScQueryEntry::ByValue
;
5722 rEntry
.eOp
= SC_EQUAL
;
5726 rParam
.FillInExcelSyntax(mrDoc
.GetSharedStringPool(), aString
.getString(), 0, &mrContext
);
5727 if (rItem
.meType
== ScQueryEntry::ByString
)
5728 rParam
.eSearchType
= DetectSearchType(rItem
.maString
.getString(), mrDoc
);
5731 aAdr
.SetTab( nTab3
);
5732 rParam
.nCol1
= nCol1
;
5733 rParam
.nCol2
= nCol2
;
5734 rEntry
.nField
= nCol1
;
5735 SCCOL nColDiff
= nCol3
- nCol1
;
5736 SCROW nRowDiff
= nRow3
- nRow1
;
5739 // Never case-sensitive.
5740 sc::CompareOptions
aOptions( mrDoc
, rEntry
, rParam
.eSearchType
);
5741 ScMatrixRef pResultMatrix
= QueryMat( pQueryMatrix
, aOptions
);
5742 if (nGlobalError
!= FormulaError::NONE
|| !pResultMatrix
)
5744 PushIllegalParameter();
5748 if (pSumExtraMatrix
)
5750 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
5752 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
5754 if (pResultMatrix
->IsValue( nCol
, nRow
) &&
5755 pResultMatrix
->GetDouble( nCol
, nRow
))
5757 SCSIZE nC
= nCol
+ nColDiff
;
5758 SCSIZE nR
= nRow
+ nRowDiff
;
5759 if (pSumExtraMatrix
->IsValue( nC
, nR
))
5761 fVal
= pSumExtraMatrix
->GetDouble( nC
, nR
);
5769 else if (!bSumExtraRange
)
5771 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
5773 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
5775 if (pResultMatrix
->IsValue( nCol
, nRow
) &&
5776 pResultMatrix
->GetDouble( nCol
, nRow
))
5778 if (pQueryMatrix
->IsValue( nCol
, nRow
))
5780 fVal
= pQueryMatrix
->GetDouble( nCol
, nRow
);
5790 for (SCCOL nCol
= nCol1
; nCol
<= nCol2
; ++nCol
)
5792 for (SCROW nRow
= nRow1
; nRow
<= nRow2
; ++nRow
)
5794 if (pResultMatrix
->GetDouble( nCol
, nRow
))
5796 aAdr
.SetCol( nCol
+ nColDiff
);
5797 aAdr
.SetRow( nRow
+ nRowDiff
);
5798 ScRefCellValue
aCell(mrDoc
, aAdr
);
5799 if (aCell
.hasNumeric())
5801 fVal
= GetCellValue(aAdr
, aCell
);
5812 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab1
, rParam
, false, false);
5813 // Increment Entry.nField in iterator when switching to next column.
5814 aCellIter
.SetAdvanceQueryParamEntryField( true );
5815 if ( aCellIter
.GetFirst() )
5817 if (pSumExtraMatrix
)
5821 SCSIZE nC
= aCellIter
.GetCol() + nColDiff
;
5822 SCSIZE nR
= aCellIter
.GetRow() + nRowDiff
;
5823 if (pSumExtraMatrix
->IsValue( nC
, nR
))
5825 fVal
= pSumExtraMatrix
->GetDouble( nC
, nR
);
5829 } while ( aCellIter
.GetNext() );
5835 aAdr
.SetCol( aCellIter
.GetCol() + nColDiff
);
5836 aAdr
.SetRow( aCellIter
.GetRow() + nRowDiff
);
5837 ScRefCellValue
aCell(mrDoc
, aAdr
);
5838 if (aCell
.hasNumeric())
5840 fVal
= GetCellValue(aAdr
, aCell
);
5844 } while ( aCellIter
.GetNext() );
5851 PushError( FormulaError::IllegalParameter
);
5857 case ifSUMIF
: fRes
= fSum
.get(); break;
5858 case ifAVERAGEIF
: fRes
= div( fSum
.get(), fCount
); break;
5862 if (nGlobalError
== FormulaError::NONE
)
5863 xResMat
->PutDouble( fRes
, 0, nRefListArrayPos
);
5866 xResMat
->PutError( nGlobalError
, 0, nRefListArrayPos
);
5867 nGlobalError
= FormulaError::NONE
;
5869 fRes
= fCount
= 0.0;
5874 PushMatrix( xResMat
);
5879 void ScInterpreter::ScSumIf()
5881 IterateParametersIf( ifSUMIF
);
5884 void ScInterpreter::ScAverageIf()
5886 IterateParametersIf( ifAVERAGEIF
);
5889 void ScInterpreter::ScCountIf()
5891 if ( !MustHaveParamCount( GetByte(), 2 ) )
5894 svl::SharedString aString
;
5896 bool bIsString
= true;
5897 switch ( GetStackType() )
5903 if ( !PopDoubleRefOrSingleRef( aAdr
) )
5908 ScRefCellValue
aCell(mrDoc
, aAdr
);
5909 switch (aCell
.getType())
5911 case CELLTYPE_VALUE
:
5912 fVal
= GetCellValue(aAdr
, aCell
);
5915 case CELLTYPE_FORMULA
:
5916 if (aCell
.getFormula()->IsValue())
5918 fVal
= GetCellValue(aAdr
, aCell
);
5922 GetCellString(aString
, aCell
);
5924 case CELLTYPE_STRING
:
5925 case CELLTYPE_EDIT
:
5926 GetCellString(aString
, aCell
);
5935 case svExternalSingleRef
:
5936 case svExternalDoubleRef
:
5938 ScMatValType nType
= GetDoubleOrStringFromMatrix(fVal
, aString
);
5939 bIsString
= ScMatrix::IsRealStringType( nType
);
5943 aString
= GetString();
5951 double fCount
= 0.0;
5953 const SCSIZE nMatRows
= GetRefListArrayMaxSize( nParam
);
5954 // There's either one RefList and nothing else, or none.
5955 ScMatrixRef xResMat
= (nMatRows
? GetNewMat( 1, nMatRows
, /*bEmpty*/true ) : nullptr);
5956 SCSIZE nRefListArrayPos
= 0;
5957 size_t nRefInList
= 0;
5958 while (nParam
-- > 0)
5966 ScMatrixRef pQueryMatrix
;
5967 const ScComplexRefData
* refData
= nullptr;
5968 switch ( GetStackType() )
5971 nRefListArrayPos
= nRefInList
;
5975 refData
= GetStackDoubleRef(nRefInList
);
5977 PopDoubleRef( aRange
, nParam
, nRefInList
);
5978 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
5982 PopSingleRef( nCol1
, nRow1
, nTab1
);
5988 case svExternalSingleRef
:
5989 case svExternalDoubleRef
:
5991 pQueryMatrix
= GetMatrix();
5994 PushIllegalParameter();
6001 pQueryMatrix
->GetDimensions( nC
, nR
);
6002 nCol2
= static_cast<SCCOL
>(nC
- 1);
6003 nRow2
= static_cast<SCROW
>(nR
- 1);
6008 PopError(); // Propagate it further
6009 PushIllegalParameter();
6012 if ( nTab1
!= nTab2
)
6014 PushIllegalParameter();
6019 PushIllegalParameter();
6022 if (nGlobalError
== FormulaError::NONE
)
6024 ScQueryParam rParam
;
6025 rParam
.nRow1
= nRow1
;
6026 rParam
.nRow2
= nRow2
;
6027 rParam
.nTab
= nTab1
;
6029 ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
6030 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
6031 rEntry
.bDoQuery
= true;
6034 rItem
.meType
= ScQueryEntry::ByValue
;
6036 rEntry
.eOp
= SC_EQUAL
;
6040 rParam
.FillInExcelSyntax(mrDoc
.GetSharedStringPool(), aString
.getString(), 0, &mrContext
);
6041 if (rItem
.meType
== ScQueryEntry::ByString
)
6042 rParam
.eSearchType
= DetectSearchType(rItem
.maString
.getString(), mrDoc
);
6044 rParam
.nCol1
= nCol1
;
6045 rParam
.nCol2
= nCol2
;
6046 rEntry
.nField
= nCol1
;
6049 // Never case-sensitive.
6050 sc::CompareOptions
aOptions( mrDoc
, rEntry
, rParam
.eSearchType
);
6051 ScMatrixRef pResultMatrix
= QueryMat( pQueryMatrix
, aOptions
);
6052 if (nGlobalError
!= FormulaError::NONE
|| !pResultMatrix
)
6054 PushIllegalParameter();
6058 SCSIZE nSize
= pResultMatrix
->GetElementCount();
6059 for (SCSIZE nIndex
= 0; nIndex
< nSize
; ++nIndex
)
6061 if (pResultMatrix
->IsValue( nIndex
) &&
6062 pResultMatrix
->GetDouble( nIndex
))
6068 if(ScCountIfCellIteratorSortedCache::CanBeUsed(mrDoc
, rParam
, nTab1
, pMyFormulaCell
,
6069 refData
, mrContext
))
6071 ScCountIfCellIteratorSortedCache
aCellIter(mrDoc
, mrContext
, nTab1
, rParam
, false, false);
6072 fCount
+= aCellIter
.GetCount();
6076 ScCountIfCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab1
, rParam
, false, false);
6077 fCount
+= aCellIter
.GetCount();
6083 PushIllegalParameter();
6088 xResMat
->PutDouble( fCount
, 0, nRefListArrayPos
);
6093 PushMatrix( xResMat
);
6098 void ScInterpreter::IterateParametersIfs( double(*ResultFunc
)( const sc::ParamIfsResult
& rRes
) )
6100 sal_uInt8 nParamCount
= GetByte();
6101 sal_uInt8 nQueryCount
= nParamCount
/ 2;
6103 std::vector
<sal_uInt8
>& vConditions
= mrContext
.maConditions
;
6104 // vConditions is cached, although it is clear'ed after every cell is interpreted,
6105 // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because
6106 // with a single InterpretTail() call it results in evaluation of all the cells in the
6108 vConditions
.clear();
6110 // Range-reduce optimization
6111 SCCOL nStartColDiff
= 0;
6112 SCCOL nEndColDiff
= 0;
6113 SCROW nStartRowDiff
= 0;
6114 SCROW nEndRowDiff
= 0;
6115 bool bRangeReduce
= false;
6118 bool bHasDoubleRefCriteriaRanges
= true;
6119 // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs.
6120 // For COUNTIFS queries it's possible to range-reduce too, if the query is not supposed
6121 // to match empty cells (will be checked and undone later if needed), so simply treat
6122 // the first criteria range as the main range for purposes of detecting if this can be done.
6123 for (sal_uInt16 nParamIdx
= 2; nParamIdx
< nParamCount
; nParamIdx
+= 2 )
6125 const formula::FormulaToken
* pCriteriaRangeToken
= pStack
[ sp
-nParamIdx
];
6126 if (pCriteriaRangeToken
->GetType() != svDoubleRef
)
6128 bHasDoubleRefCriteriaRanges
= false;
6133 // Probe the main range token, and try if we can shrink the range without altering results.
6134 const formula::FormulaToken
* pMainRangeToken
= pStack
[ sp
-nParamCount
];
6135 if (pMainRangeToken
->GetType() == svDoubleRef
&& bHasDoubleRefCriteriaRanges
)
6137 const ScComplexRefData
* pRefData
= pMainRangeToken
->GetDoubleRef();
6138 if (!pRefData
->IsDeleted())
6140 DoubleRefToRange( *pRefData
, aMainRange
);
6141 if (aMainRange
.aStart
.Tab() == aMainRange
.aEnd
.Tab())
6143 // Shrink the range to actual data content.
6144 ScRange aSubRange
= aMainRange
;
6145 mrDoc
.GetDataAreaSubrange(aSubRange
);
6146 nStartColDiff
= aSubRange
.aStart
.Col() - aMainRange
.aStart
.Col();
6147 nStartRowDiff
= aSubRange
.aStart
.Row() - aMainRange
.aStart
.Row();
6148 nEndColDiff
= aSubRange
.aEnd
.Col() - aMainRange
.aEnd
.Col();
6149 nEndRowDiff
= aSubRange
.aEnd
.Row() - aMainRange
.aEnd
.Row();
6150 bRangeReduce
= nStartColDiff
|| nStartRowDiff
|| nEndColDiff
|| nEndRowDiff
;
6156 SCCOL nDimensionCols
= 0;
6157 SCROW nDimensionRows
= 0;
6158 const SCSIZE nRefArrayRows
= GetRefListArrayMaxSize( nParamCount
);
6159 std::vector
<std::vector
<sal_uInt8
>> vRefArrayConditions
;
6161 while (nParamCount
> 1 && nGlobalError
== FormulaError::NONE
)
6164 svl::SharedString aString
;
6166 bool bIsString
= true;
6167 switch ( GetStackType() )
6173 if ( !PopDoubleRefOrSingleRef( aAdr
) )
6175 PushError( nGlobalError
);
6179 ScRefCellValue
aCell(mrDoc
, aAdr
);
6180 switch (aCell
.getType())
6182 case CELLTYPE_VALUE
:
6183 fVal
= GetCellValue(aAdr
, aCell
);
6186 case CELLTYPE_FORMULA
:
6187 if (aCell
.getFormula()->IsValue())
6189 fVal
= GetCellValue(aAdr
, aCell
);
6193 GetCellString(aString
, aCell
);
6195 case CELLTYPE_STRING
:
6196 case CELLTYPE_EDIT
:
6197 GetCellString(aString
, aCell
);
6206 aString
= GetString();
6209 case svExternalDoubleRef
:
6211 ScMatValType nType
= GetDoubleOrStringFromMatrix( fVal
, aString
);
6212 bIsString
= ScMatrix::IsRealStringType( nType
);
6215 case svExternalSingleRef
:
6217 ScExternalRefCache::TokenRef pToken
;
6218 PopExternalSingleRef(pToken
);
6219 if (nGlobalError
== FormulaError::NONE
)
6221 if (pToken
->GetType() == svDouble
)
6223 fVal
= pToken
->GetDouble();
6227 aString
= pToken
->GetString();
6238 if (nGlobalError
!= FormulaError::NONE
)
6240 PushError( nGlobalError
);
6241 return; // and bail out, no need to evaluate other arguments
6245 short nParam
= nParamCount
;
6246 size_t nRefInList
= 0;
6247 size_t nRefArrayPos
= std::numeric_limits
<size_t>::max();
6254 ScMatrixRef pQueryMatrix
;
6255 while (nParam
-- == nParamCount
)
6257 const ScComplexRefData
* refData
= nullptr;
6258 switch ( GetStackType() )
6262 const ScRefListToken
* p
= dynamic_cast<const ScRefListToken
*>(pStack
[sp
-1]);
6263 if (p
&& p
->IsArrayResult())
6265 if (nRefInList
== 0)
6267 if (vRefArrayConditions
.empty())
6268 vRefArrayConditions
.resize( nRefArrayRows
);
6269 if (!vConditions
.empty())
6271 // Similar to other reference list array
6272 // handling, add/op the current value to
6273 // all array positions.
6274 for (auto & rVec
: vRefArrayConditions
)
6280 assert(rVec
.size() == vConditions
.size()); // see dimensions below
6281 for (size_t i
=0, n
= rVec
.size(); i
< n
; ++i
)
6283 rVec
[i
] += vConditions
[i
];
6287 // Reset condition results.
6288 std::for_each( vConditions
.begin(), vConditions
.end(),
6289 [](sal_uInt8
& r
){ r
= 0.0; } );
6292 nRefArrayPos
= nRefInList
;
6294 refData
= GetStackDoubleRef(nRefInList
);
6296 PopDoubleRef( aRange
, nParam
, nRefInList
);
6297 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
6301 refData
= GetStackDoubleRef();
6302 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
6305 PopSingleRef( nCol1
, nRow1
, nTab1
);
6311 case svExternalSingleRef
:
6312 case svExternalDoubleRef
:
6314 pQueryMatrix
= GetMatrix();
6317 PushError( FormulaError::IllegalParameter
);
6324 pQueryMatrix
->GetDimensions( nC
, nR
);
6325 nCol2
= static_cast<SCCOL
>(nC
- 1);
6326 nRow2
= static_cast<SCROW
>(nR
- 1);
6331 PushError( FormulaError::IllegalParameter
);
6334 if ( nTab1
!= nTab2
)
6336 PushError( FormulaError::IllegalArgument
);
6340 ScQueryParam rParam
;
6341 ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
6342 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
6343 rEntry
.bDoQuery
= true;
6346 rItem
.meType
= ScQueryEntry::ByValue
;
6348 rEntry
.eOp
= SC_EQUAL
;
6352 rParam
.FillInExcelSyntax(mrDoc
.GetSharedStringPool(), aString
.getString(), 0, &mrContext
);
6353 if (rItem
.meType
== ScQueryEntry::ByString
)
6354 rParam
.eSearchType
= DetectSearchType(rItem
.maString
.getString(), mrDoc
);
6357 // Undo bRangeReduce if asked to match empty cells for COUNTIFS (which should be rare).
6358 assert(rEntry
.GetQueryItems().size() == 1);
6359 const bool isCountIfs
= (nParamCount
% 2) == 0;
6360 if(isCountIfs
&& (rEntry
.IsQueryByEmpty() || rItem
.mbMatchEmpty
) && bRangeReduce
)
6362 bRangeReduce
= false;
6363 // All criteria ranges are svDoubleRef's, so only vConditions needs adjusting.
6364 assert(vRefArrayConditions
.empty());
6365 if(!vConditions
.empty())
6367 std::vector
<sal_uInt8
> newConditions
;
6368 SCCOL newDimensionCols
= nCol2
- nCol1
+ 1;
6369 SCROW newDimensionRows
= nRow2
- nRow1
+ 1;
6370 newConditions
.reserve( newDimensionCols
* newDimensionRows
);
6372 for(; col
< nCol1
+ nStartColDiff
; ++col
)
6373 newConditions
.insert( newConditions
.end(), newDimensionRows
, 0 );
6374 for(; col
<= nCol2
- nStartColDiff
; ++col
)
6376 newConditions
.insert( newConditions
.end(), nStartRowDiff
, 0 );
6377 SCCOL oldCol
= col
- ( nCol1
+ nStartColDiff
);
6378 size_t nIndex
= oldCol
* nDimensionRows
;
6379 if (nIndex
< vConditions
.size())
6381 auto it
= vConditions
.begin() + nIndex
;
6382 newConditions
.insert( newConditions
.end(), it
, it
+ nDimensionRows
);
6385 newConditions
.insert( newConditions
.end(), nDimensionRows
, 0 );
6386 newConditions
.insert( newConditions
.end(), -nEndRowDiff
, 0 );
6388 for(; col
<= nCol2
; ++col
)
6389 newConditions
.insert( newConditions
.end(), newDimensionRows
, 0 );
6390 assert( newConditions
.size() == o3tl::make_unsigned( newDimensionCols
* newDimensionRows
));
6391 vConditions
= std::move( newConditions
);
6392 nDimensionCols
= newDimensionCols
;
6393 nDimensionRows
= newDimensionRows
;
6399 // All reference ranges must be of the same size as the main range.
6400 if( aMainRange
.aEnd
.Col() - aMainRange
.aStart
.Col() != nCol2
- nCol1
6401 || aMainRange
.aEnd
.Row() - aMainRange
.aStart
.Row() != nRow2
- nRow1
)
6403 PushError ( FormulaError::IllegalArgument
);
6406 nCol1
+= nStartColDiff
;
6407 nRow1
+= nStartRowDiff
;
6409 nCol2
+= nEndColDiff
;
6410 nRow2
+= nEndRowDiff
;
6413 // All reference ranges must be of same dimension and size.
6414 if (!nDimensionCols
)
6415 nDimensionCols
= nCol2
- nCol1
+ 1;
6416 if (!nDimensionRows
)
6417 nDimensionRows
= nRow2
- nRow1
+ 1;
6418 if ((nDimensionCols
!= (nCol2
- nCol1
+ 1)) || (nDimensionRows
!= (nRow2
- nRow1
+ 1)))
6420 PushError ( FormulaError::IllegalArgument
);
6424 // recalculate matrix values
6425 if (nGlobalError
!= FormulaError::NONE
)
6427 PushError( nGlobalError
);
6431 // initialize temporary result matrix
6432 if (vConditions
.empty())
6433 vConditions
.resize( nDimensionCols
* nDimensionRows
, 0);
6435 rParam
.nRow1
= nRow1
;
6436 rParam
.nRow2
= nRow2
;
6437 rParam
.nCol1
= nCol1
;
6438 rParam
.nCol2
= nCol2
;
6439 rEntry
.nField
= nCol1
;
6440 SCCOL nColDiff
= -nCol1
;
6441 SCROW nRowDiff
= -nRow1
;
6444 // Never case-sensitive.
6445 sc::CompareOptions
aOptions(mrDoc
, rEntry
, rParam
.eSearchType
);
6446 ScMatrixRef pResultMatrix
= QueryMat( pQueryMatrix
, aOptions
);
6447 if (nGlobalError
!= FormulaError::NONE
|| !pResultMatrix
)
6449 PushError( FormulaError::IllegalParameter
);
6453 // result matrix is filled with boolean values.
6454 std::vector
<double> aResValues
;
6455 pResultMatrix
->GetDoubleArray(aResValues
);
6456 if (vConditions
.size() != aResValues
.size())
6458 PushError( FormulaError::IllegalParameter
);
6462 std::vector
<double>::const_iterator itThisRes
= aResValues
.begin();
6463 for (auto& rCondition
: vConditions
)
6465 rCondition
+= *itThisRes
;
6471 if( ScQueryCellIteratorSortedCache::CanBeUsed( mrDoc
, rParam
, nTab1
, pMyFormulaCell
,
6472 refData
, mrContext
))
6474 ScQueryCellIteratorSortedCache
aCellIter(mrDoc
, mrContext
, nTab1
, rParam
, false, false);
6475 // Increment Entry.nField in iterator when switching to next column.
6476 aCellIter
.SetAdvanceQueryParamEntryField( true );
6477 if ( aCellIter
.GetFirst() )
6481 size_t nC
= aCellIter
.GetCol() + nColDiff
;
6482 size_t nR
= aCellIter
.GetRow() + nRowDiff
;
6483 ++vConditions
[nC
* nDimensionRows
+ nR
];
6484 } while ( aCellIter
.GetNext() );
6489 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab1
, rParam
, false, false);
6490 // Increment Entry.nField in iterator when switching to next column.
6491 aCellIter
.SetAdvanceQueryParamEntryField( true );
6492 if ( aCellIter
.GetFirst() )
6496 size_t nC
= aCellIter
.GetCol() + nColDiff
;
6497 size_t nR
= aCellIter
.GetRow() + nRowDiff
;
6498 ++vConditions
[nC
* nDimensionRows
+ nR
];
6499 } while ( aCellIter
.GetNext() );
6503 if (nRefArrayPos
!= std::numeric_limits
<size_t>::max())
6505 // Apply condition result to reference list array result position.
6506 std::vector
<sal_uInt8
>& rVec
= vRefArrayConditions
[nRefArrayPos
];
6511 assert(rVec
.size() == vConditions
.size()); // see dimensions above
6512 for (size_t i
=0, n
= rVec
.size(); i
< n
; ++i
)
6514 rVec
[i
] += vConditions
[i
];
6517 // Reset conditions vector.
6518 // When leaving an svRefList this has to be emptied not set to
6519 // 0.0 because it's checked when entering an svRefList.
6520 if (nRefInList
== 0)
6521 std::vector
<sal_uInt8
>().swap( vConditions
);
6523 std::for_each( vConditions
.begin(), vConditions
.end(), [](sal_uInt8
& r
){ r
= 0; } );
6529 if (!vRefArrayConditions
.empty() && !vConditions
.empty())
6531 // Add/op the last current value to all array positions.
6532 for (auto & rVec
: vRefArrayConditions
)
6538 assert(rVec
.size() == vConditions
.size()); // see dimensions above
6539 for (size_t i
=0, n
= rVec
.size(); i
< n
; ++i
)
6541 rVec
[i
] += vConditions
[i
];
6547 if (nGlobalError
!= FormulaError::NONE
)
6549 PushError( nGlobalError
);
6553 sc::ParamIfsResult aRes
;
6554 ScMatrixRef xResMat
;
6556 // main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS
6557 if (nParamCount
== 1)
6559 short nParam
= nParamCount
;
6560 size_t nRefInList
= 0;
6561 size_t nRefArrayPos
= std::numeric_limits
<size_t>::max();
6562 bool bRefArrayMain
= false;
6563 while (nParam
-- == nParamCount
)
6565 SCCOL nMainCol1
= 0;
6566 SCROW nMainRow1
= 0;
6567 SCTAB nMainTab1
= 0;
6568 SCCOL nMainCol2
= 0;
6569 SCROW nMainRow2
= 0;
6570 SCTAB nMainTab2
= 0;
6571 ScMatrixRef pMainMatrix
;
6572 switch ( GetStackType() )
6576 const ScRefListToken
* p
= dynamic_cast<const ScRefListToken
*>(pStack
[sp
-1]);
6577 if (p
&& p
->IsArrayResult())
6579 if (vRefArrayConditions
.empty())
6581 // Replicate conditions if there wasn't a
6582 // reference list array for criteria
6584 vRefArrayConditions
.resize( nRefArrayRows
);
6585 for (auto & rVec
: vRefArrayConditions
)
6591 bRefArrayMain
= true;
6592 nRefArrayPos
= nRefInList
;
6595 PopDoubleRef( aRange
, nParam
, nRefInList
);
6596 aRange
.GetVars( nMainCol1
, nMainRow1
, nMainTab1
, nMainCol2
, nMainRow2
, nMainTab2
);
6600 PopDoubleRef( nMainCol1
, nMainRow1
, nMainTab1
, nMainCol2
, nMainRow2
, nMainTab2
);
6603 PopSingleRef( nMainCol1
, nMainRow1
, nMainTab1
);
6604 nMainCol2
= nMainCol1
;
6605 nMainRow2
= nMainRow1
;
6606 nMainTab2
= nMainTab1
;
6609 case svExternalSingleRef
:
6610 case svExternalDoubleRef
:
6612 pMainMatrix
= GetMatrix();
6615 PushError( FormulaError::IllegalParameter
);
6622 pMainMatrix
->GetDimensions( nC
, nR
);
6623 nMainCol2
= static_cast<SCCOL
>(nC
- 1);
6624 nMainRow2
= static_cast<SCROW
>(nR
- 1);
6628 // Treat a scalar value as 1x1 matrix.
6630 pMainMatrix
= GetNewMat(1,1);
6631 nMainCol1
= nMainCol2
= 0;
6632 nMainRow1
= nMainRow2
= 0;
6633 nMainTab1
= nMainTab2
= 0;
6634 pMainMatrix
->PutDouble( GetDouble(), 0, 0);
6637 pMainMatrix
= GetNewMat(1,1);
6638 nMainCol1
= nMainCol2
= 0;
6639 nMainRow1
= nMainRow2
= 0;
6640 nMainTab1
= nMainTab2
= 0;
6641 pMainMatrix
->PutString( GetString(), 0, 0);
6645 PushError( FormulaError::IllegalParameter
);
6648 if ( nMainTab1
!= nMainTab2
)
6650 PushError( FormulaError::IllegalArgument
);
6656 nMainCol1
+= nStartColDiff
;
6657 nMainRow1
+= nStartRowDiff
;
6659 nMainCol2
+= nEndColDiff
;
6660 nMainRow2
+= nEndRowDiff
;
6663 // All reference ranges must be of same dimension and size.
6664 if ((nDimensionCols
!= (nMainCol2
- nMainCol1
+ 1)) || (nDimensionRows
!= (nMainRow2
- nMainRow1
+ 1)))
6666 PushError ( FormulaError::IllegalArgument
);
6670 if (nGlobalError
!= FormulaError::NONE
)
6672 PushError( nGlobalError
);
6676 // end-result calculation
6678 // This gets weird... if conditions were calculated using a
6679 // reference list array but the main calculation range is not a
6680 // reference list array, then the conditions of the array are
6681 // applied to the main range each in turn to form the array result.
6683 size_t nRefArrayMainPos
= (bRefArrayMain
? nRefArrayPos
:
6684 (vRefArrayConditions
.empty() ? std::numeric_limits
<size_t>::max() : 0));
6685 const bool bAppliedArray
= (!bRefArrayMain
&& nRefArrayMainPos
== 0);
6687 if (nRefArrayMainPos
== 0)
6688 xResMat
= GetNewMat( 1, nRefArrayRows
, /*bEmpty*/true );
6692 std::vector
<double> aMainValues
;
6693 pMainMatrix
->GetDoubleArray(aMainValues
, false); // Map empty values to NaN's.
6697 if (nRefArrayMainPos
< vRefArrayConditions
.size())
6698 vConditions
= vRefArrayConditions
[nRefArrayMainPos
];
6700 if (vConditions
.size() != aMainValues
.size())
6702 PushError( FormulaError::IllegalArgument
);
6706 std::vector
<sal_uInt8
>::const_iterator itRes
= vConditions
.begin(), itResEnd
= vConditions
.end();
6707 std::vector
<double>::const_iterator itMain
= aMainValues
.begin();
6708 for (; itRes
!= itResEnd
; ++itRes
, ++itMain
)
6710 if (*itRes
!= nQueryCount
)
6714 if (GetDoubleErrorValue(fVal
) == FormulaError::ElementNaN
)
6719 if ( aRes
.mfMin
> fVal
)
6721 if ( aRes
.mfMax
< fVal
)
6724 if (nRefArrayMainPos
!= std::numeric_limits
<size_t>::max())
6726 xResMat
->PutDouble( ResultFunc( aRes
), 0, nRefArrayMainPos
);
6727 aRes
= sc::ParamIfsResult();
6730 while (bAppliedArray
&& ++nRefArrayMainPos
< nRefArrayRows
);
6735 aAdr
.SetTab( nMainTab1
);
6738 if (nRefArrayMainPos
< vRefArrayConditions
.size())
6739 vConditions
= vRefArrayConditions
[nRefArrayMainPos
];
6741 SAL_WARN_IF(nDimensionCols
&& nDimensionRows
&& vConditions
.empty(), "sc", "ScInterpreter::IterateParametersIfs vConditions is empty");
6742 if (!vConditions
.empty())
6744 std::vector
<sal_uInt8
>::const_iterator itRes
= vConditions
.begin();
6745 for (SCCOL nCol
= 0; nCol
< nDimensionCols
; ++nCol
)
6747 for (SCROW nRow
= 0; nRow
< nDimensionRows
; ++nRow
, ++itRes
)
6749 if (*itRes
== nQueryCount
)
6751 aAdr
.SetCol( nCol
+ nMainCol1
);
6752 aAdr
.SetRow( nRow
+ nMainRow1
);
6753 ScRefCellValue
aCell(mrDoc
, aAdr
);
6754 if (aCell
.hasNumeric())
6756 fVal
= GetCellValue(aAdr
, aCell
);
6759 if ( aRes
.mfMin
> fVal
)
6761 if ( aRes
.mfMax
< fVal
)
6768 if (nRefArrayMainPos
!= std::numeric_limits
<size_t>::max())
6770 xResMat
->PutDouble( ResultFunc( aRes
), 0, nRefArrayMainPos
);
6771 aRes
= sc::ParamIfsResult();
6774 while (bAppliedArray
&& ++nRefArrayMainPos
< nRefArrayRows
);
6781 if (vRefArrayConditions
.empty())
6783 // The code below is this but optimized for most elements not matching.
6784 // for (auto const & rCond : vConditions)
6785 // if (rCond == nQueryCount)
6787 static_assert(sizeof(vConditions
[0]) == 1);
6788 const sal_uInt8
* pos
= vConditions
.data();
6789 const sal_uInt8
* end
= pos
+ vConditions
.size();
6792 pos
= static_cast< const sal_uInt8
* >( memchr( pos
, nQueryCount
, end
- pos
));
6793 if( pos
== nullptr )
6801 xResMat
= GetNewMat( 1, nRefArrayRows
, /*bEmpty*/true );
6802 for (size_t i
=0, n
= vRefArrayConditions
.size(); i
< n
; ++i
)
6804 double fCount
= 0.0;
6805 for (auto const & rCond
: vRefArrayConditions
[i
])
6807 if (rCond
== nQueryCount
)
6810 xResMat
->PutDouble( fCount
, 0, i
);
6816 PushMatrix( xResMat
);
6818 PushDouble( ResultFunc( aRes
));
6821 void ScInterpreter::ScSumIfs()
6823 // ScMutationGuard aShouldFail(pDok, ScMutationGuardFlags::CORE);
6824 sal_uInt8 nParamCount
= GetByte();
6826 if (nParamCount
< 3 || (nParamCount
% 2 != 1))
6828 PushError( FormulaError::ParameterExpected
);
6832 auto ResultFunc
= []( const sc::ParamIfsResult
& rRes
)
6834 return rRes
.mfSum
.get();
6836 IterateParametersIfs(ResultFunc
);
6839 void ScInterpreter::ScAverageIfs()
6841 sal_uInt8 nParamCount
= GetByte();
6843 if (nParamCount
< 3 || (nParamCount
% 2 != 1))
6845 PushError( FormulaError::ParameterExpected
);
6849 auto ResultFunc
= []( const sc::ParamIfsResult
& rRes
)
6851 return sc::div( rRes
.mfSum
.get(), rRes
.mfCount
);
6853 IterateParametersIfs(ResultFunc
);
6856 void ScInterpreter::ScCountIfs()
6858 sal_uInt8 nParamCount
= GetByte();
6860 if (nParamCount
< 2 || (nParamCount
% 2 != 0))
6862 PushError( FormulaError::ParameterExpected
);
6866 auto ResultFunc
= []( const sc::ParamIfsResult
& rRes
)
6868 return rRes
.mfCount
;
6870 IterateParametersIfs(ResultFunc
);
6873 void ScInterpreter::ScMinIfs_MS()
6875 sal_uInt8 nParamCount
= GetByte();
6877 if (nParamCount
< 3 || (nParamCount
% 2 != 1))
6879 PushError( FormulaError::ParameterExpected
);
6883 auto ResultFunc
= []( const sc::ParamIfsResult
& rRes
)
6885 return (rRes
.mfMin
< std::numeric_limits
<double>::max()) ? rRes
.mfMin
: 0.0;
6887 IterateParametersIfs(ResultFunc
);
6891 void ScInterpreter::ScMaxIfs_MS()
6893 sal_uInt8 nParamCount
= GetByte();
6895 if (nParamCount
< 3 || (nParamCount
% 2 != 1))
6897 PushError( FormulaError::ParameterExpected
);
6901 auto ResultFunc
= []( const sc::ParamIfsResult
& rRes
)
6903 return (rRes
.mfMax
> std::numeric_limits
<double>::lowest()) ? rRes
.mfMax
: 0.0;
6905 IterateParametersIfs(ResultFunc
);
6908 void ScInterpreter::ScLookup()
6910 sal_uInt8 nParamCount
= GetByte();
6911 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
6914 ScMatrixRef pDataMat
= nullptr, pResMat
= nullptr;
6915 SCCOL nCol1
= 0, nCol2
= 0, nResCol1
= 0, nResCol2
= 0;
6916 SCROW nRow1
= 0, nRow2
= 0, nResRow1
= 0, nResRow2
= 0;
6917 SCTAB nTab1
= 0, nResTab
= 0;
6918 SCSIZE nLenMajor
= 0; // length of major direction
6919 bool bVertical
= true; // whether to lookup vertically or horizontally
6921 // The third parameter, result array, double, string and reference.
6922 double fResVal
= 0.0;
6923 svl::SharedString aResStr
;
6924 StackVar eResArrayType
= svUnknown
;
6926 if (nParamCount
== 3)
6928 eResArrayType
= GetStackType();
6929 switch (eResArrayType
)
6934 PopDoubleRef(nResCol1
, nResRow1
, nResTab
,
6935 nResCol2
, nResRow2
, nTabJunk
);
6936 if (nResTab
!= nTabJunk
||
6937 ((nResRow2
- nResRow1
) > 0 && (nResCol2
- nResCol1
) > 0))
6939 // The result array must be a vector.
6940 PushIllegalParameter();
6946 PopSingleRef( nResCol1
, nResRow1
, nResTab
);
6947 nResCol2
= nResCol1
;
6948 nResRow2
= nResRow1
;
6951 case svExternalSingleRef
:
6952 case svExternalDoubleRef
:
6954 pResMat
= GetMatrix();
6957 PushIllegalParameter();
6961 pResMat
->GetDimensions(nC
, nR
);
6962 if (nC
!= 1 && nR
!= 1)
6964 // Result matrix must be a vector.
6965 PushIllegalParameter();
6971 fResVal
= GetDouble();
6974 aResStr
= GetString();
6977 PushIllegalParameter();
6982 // For double, string and single reference.
6983 double fDataVal
= 0.0;
6984 svl::SharedString aDataStr
;
6986 bool bValueData
= false;
6988 // Get the data-result range and also determine whether this is vertical
6989 // lookup or horizontal lookup.
6991 StackVar eDataArrayType
= GetStackType();
6992 switch (eDataArrayType
)
6997 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTabJunk
);
6998 if (nTab1
!= nTabJunk
)
7000 PushIllegalParameter();
7003 bVertical
= (nRow2
- nRow1
) >= (nCol2
- nCol1
);
7004 nLenMajor
= bVertical
? nRow2
- nRow1
+ 1 : nCol2
- nCol1
+ 1;
7008 case svExternalSingleRef
:
7009 case svExternalDoubleRef
:
7011 pDataMat
= GetMatrix();
7014 PushIllegalParameter();
7019 pDataMat
->GetDimensions(nC
, nR
);
7020 bVertical
= (nR
>= nC
);
7021 nLenMajor
= bVertical
? nR
: nC
;
7026 fDataVal
= GetDouble();
7032 aDataStr
= GetString();
7037 PopSingleRef( aDataAdr
);
7038 ScRefCellValue
aCell(mrDoc
, aDataAdr
);
7039 if (aCell
.hasEmptyValue())
7041 // Empty cells aren't found anywhere, bail out early.
7042 SetError( FormulaError::NotAvailable
);
7044 else if (aCell
.hasNumeric())
7046 fDataVal
= GetCellValue(aDataAdr
, aCell
);
7050 GetCellString(aDataStr
, aCell
);
7054 SetError( FormulaError::IllegalParameter
);
7057 if (nGlobalError
!= FormulaError::NONE
)
7059 PushError( nGlobalError
);
7063 // Get the lookup value.
7065 ScQueryParam aParam
;
7066 ScQueryEntry
& rEntry
= aParam
.GetEntry(0);
7067 if ( !FillEntry(rEntry
) )
7070 if ( eDataArrayType
== svDouble
|| eDataArrayType
== svString
||
7071 eDataArrayType
== svSingleRef
)
7073 // Delta position for a single value is always 0.
7075 // Found if data <= query, but not if query is string and found data is
7076 // numeric or vice versa. This is how Excel does it but doesn't
7079 bool bFound
= false;
7080 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
7084 if (rItem
.meType
== ScQueryEntry::ByString
)
7087 bFound
= (fDataVal
<= rItem
.mfVal
);
7091 if (rItem
.meType
!= ScQueryEntry::ByString
)
7094 bFound
= (ScGlobal::GetCollator().compareString(aDataStr
.getString(), rItem
.maString
.getString()) <= 0);
7105 if (pResMat
->IsValue( 0, 0 ))
7106 PushDouble(pResMat
->GetDouble( 0, 0 ));
7108 PushString(pResMat
->GetString(0, 0));
7110 else if (nParamCount
== 3)
7112 switch (eResArrayType
)
7115 PushDouble( fResVal
);
7118 PushString( aResStr
);
7122 PushCellResultToken( true, ScAddress( nResCol1
, nResRow1
, nResTab
), nullptr, nullptr);
7125 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, single value data");
7126 PushIllegalParameter();
7131 switch (eDataArrayType
)
7134 PushDouble( fDataVal
);
7137 PushString( aDataStr
);
7140 PushCellResultToken( true, aDataAdr
, nullptr, nullptr);
7143 assert(!"ScInterpreter::ScLookup: unhandled eDataArrayType, single value data");
7144 PushIllegalParameter();
7150 // Now, perform the search to compute the delta position (nDelta).
7154 // Data array is given as a matrix.
7155 rEntry
.bDoQuery
= true;
7156 rEntry
.eOp
= SC_LESS_EQUAL
;
7157 bool bFound
= false;
7160 pDataMat
->GetDimensions(nC
, nR
);
7162 // Do not propagate errors from matrix while copying to vector.
7163 pDataMat
->SetErrorInterpreter( nullptr);
7165 // Excel has an undocumented behaviour in that it seems to internally
7166 // sort an interim array (i.e. error values specifically #DIV/0! are
7167 // sorted to the end) or ignore error values that makes these "get last
7168 // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A)
7170 // Instead of sorting a million entries of which mostly only a bunch of
7171 // rows are filled and moving error values to the end which most are
7172 // already anyway, assume the matrix to be sorted except error values
7173 // and omit the coded DoubleError values.
7174 // Do this only for a numeric matrix (that includes errors coded as
7175 // doubles), which covers the case in question.
7176 /* TODO: it's unclear whether this really matches Excel behaviour in
7177 * all constellations or if there are cases that include unsorted error
7178 * values and thus yield arbitrary binary search results or something
7179 * different or whether there are cases where error values are also
7180 * omitted from mixed numeric/string arrays or if it's not an interim
7181 * matrix but a cell range reference instead. */
7182 const bool bOmitErrorValues
= (eDataArrayType
== svMatrix
&& pDataMat
->IsNumeric());
7184 // In case of non-vector matrix, only search the first row or column.
7185 ScMatrixRef pDataMat2
;
7186 std::vector
<SCCOLROW
> vIndex
;
7187 if (bOmitErrorValues
)
7189 std::vector
<double> vArray
;
7190 VectorMatrixAccessor
aMatAcc(*pDataMat
, bVertical
);
7191 const SCSIZE nElements
= aMatAcc
.GetElementCount();
7192 for (SCSIZE i
=0; i
< nElements
; ++i
)
7194 const double fVal
= aMatAcc
.GetDouble(i
);
7195 if (std::isfinite(fVal
))
7197 vArray
.push_back(fVal
);
7198 vIndex
.push_back(i
);
7206 const size_t nElems
= vArray
.size();
7207 if (nElems
== nElements
)
7209 // No error value omitted, use as is.
7210 pDataMat2
= pDataMat
;
7211 std::vector
<SCCOLROW
>().swap( vIndex
);
7218 ScMatrixRef pTempMat
= GetNewMat( 1, nElems
, /*bEmpty*/true );
7219 pTempMat
->PutDoubleVector( vArray
, 0, 0);
7220 pDataMat2
= pTempMat
;
7224 ScMatrixRef pTempMat
= GetNewMat( nElems
, 1, /*bEmpty*/true );
7225 for (size_t i
=0; i
< nElems
; ++i
)
7226 pTempMat
->PutDouble( vArray
[i
], i
, 0);
7227 pDataMat2
= pTempMat
;
7233 // Just use as is with the VectorMatrixAccessor.
7234 pDataMat2
= pDataMat
;
7237 // Do not propagate errors from matrix while searching.
7238 pDataMat2
->SetErrorInterpreter( nullptr);
7240 VectorMatrixAccessor
aMatAcc2(*pDataMat2
, bVertical
);
7242 // binary search for non-equality mode (the source data is
7243 // assumed to be sorted in ascending order).
7245 SCCOLROW nDelta
= -1;
7246 bool bMatchWholeCell
= mrDoc
.GetDocOptions().IsMatchWholeCell();
7248 SCSIZE nFirst
= 0, nLast
= nLenMajor
-1; //, nHitIndex = 0;
7249 for (SCSIZE nLen
= nLast
-nFirst
; nLen
> 0; nLen
= nLast
-nFirst
)
7251 SCSIZE nMid
= nFirst
+ nLen
/2;
7252 sal_Int32 nCmp
= lcl_CompareMatrix2Query( nMid
, aMatAcc2
, aParam
, rEntry
, bMatchWholeCell
);
7255 // exact match. find the last item with the same value.
7256 lcl_GetLastMatch( nMid
, aMatAcc2
, nLenMajor
);
7262 if (nLen
== 1) // first and last items are next to each other.
7264 nDelta
= nCmp
< 0 ? nLast
- 1 : nFirst
- 1;
7265 // If already the 1st item is greater there's nothing found.
7266 bFound
= (nDelta
>= 0);
7276 if (nDelta
== static_cast<SCCOLROW
>(nLenMajor
-2)) // last item
7278 sal_Int32 nCmp
= lcl_CompareMatrix2Query(nDelta
+1, aMatAcc2
, aParam
, rEntry
, bMatchWholeCell
);
7281 // either the last item is an exact match or the real
7282 // hit is beyond the last item.
7287 else if (nDelta
> 0) // valid hit must be 2nd item or higher
7293 // With 0-9 < A-Z, if query is numeric and data found is string, or
7294 // vice versa, the (yet another undocumented) Excel behavior is to
7295 // return #N/A instead.
7299 if (!vIndex
.empty())
7300 nDelta
= vIndex
[nDelta
];
7302 VectorMatrixAccessor
aMatAcc(*pDataMat
, bVertical
);
7303 SCCOLROW i
= nDelta
;
7304 SCSIZE n
= aMatAcc
.GetElementCount();
7305 if (o3tl::make_unsigned(i
) >= n
)
7306 i
= static_cast<SCCOLROW
>(n
);
7307 bool bByString
= rEntry
.GetQueryItem().meType
== ScQueryEntry::ByString
;
7308 if (bByString
== aMatAcc
.IsValue(i
))
7318 // Now that we've found the delta, push the result back to the cell.
7322 VectorMatrixAccessor
aResMatAcc(*pResMat
, bVertical
);
7323 // Result array is matrix.
7324 // Note this does not replicate the other dimension.
7325 if (o3tl::make_unsigned(nDelta
) >= aResMatAcc
.GetElementCount())
7330 if (aResMatAcc
.IsValue(nDelta
))
7331 PushDouble(aResMatAcc
.GetDouble(nDelta
));
7333 PushString(aResMatAcc
.GetString(nDelta
));
7335 else if (nParamCount
== 3)
7337 /* TODO: the entire switch is a copy of the cell range search
7338 * result, factor out. */
7339 switch (eResArrayType
)
7344 // Use the result array vector. Note that the result array is assumed
7345 // to be a vector (i.e. 1-dimensional array).
7348 aAdr
.SetTab(nResTab
);
7349 bool bResVertical
= (nResRow2
- nResRow1
) > 0;
7352 SCROW nTempRow
= static_cast<SCROW
>(nResRow1
+ nDelta
);
7353 if (nTempRow
> mrDoc
.MaxRow())
7358 aAdr
.SetCol(nResCol1
);
7359 aAdr
.SetRow(nTempRow
);
7363 SCCOL nTempCol
= static_cast<SCCOL
>(nResCol1
+ nDelta
);
7364 if (nTempCol
> mrDoc
.MaxCol())
7369 aAdr
.SetCol(nTempCol
);
7370 aAdr
.SetRow(nResRow1
);
7372 PushCellResultToken( true, aAdr
, nullptr, nullptr);
7382 switch (eResArrayType
)
7385 PushDouble( fResVal
);
7388 PushString( aResStr
);
7397 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, array search");
7398 PushIllegalParameter();
7403 // No result array. Use the data array to get the final value from.
7404 // Propagate errors from matrix again.
7405 pDataMat
->SetErrorInterpreter( this);
7408 if (pDataMat
->IsValue(nC
-1, nDelta
))
7409 PushDouble(pDataMat
->GetDouble(nC
-1, nDelta
));
7411 PushString(pDataMat
->GetString(nC
-1, nDelta
));
7415 if (pDataMat
->IsValue(nDelta
, nR
-1))
7416 PushDouble(pDataMat
->GetDouble(nDelta
, nR
-1));
7418 PushString(pDataMat
->GetString(nDelta
, nR
-1));
7425 // Perform cell range search.
7427 aParam
.nCol1
= nCol1
;
7428 aParam
.nRow1
= nRow1
;
7429 aParam
.nCol2
= bVertical
? nCol1
: nCol2
;
7430 aParam
.nRow2
= bVertical
? nRow2
: nRow1
;
7431 aParam
.bByRow
= bVertical
;
7433 rEntry
.bDoQuery
= true;
7434 rEntry
.eOp
= SC_LESS_EQUAL
;
7435 rEntry
.nField
= nCol1
;
7436 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
7437 if (rItem
.meType
== ScQueryEntry::ByString
)
7438 aParam
.eSearchType
= DetectSearchType(rItem
.maString
.getString(), mrDoc
);
7440 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab1
, aParam
, false, false);
7443 // Advance Entry.nField in iterator upon switching columns if
7445 aCellIter
.SetAdvanceQueryParamEntryField(!bVertical
);
7446 if ( !aCellIter
.FindEqualOrSortedLastInRange(nC
, nR
) )
7452 SCCOLROW nDelta
= bVertical
? static_cast<SCSIZE
>(nR
-nRow1
) : static_cast<SCSIZE
>(nC
-nCol1
);
7456 VectorMatrixAccessor
aResMatAcc(*pResMat
, bVertical
);
7457 // Use the matrix result array.
7458 // Note this does not replicate the other dimension.
7459 if (o3tl::make_unsigned(nDelta
) >= aResMatAcc
.GetElementCount())
7464 if (aResMatAcc
.IsValue(nDelta
))
7465 PushDouble(aResMatAcc
.GetDouble(nDelta
));
7467 PushString(aResMatAcc
.GetString(nDelta
));
7469 else if (nParamCount
== 3)
7471 /* TODO: the entire switch is a copy of the array search result, factor
7473 switch (eResArrayType
)
7478 // Use the result array vector. Note that the result array is assumed
7479 // to be a vector (i.e. 1-dimensional array).
7482 aAdr
.SetTab(nResTab
);
7483 bool bResVertical
= (nResRow2
- nResRow1
) > 0;
7486 SCROW nTempRow
= static_cast<SCROW
>(nResRow1
+ nDelta
);
7487 if (nTempRow
> mrDoc
.MaxRow())
7492 aAdr
.SetCol(nResCol1
);
7493 aAdr
.SetRow(nTempRow
);
7497 SCCOL nTempCol
= static_cast<SCCOL
>(nResCol1
+ nDelta
);
7498 if (nTempCol
> mrDoc
.MaxCol())
7503 aAdr
.SetCol(nTempCol
);
7504 aAdr
.SetRow(nResRow1
);
7506 PushCellResultToken( true, aAdr
, nullptr, nullptr);
7516 switch (eResArrayType
)
7519 PushDouble( fResVal
);
7522 PushString( aResStr
);
7531 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, range search");
7532 PushIllegalParameter();
7537 // Regardless of whether or not the result array exists, the last
7538 // array is always used as the "result" array.
7544 SCROW nTempRow
= static_cast<SCROW
>(nRow1
+ nDelta
);
7545 if (nTempRow
> mrDoc
.MaxRow())
7551 aAdr
.SetRow(nTempRow
);
7555 SCCOL nTempCol
= static_cast<SCCOL
>(nCol1
+ nDelta
);
7556 if (nTempCol
> mrDoc
.MaxCol())
7561 aAdr
.SetCol(nTempCol
);
7564 PushCellResultToken(true, aAdr
, nullptr, nullptr);
7568 void ScInterpreter::ScHLookup()
7570 CalculateLookup(true);
7573 void ScInterpreter::CalculateLookup(bool bHLookup
)
7575 sal_uInt8 nParamCount
= GetByte();
7576 if (!MustHaveParamCount(nParamCount
, 3, 4))
7579 // Optional 4th argument to declare whether or not the range is sorted.
7580 bool bSorted
= true;
7581 if (nParamCount
== 4)
7582 bSorted
= GetBool();
7584 // Index of column to search.
7585 double fIndex
= ::rtl::math::approxFloor( GetDouble() ) - 1.0;
7587 ScMatrixRef pMat
= nullptr;
7588 SCSIZE nC
= 0, nR
= 0;
7594 const ScComplexRefData
* refData
= nullptr;
7595 StackVar eType
= GetStackType();
7596 if (eType
== svDoubleRef
)
7598 refData
= GetStackDoubleRef(0);
7600 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
7603 PushIllegalParameter();
7607 else if (eType
== svSingleRef
)
7609 PopSingleRef(nCol1
, nRow1
, nTab1
);
7613 else if (eType
== svMatrix
|| eType
== svExternalDoubleRef
|| eType
== svExternalSingleRef
)
7618 pMat
->GetDimensions(nC
, nR
);
7621 PushIllegalParameter();
7627 PushIllegalParameter();
7631 if ( fIndex
< 0.0 || (bHLookup
? (pMat
? (fIndex
>= nR
) : (fIndex
+nRow1
> nRow2
)) : (pMat
? (fIndex
>= nC
) : (fIndex
+nCol1
> nCol2
)) ) )
7633 PushIllegalArgument();
7637 SCROW nZIndex
= static_cast<SCROW
>(fIndex
);
7638 SCCOL nSpIndex
= static_cast<SCCOL
>(fIndex
);
7642 nZIndex
+= nRow1
; // value row
7643 nSpIndex
= sal::static_int_cast
<SCCOL
>( nSpIndex
+ nCol1
); // value column
7646 if (nGlobalError
!= FormulaError::NONE
)
7648 PushIllegalParameter();
7652 ScQueryParam aParam
;
7653 aParam
.nCol1
= nCol1
;
7654 aParam
.nRow1
= nRow1
;
7657 aParam
.nCol2
= nCol2
;
7658 aParam
.nRow2
= nRow1
; // search only in the first row
7659 aParam
.bByRow
= false;
7663 aParam
.nCol2
= nCol1
; // search only in the first column
7664 aParam
.nRow2
= nRow2
;
7665 aParam
.nTab
= nTab1
;
7668 ScQueryEntry
& rEntry
= aParam
.GetEntry(0);
7669 rEntry
.bDoQuery
= true;
7671 rEntry
.eOp
= SC_LESS_EQUAL
;
7672 if ( !FillEntry(rEntry
) )
7675 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
7676 svl::SharedString aParamStr
;
7677 if (rItem
.meType
== ScQueryEntry::ByString
)
7679 aParam
.eSearchType
= DetectSearchType(rItem
.maString
.getString(), mrDoc
);
7680 aParamStr
= rItem
.maString
;
7685 SCSIZE nMatCount
= bHLookup
? nC
: nR
;
7686 SCSIZE nDelta
= SCSIZE_MAX
;
7687 if (rItem
.meType
== ScQueryEntry::ByString
)
7690 //TODO: enable regex on matrix strings
7694 CollatorWrapper
& rCollator
= ScGlobal::GetCollator();
7695 for (SCSIZE i
= 0; i
< nMatCount
; i
++)
7697 if (bHLookup
? pMat
->IsStringOrEmpty(i
, 0) : pMat
->IsStringOrEmpty(0, i
))
7700 rCollator
.compareString(
7701 bHLookup
? pMat
->GetString(i
,0).getString() : pMat
->GetString(0,i
).getString(), aParamStr
.getString());
7704 else if (i
>0) // #i2168# ignore first mismatch
7715 for (SCSIZE i
= 0; i
< nMatCount
; i
++)
7717 if (pMat
->IsStringOrEmpty(i
, 0))
7719 if (pMat
->GetString(i
,0).getDataIgnoreCase() == aParamStr
.getDataIgnoreCase())
7729 nDelta
= pMat
->MatchStringInColumns(aParamStr
, 0, 0);
7737 // #i2168# ignore strings
7738 for (SCSIZE i
= 0; i
< nMatCount
; i
++)
7740 if (!(bHLookup
? pMat
->IsStringOrEmpty(i
, 0) : pMat
->IsStringOrEmpty(0, i
)))
7742 if ((bHLookup
? pMat
->GetDouble(i
,0) : pMat
->GetDouble(0,i
)) <= rItem
.mfVal
)
7753 for (SCSIZE i
= 0; i
< nMatCount
; i
++)
7755 if (! pMat
->IsStringOrEmpty(i
, 0) )
7757 if ( pMat
->GetDouble(i
,0) == rItem
.mfVal
)
7767 nDelta
= pMat
->MatchDoubleInColumns(rItem
.mfVal
, 0, 0);
7771 if ( nDelta
!= SCSIZE_MAX
)
7773 SCSIZE nX
= static_cast<SCSIZE
>(nSpIndex
);
7780 nY
= static_cast<SCSIZE
>(nZIndex
);
7784 assert( nX
< nC
&& nY
< nR
);
7785 if (!(rItem
.meType
== ScQueryEntry::ByString
&& pMat
->IsValue( nXs
, nYs
)))
7787 if (pMat
->IsStringOrEmpty( nX
, nY
))
7788 PushString(pMat
->GetString( nX
, nY
).getString());
7790 PushDouble(pMat
->GetDouble( nX
, nY
));
7802 rEntry
.nField
= nCol1
;
7803 bool bFound
= false;
7807 rEntry
.eOp
= SC_LESS_EQUAL
;
7810 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab1
, aParam
, false, false);
7811 // advance Entry.nField in Iterator upon switching columns
7812 aCellIter
.SetAdvanceQueryParamEntryField( true );
7816 bFound
= aCellIter
.FindEqualOrSortedLastInRange( nCol
, nRow1_temp
);
7818 else if ( aCellIter
.GetFirst() )
7821 nCol
= aCellIter
.GetCol();
7827 ScAddress
aResultPos( nCol1
, nRow1
, nTab1
);
7828 bFound
= LookupQueryWithCache( aResultPos
, aParam
, refData
, 0, SC_OPCODE_V_LOOKUP
);
7829 nRow
= aResultPos
.Row();
7835 ScAddress
aAdr( nCol
, nRow
, nTab1
);
7836 PushCellResultToken( true, aAdr
, nullptr, nullptr);
7843 bool ScInterpreter::FillEntry(ScQueryEntry
& rEntry
)
7845 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
7846 switch ( GetStackType() )
7850 rItem
.meType
= ScQueryEntry::ByValue
;
7851 rItem
.mfVal
= GetDouble();
7856 rItem
.meType
= ScQueryEntry::ByString
;
7857 rItem
.maString
= GetString();
7864 if ( !PopDoubleRefOrSingleRef( aAdr
) )
7869 ScRefCellValue
aCell(mrDoc
, aAdr
);
7870 if (aCell
.hasNumeric())
7872 rItem
.meType
= ScQueryEntry::ByValue
;
7873 rItem
.mfVal
= GetCellValue(aAdr
, aCell
);
7877 GetCellString(rItem
.maString
, aCell
);
7878 rItem
.meType
= ScQueryEntry::ByString
;
7882 case svExternalDoubleRef
:
7883 case svExternalSingleRef
:
7886 svl::SharedString aStr
;
7887 const ScMatValType nType
= GetDoubleOrStringFromMatrix(rItem
.mfVal
, aStr
);
7888 rItem
.maString
= aStr
;
7889 rItem
.meType
= ScMatrix::IsNonValueType(nType
) ?
7890 ScQueryEntry::ByString
: ScQueryEntry::ByValue
;
7895 PushIllegalParameter();
7898 } // switch ( GetStackType() )
7902 void ScInterpreter::ScVLookup()
7904 CalculateLookup(false);
7907 void ScInterpreter::ScXLookup()
7910 -use VectorSearchArguments and SearchVectorForValue() with ScLookup, ScHLookup and ScVLookup
7911 as well to reduce redundant code, can de done later with lots of other MATCH/LOOKUP related code
7913 -BinarySearch not supported for columns (horizontal search), now just use linear mode in this case
7914 -improve efficiency of code
7916 sal_uInt8 nParamCount
= GetByte();
7917 if ( !MustHaveParamCount( nParamCount
, 3, 6 ) )
7920 VectorSearchArguments vsa
;
7921 vsa
.nSearchOpCode
= SC_OPCODE_X_LOOKUP
;
7923 if ( nParamCount
== 6 )
7925 sal_Int16 k
= GetInt16();
7926 if ( k
>= -2 && k
<= 2 && k
!= 0 )
7927 vsa
.eSearchMode
= static_cast<SearchMode
>(k
);
7930 PushIllegalParameter();
7935 vsa
.eSearchMode
= searchfwd
;
7937 if ( nParamCount
>= 5 )
7939 sal_Int16 k
= GetInt16();
7940 if ( k
>= -1 && k
<= 3 )
7941 vsa
.eMatchMode
= static_cast<MatchMode
>(k
);
7944 PushIllegalParameter();
7949 vsa
.eMatchMode
= exactorNA
;
7951 // Optional 4th argument to set return values if not found (default is #N/A)
7952 formula::FormulaConstTokenRef xNotFound
;
7953 FormulaError nFirstMatchError
= FormulaError::NONE
;
7954 if ( nParamCount
>= 4 && GetStackType() != svEmptyCell
)
7956 xNotFound
= PopToken();
7957 nFirstMatchError
= xNotFound
->GetError();
7958 nGlobalError
= FormulaError::NONE
; // propagate only for match or active result path
7961 // 3rd argument is return value array
7962 ScMatrixRef prMat
= nullptr;
7963 SCCOL nSearchCol1
= 0;
7964 SCROW nSearchRow1
= 0;
7965 SCTAB nSearchTab1
= 0;
7966 SCCOL nSearchCol2
= 0;
7967 SCROW nSearchRow2
= 0;
7968 SCTAB nSearchTab2
= 0;
7969 SCSIZE nrC
= 0, nrR
= 0;
7971 switch ( GetStackType() )
7974 PopSingleRef(nSearchCol1
, nSearchRow1
, nSearchTab1
);
7975 nSearchCol2
= nSearchCol1
;
7976 nSearchRow2
= nSearchRow1
;
7977 nrC
= nSearchCol2
- nSearchCol1
+ 1;
7978 nrR
= nSearchRow2
- nSearchRow1
+ 1;
7982 PopDoubleRef(nSearchCol1
, nSearchRow1
, nSearchTab1
, nSearchCol2
, nSearchRow2
, nSearchTab2
);
7983 if (nSearchTab1
!= nSearchTab2
)
7985 PushIllegalParameter();
7988 nrC
= nSearchCol2
- nSearchCol1
+ 1;
7989 nrR
= nSearchRow2
- nSearchRow1
+ 1;
7993 case svExternalDoubleRef
:
7995 if (GetStackType() == svMatrix
)
7996 prMat
= PopMatrix();
7998 PopExternalDoubleRef(prMat
);
8002 PushIllegalParameter();
8005 prMat
->GetDimensions(nrC
, nrR
);
8010 PushIllegalParameter();
8014 // 2nd argument is vector to be searched
8015 SCSIZE nsC
= 0, nsR
= 0;
8016 switch ( GetStackType() )
8019 vsa
.pMatSrc
= nullptr;
8020 PopSingleRef( vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
);
8021 vsa
.nCol2
= vsa
.nCol1
;
8022 vsa
.nRow2
= vsa
.nRow1
;
8023 nsC
= vsa
.nCol2
- vsa
.nCol1
+ 1;
8024 nsR
= vsa
.nRow2
- vsa
.nRow1
+ 1;
8028 vsa
.pMatSrc
= nullptr;
8030 PopDoubleRef(vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
, vsa
.nCol2
, vsa
.nRow2
, nTab2
);
8031 if (vsa
.nTab1
!= nTab2
|| (vsa
.nCol1
!= vsa
.nCol2
&& vsa
.nRow1
!= vsa
.nRow2
))
8033 PushIllegalParameter();
8036 nsC
= vsa
.nCol2
- vsa
.nCol1
+ 1;
8037 nsR
= vsa
.nRow2
- vsa
.nRow1
+ 1;
8041 case svExternalDoubleRef
:
8043 if (GetStackType() == svMatrix
)
8044 vsa
.pMatSrc
= PopMatrix();
8046 PopExternalDoubleRef(vsa
.pMatSrc
);
8050 PushIllegalParameter();
8053 vsa
.pMatSrc
->GetDimensions( nsC
, nsR
);
8058 PushIllegalParameter();
8061 if ( ( nsR
>= nsC
&& nsR
!= nrR
) || ( nsR
< nsC
&& nsC
!= nrC
) )
8063 // search matrix must have same number of elements as result matrix in search direction
8064 PushIllegalParameter();
8068 // 1st argument is search value
8069 if (nGlobalError
== FormulaError::NONE
)
8071 switch ( GetRawStackType() )
8076 vsa
.isEmptySearch
= true;
8077 vsa
.isStringSearch
= false;
8078 vsa
.sSearchStr
= GetString();
8084 vsa
.isStringSearch
= false;
8085 vsa
.fSearchVal
= GetDouble();
8091 vsa
.isStringSearch
= true;
8092 vsa
.sSearchStr
= GetString();
8100 if ( !PopDoubleRefOrSingleRef( aAdr
) )
8105 ScRefCellValue
aCell(mrDoc
, aAdr
);
8106 if (aCell
.hasNumeric())
8108 vsa
.isStringSearch
= false;
8109 vsa
.fSearchVal
= GetCellValue(aAdr
, aCell
);
8113 vsa
.isStringSearch
= true;
8114 GetCellString(vsa
.sSearchStr
, aCell
);
8119 case svExternalSingleRef
:
8121 ScExternalRefCache::TokenRef pToken
;
8122 PopExternalSingleRef(pToken
);
8123 if (nGlobalError
!= FormulaError::NONE
)
8125 PushError( nGlobalError
);
8128 if (pToken
->GetType() == svDouble
)
8130 vsa
.isStringSearch
= false;
8131 vsa
.fSearchVal
= pToken
->GetDouble();
8135 vsa
.isStringSearch
= true;
8136 vsa
.sSearchStr
= pToken
->GetString();
8141 case svExternalDoubleRef
:
8144 ScMatValType nType
= GetDoubleOrStringFromMatrix(
8145 vsa
.fSearchVal
, vsa
.sSearchStr
);
8146 vsa
.isStringSearch
= ScMatrix::IsNonValueType(nType
);
8152 PushIllegalParameter();
8159 if ( SearchVectorForValue( vsa
) )
8161 // found, output result
8162 assert( vsa
.bVLookup
? ( o3tl::make_unsigned(vsa
.nIndex
) < nrR
) :
8163 ( o3tl::make_unsigned(vsa
.nIndex
) < nrC
) );
8170 nX
= static_cast<SCSIZE
>(0);
8178 nY
= static_cast<SCSIZE
>(0);
8182 // if result has more than one row or column push double ref or matrix, else push single ref
8183 if ( nResCols
> 1 || nResRows
> 1 )
8187 // result is matrix, make / fill matrix with output and push that
8188 ScMatrixRef pResMat
= GetNewMat(nResCols
, nResRows
, /*bEmpty*/true);
8191 for (SCSIZE i
= 0; i
< nResCols
; i
++)
8193 for (SCSIZE j
= 0; j
< nResRows
; j
++)
8207 if (prMat
->IsEmptyCell(ri
, rj
))
8208 pResMat
->PutEmpty(i
, j
);
8209 else if (prMat
->IsStringOrEmpty(ri
, rj
))
8210 pResMat
->PutString(prMat
->GetString(ri
, rj
), i
, j
);
8212 pResMat
->PutDouble(prMat
->GetDouble(ri
, rj
), i
, j
);
8215 PushMatrix(pResMat
);
8219 PushIllegalParameter();
8225 // result is a double ref
8226 PushDoubleRef(nSearchCol1
+ nX
, nSearchRow1
+ nY
, nSearchTab1
,
8227 nSearchCol1
+ (nResCols
- 1) + nX
, nSearchRow1
+ (nResRows
- 1) + nY
, nSearchTab1
);
8232 // result is a single ref
8233 PushSingleRef(nSearchCol1
+ nX
, nSearchRow1
+ nY
, nSearchTab1
);
8238 if ( vsa
.isResultNA
)
8240 if ( xNotFound
&& ( xNotFound
->GetType() != svMissing
) )
8242 nGlobalError
= nFirstMatchError
;
8243 PushTokenRef(xNotFound
);
8251 void ScInterpreter::ScFilter()
8253 sal_uInt8 nParamCount
= GetByte();
8254 if (!MustHaveParamCount(nParamCount
, 2, 3))
8257 // Optional 3th argument to set the value to return if all values
8258 // in the included array are empty (filter returns nothing)
8259 formula::FormulaConstTokenRef xNotFound
;
8260 if (nParamCount
== 3 && GetStackType() != svEmptyCell
)
8261 xNotFound
= PopToken();
8263 SCCOL nCondResultColEnd
= 0;
8264 SCROW nCondResultRowEnd
= 0;
8265 ScMatrixRef pCondResultMatrix
= nullptr;
8266 std::vector
<double> aResValues
;
8268 // take 2nd argument criteria bool array
8269 switch ( GetStackType() )
8272 case svExternalDoubleRef
:
8273 case svExternalSingleRef
:
8277 pCondResultMatrix
= GetMatrix();
8278 if (!pCondResultMatrix
)
8280 PushError(FormulaError::IllegalParameter
);
8284 pCondResultMatrix
->GetDimensions(nC
, nR
);
8285 nCondResultColEnd
= static_cast<SCCOL
>(nC
- 1);
8286 nCondResultRowEnd
= static_cast<SCROW
>(nR
- 1);
8288 // only 1 dimension of filtering allowed (also in excel)
8289 if (nCondResultColEnd
> 0 && nCondResultRowEnd
> 0)
8291 PushError(FormulaError::NoValue
);
8295 // result matrix is filled with boolean values.
8296 pCondResultMatrix
->GetDoubleArray(aResValues
);
8298 FormulaError nError
= FormulaError::NONE
;
8299 auto matchNum
= [&nMatch
, &nError
](double i
) {
8300 nError
= GetDoubleErrorValue(i
);
8301 if (nError
!= FormulaError::NONE
)
8313 if (auto it
= std::find_if(aResValues
.begin(), aResValues
.end(), matchNum
); it
!= aResValues
.end())
8323 PushIllegalParameter();
8328 // bail out, no need to evaluate other arguments
8329 if (nGlobalError
!= FormulaError::NONE
)
8331 PushError(nGlobalError
);
8335 SCCOL nQueryCol1
= 0;
8336 SCROW nQueryRow1
= 0;
8337 SCCOL nQueryCol2
= 0;
8338 SCROW nQueryRow2
= 0;
8339 ScMatrixRef pQueryMatrix
= nullptr;
8340 // take 1st argument range
8341 switch ( GetStackType() )
8346 case svExternalSingleRef
:
8347 case svExternalDoubleRef
:
8349 pQueryMatrix
= GetMatrix();
8352 PushError( FormulaError::IllegalParameter
);
8356 pQueryMatrix
->GetDimensions( nC
, nR
);
8357 nQueryCol2
= static_cast<SCCOL
>(nC
- 1);
8358 nQueryRow2
= static_cast<SCROW
>(nR
- 1);
8362 PushError( FormulaError::IllegalParameter
);
8366 // bail out, no need to set a matrix if we have no result
8369 if (xNotFound
&& (xNotFound
->GetType() != svMissing
))
8370 PushTokenRef(xNotFound
);
8372 PushError(FormulaError::NestedArray
);
8377 ScMatrixRef pResMat
= nullptr;
8378 if (nQueryCol2
== nCondResultColEnd
&& nCondResultColEnd
> 0)
8380 pResMat
= GetNewMat(nMatch
, nQueryRow2
+ 1 , /*bEmpty*/true);
8381 for (SCROW iR
= nQueryRow1
; iR
<= nQueryRow2
; iR
++)
8383 for (size_t iC
= 0; iC
< aResValues
.size(); iC
++)
8385 if (aResValues
[iC
] > 0)
8387 if (pQueryMatrix
->IsEmptyCell(iC
, iR
))
8388 pResMat
->PutEmptyTrans(nResPos
++);
8389 else if (pQueryMatrix
->IsStringOrEmpty(iC
, iR
))
8390 pResMat
->PutStringTrans(pQueryMatrix
->GetString(iC
, iR
), nResPos
++);
8392 pResMat
->PutDoubleTrans(pQueryMatrix
->GetDouble(iC
, iR
), nResPos
++);
8397 else if (nQueryRow2
== nCondResultRowEnd
&& nCondResultRowEnd
> 0)
8399 pResMat
= GetNewMat(nQueryCol2
+ 1, nMatch
, /*bEmpty*/true);
8400 for (SCCOL iC
= nQueryCol1
; iC
<= nQueryCol2
; iC
++)
8402 for (size_t iR
= 0; iR
< aResValues
.size(); iR
++)
8404 if (aResValues
[iR
] > 0)
8406 if (pQueryMatrix
->IsEmptyCell(iC
, iR
))
8407 pResMat
->PutEmpty(nResPos
++);
8408 else if (pQueryMatrix
->IsStringOrEmpty(iC
, iR
))
8409 pResMat
->PutString(pQueryMatrix
->GetString(iC
, iR
), nResPos
++);
8411 pResMat
->PutDouble(pQueryMatrix
->GetDouble(iC
, iR
), nResPos
++);
8418 PushError(FormulaError::IllegalParameter
);
8423 PushMatrix(pResMat
);
8425 PushError(FormulaError::NestedArray
);
8428 void ScInterpreter::ScSort()
8430 sal_uInt8 nParamCount
= GetByte();
8431 if (!MustHaveParamCount(nParamCount
, 1, 4))
8435 ScSortParam aSortData
;
8437 // 4th argument optional
8438 aSortData
.bByRow
= true; // default: By_Col = false --> bByRow = true
8439 if (nParamCount
== 4)
8440 aSortData
.bByRow
= !GetBool();
8442 // 3rd argument optional
8443 std::vector
<double> aSortOrderValues
{ 1.0 }; // default: 1 = asc, -1 = desc
8444 if (nParamCount
>= 3)
8446 bool bMissing
= IsMissing();
8447 ScMatrixRef pSortOrder
= GetMatrix();
8450 aSortOrderValues
.clear();
8451 pSortOrder
->GetDoubleArray(aSortOrderValues
);
8452 for (const double& sortOrder
: aSortOrderValues
)
8454 if (sortOrder
!= 1.0 && sortOrder
!= -1.0)
8456 PushIllegalParameter();
8463 // 2nd argument optional
8464 std::vector
<double> aSortIndexValues
{ 0.0 }; // default: first column or row
8465 if (nParamCount
>= 2)
8467 bool bMissing
= IsMissing();
8468 ScMatrixRef pSortIndex
= GetMatrix();
8471 aSortIndexValues
.clear();
8472 pSortIndex
->GetDoubleArray(aSortIndexValues
);
8473 for (double& sortIndex
: aSortIndexValues
)
8477 PushIllegalParameter();
8486 if (aSortIndexValues
.size() != aSortOrderValues
.size() && aSortOrderValues
.size() > 1)
8488 PushIllegalParameter();
8492 // 1st argument is vector to be sorted
8493 SCSIZE nsC
= 0, nsR
= 0;
8494 ScMatrixRef pMatSrc
= nullptr;
8495 switch ( GetStackType() )
8498 PopSingleRef(aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nSourceTab
);
8499 aSortData
.nCol2
= aSortData
.nCol1
;
8500 aSortData
.nRow2
= aSortData
.nRow1
;
8501 nsC
= aSortData
.nCol2
- aSortData
.nCol1
+ 1;
8502 nsR
= aSortData
.nRow2
- aSortData
.nRow1
+ 1;
8507 PopDoubleRef(aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nSourceTab
, aSortData
.nCol2
, aSortData
.nRow2
, nTab2
);
8508 if (aSortData
.nSourceTab
!= nTab2
)
8510 PushIllegalParameter();
8513 nsC
= aSortData
.nCol2
- aSortData
.nCol1
+ 1;
8514 nsR
= aSortData
.nRow2
- aSortData
.nRow1
+ 1;
8518 case svExternalSingleRef
:
8519 case svExternalDoubleRef
:
8521 pMatSrc
= GetMatrix();
8524 PushIllegalParameter();
8527 pMatSrc
->GetDimensions(nsC
, nsR
);
8528 aSortData
.nCol2
= nsC
- 1; // aSortData.nCol1 = 0
8529 aSortData
.nRow2
= nsR
- 1; // aSortData.nRow1 = 0
8534 PushIllegalParameter();
8538 aSortData
.maKeyState
.resize(aSortIndexValues
.size());
8539 for (size_t i
= 0; i
< aSortIndexValues
.size(); i
++)
8541 SCSIZE fIndex
= static_cast<SCSIZE
>(double_to_int32(aSortIndexValues
[i
]));
8542 if ((aSortData
.bByRow
&& fIndex
+ 1 > nsC
) || (!aSortData
.bByRow
&& fIndex
+ 1 > nsR
))
8544 PushIllegalParameter();
8548 aSortData
.maKeyState
[i
].bDoSort
= true;
8549 aSortData
.maKeyState
[i
].nField
= (aSortData
.bByRow
? aSortData
.nCol1
: aSortData
.nRow1
) + fIndex
;
8551 if (aSortIndexValues
.size() == aSortOrderValues
.size())
8552 aSortData
.maKeyState
[i
].bAscending
= (aSortOrderValues
[i
] == 1.0);
8554 aSortData
.maKeyState
[i
].bAscending
= (aSortOrderValues
[0] == 1.0);
8558 std::vector
<SCCOLROW
> aOrderIndices
= GetSortOrder(aSortData
, pMatSrc
);
8559 // create sorted matrix
8560 ScMatrixRef pResMat
= CreateSortedMatrix(aSortData
, pMatSrc
,
8561 ScRange(aSortData
.nCol1
, aSortData
.nRow1
, aSortData
.nSourceTab
,
8562 aSortData
.nCol2
, aSortData
.nRow2
, aSortData
.nSourceTab
),
8563 aOrderIndices
, nsC
, nsR
);
8566 PushMatrix(pResMat
);
8568 PushIllegalParameter();
8571 void ScInterpreter::ScSortBy()
8573 sal_uInt8 nParamCount
= GetByte();
8575 if (nParamCount
< 2/*|| (nParamCount % 2 != 1)*/)
8577 PushError(FormulaError::ParameterExpected
);
8581 sal_uInt8 nSortCount
= nParamCount
/ 2;
8583 ScSortParam aSortData
;
8584 aSortData
.maKeyState
.resize(nSortCount
);
8586 // 127th, ..., 3rd and 2nd argument: sort by range/array and sort orders pair
8587 sal_uInt8 nSortBy
= nSortCount
;
8588 ScMatrixRef pFullMatSortBy
= nullptr;
8589 while (nSortBy
-- > 0 && nGlobalError
== FormulaError::NONE
)
8591 // 3rd argument sort_order optional: default ascending
8592 if (nParamCount
>= 3 && (nParamCount
% 2 == 1))
8594 sal_Int8 nSortOrder
= static_cast<sal_Int8
>(GetInt32WithDefault(1));
8595 if (nSortOrder
!= 1 && nSortOrder
!= -1)
8597 PushIllegalParameter();
8600 aSortData
.maKeyState
[nSortBy
].bAscending
= (nSortOrder
== 1);
8604 // 2nd argument: take sort by ranges
8605 ScMatrixRef pMatSortBy
= nullptr;
8606 SCSIZE nbyC
= 0, nbyR
= 0;
8607 switch (GetStackType())
8612 case svExternalSingleRef
:
8613 case svExternalDoubleRef
:
8615 if (nSortCount
== 1)
8617 pFullMatSortBy
= GetMatrix();
8618 if (!pFullMatSortBy
)
8620 PushIllegalParameter();
8623 pFullMatSortBy
->GetDimensions(nbyC
, nbyR
);
8627 pMatSortBy
= GetMatrix();
8630 PushIllegalParameter();
8633 pMatSortBy
->GetDimensions(nbyC
, nbyR
);
8636 // last->first (backward) sortby array
8637 if (nSortBy
== nSortCount
- 1)
8639 if (nbyC
== 1 && nbyR
> 1)
8640 aSortData
.bByRow
= true;
8641 else if (nbyR
== 1 && nbyC
> 1)
8642 aSortData
.bByRow
= false;
8645 PushIllegalParameter();
8651 pFullMatSortBy
= GetNewMat(aSortData
.bByRow
? (nbyC
* nSortCount
) : nbyC
,
8652 aSortData
.bByRow
? nbyR
: (nbyR
* nSortCount
), /*bEmpty*/true);
8659 PushIllegalParameter();
8663 // ..., penultimate sortby arrays
8664 if (nSortCount
> 1 && nSortBy
<= nSortCount
- 1)
8666 SCSIZE nCheckCol
= 0, nCheckRow
= 0;
8667 pFullMatSortBy
->GetDimensions(nCheckCol
, nCheckRow
);
8668 if ((aSortData
.bByRow
&& nbyR
== nCheckRow
&& nbyC
== 1) ||
8669 (!aSortData
.bByRow
&& nbyC
== nCheckCol
&& nbyR
== 1))
8671 for (SCSIZE ci
= 0; ci
< nbyC
; ci
++)//col
8673 for (SCSIZE rj
= 0; rj
< nbyR
; rj
++)//row
8675 if (pMatSortBy
->IsEmptyCell(ci
, rj
))
8677 if (aSortData
.bByRow
)
8678 pFullMatSortBy
->PutEmpty(ci
+ nSortBy
, rj
);
8680 pFullMatSortBy
->PutEmpty(ci
, rj
+ nSortBy
);
8682 else if (pMatSortBy
->IsStringOrEmpty(ci
, rj
))
8684 if (aSortData
.bByRow
)
8685 pFullMatSortBy
->PutString(pMatSortBy
->GetString(ci
, rj
), ci
+ nSortBy
, rj
);
8687 pFullMatSortBy
->PutString(pMatSortBy
->GetString(ci
, rj
), ci
, rj
+ nSortBy
);
8691 if (aSortData
.bByRow
)
8692 pFullMatSortBy
->PutDouble(pMatSortBy
->GetDouble(ci
, rj
), ci
+ nSortBy
, rj
);
8694 pFullMatSortBy
->PutDouble(pMatSortBy
->GetDouble(ci
, rj
), ci
, rj
+ nSortBy
);
8701 PushIllegalParameter();
8706 aSortData
.maKeyState
[nSortBy
].bDoSort
= true;
8707 aSortData
.maKeyState
[nSortBy
].nField
= nSortBy
;
8712 // 1st argument is the range/array to be sorted
8713 SCSIZE nsC
= 0, nsR
= 0;
8714 SCCOL nSortCol1
= 0, nSortCol2
= 0;
8715 SCROW nSortRow1
= 0, nSortRow2
= 0;
8716 SCTAB nSortTab1
= 0, nSortTab2
= 0;
8717 ScMatrixRef pMatSrc
= nullptr;
8718 switch ( GetStackType() )
8721 PopSingleRef(nSortCol1
, nSortRow1
, nSortTab1
);
8722 nSortCol2
= nSortCol1
;
8723 nSortRow2
= nSortRow1
;
8724 nsC
= nSortCol2
- nSortCol1
+ 1;
8725 nsR
= nSortRow2
- nSortRow1
+ 1;
8729 PopDoubleRef(nSortCol1
, nSortRow1
, nSortTab1
, nSortCol2
, nSortRow2
, nSortTab2
);
8730 if (nSortTab1
!= nSortTab2
)
8732 PushIllegalParameter();
8735 nsC
= nSortCol2
- nSortCol1
+ 1;
8736 nsR
= nSortRow2
- nSortRow1
+ 1;
8740 case svExternalSingleRef
:
8741 case svExternalDoubleRef
:
8743 pMatSrc
= GetMatrix();
8746 PushIllegalParameter();
8749 pMatSrc
->GetDimensions(nsC
, nsR
);
8750 nSortCol2
= nsC
- 1; // nSortCol1 = 0
8751 nSortRow2
= nsR
- 1; // nSortRow1 = 0
8756 PushIllegalParameter();
8760 SCSIZE nCheckMatrixCol
= 0, nCheckMatrixRow
= 0;
8761 pFullMatSortBy
->GetDimensions(nCheckMatrixCol
, nCheckMatrixRow
);
8762 if (nGlobalError
!= FormulaError::NONE
)
8764 PushError(nGlobalError
);
8767 else if ((aSortData
.bByRow
&& nsR
!= nCheckMatrixRow
) ||
8768 (!aSortData
.bByRow
&& nsC
!= nCheckMatrixCol
))
8770 PushIllegalParameter();
8775 aSortData
.nCol2
= nCheckMatrixCol
- 1;
8776 aSortData
.nRow2
= nCheckMatrixRow
- 1;
8780 std::vector
<SCCOLROW
> aOrderIndices
= GetSortOrder(aSortData
, pFullMatSortBy
);
8781 // create sorted matrix
8782 ScMatrixRef pResMat
= CreateSortedMatrix(aSortData
, pMatSrc
,
8783 ScRange(nSortCol1
, nSortRow1
, nSortTab1
, nSortCol2
, nSortRow2
, nSortTab2
),
8784 aOrderIndices
, nsC
, nsR
);
8787 PushMatrix(pResMat
);
8789 PushIllegalParameter();
8792 void ScInterpreter::ScUnique()
8794 sal_uInt8 nParamCount
= GetByte();
8795 if (!MustHaveParamCount(nParamCount
, 1, 3))
8798 // 3rd argument optional - Exactly_once: default FALSE
8799 bool bExactly_once
= false;
8800 if (nParamCount
== 3)
8801 bExactly_once
= GetBoolWithDefault(false);
8803 // 2nd argument optional - default: By_Col = false --> bByRow = true
8805 if (nParamCount
>= 2)
8806 bByRow
= !GetBoolWithDefault(false);
8808 // 1st argument: take unique search range
8809 ScMatrixRef pMatSource
= nullptr;
8810 SCSIZE nsC
= 0, nsR
= 0;
8811 switch (GetStackType())
8816 case svExternalSingleRef
:
8817 case svExternalDoubleRef
:
8819 pMatSource
= GetMatrix();
8822 PushIllegalParameter();
8826 pMatSource
->GetDimensions(nsC
, nsR
);
8831 PushIllegalParameter();
8835 if (nGlobalError
!= FormulaError::NONE
|| nsC
< 1 || nsR
< 1)
8837 PushIllegalArgument();
8841 // Create unique dataset
8842 std::unordered_set
<OUString
> aStrSet
;
8843 std::vector
<std::pair
<SCSIZE
, OUString
>> aResPos
;
8844 SCSIZE nOut
= bByRow
? nsR
: nsC
;
8845 SCSIZE nIn
= bByRow
? nsC
: nsR
;
8847 for (SCSIZE i
= 0; i
< nOut
; i
++)
8850 for (SCSIZE j
= 0; j
< nIn
; j
++)
8852 OUString aCellStr
= bByRow
? pMatSource
->GetString(mrContext
, j
, i
).getString() :
8853 pMatSource
->GetString(mrContext
, i
, j
).getString();
8854 aStr
+= aCellStr
+ u
"\x0001";
8857 if (aStrSet
.insert(aStr
).second
) // unique if inserted
8859 aResPos
.emplace_back(std::make_pair(i
, aStr
));
8865 auto it
= std::find_if(aResPos
.begin(), aResPos
.end(),
8866 [&aStr
](const std::pair
<SCSIZE
, OUString
>& aRes
)
8868 return aRes
.second
.equals(aStr
);
8871 if (it
!= aResPos
.end())
8877 if (aResPos
.size() == 0)
8879 if (nGlobalError
!= FormulaError::NONE
)
8881 PushIllegalArgument();
8889 // fill result matrix with unique values
8890 ScMatrixRef pResMat
= bByRow
? GetNewMat(nsC
, aResPos
.size(), /*bEmpty*/true) :
8891 GetNewMat(aResPos
.size(), nsR
, /*bEmpty*/true);
8892 for (SCSIZE iPos
= 0; iPos
< aResPos
.size(); iPos
++)
8896 for (SCSIZE col
= 0; col
< nsC
; col
++)
8898 if (pMatSource
->IsEmptyCell(col
, aResPos
[iPos
].first
))
8900 pResMat
->PutEmpty(col
, iPos
);
8902 else if (!pMatSource
->IsStringOrEmpty(col
, aResPos
[iPos
].first
))
8904 pResMat
->PutDouble(pMatSource
->GetDouble(col
, aResPos
[iPos
].first
), col
, iPos
);
8908 pResMat
->PutString(pMatSource
->GetString(col
, aResPos
[iPos
].first
), col
, iPos
);
8914 for (SCSIZE row
= 0; row
< nsR
; row
++)
8916 if (pMatSource
->IsEmptyCell(aResPos
[iPos
].first
, row
))
8918 pResMat
->PutEmpty(iPos
, row
);
8920 else if (!pMatSource
->IsStringOrEmpty(aResPos
[iPos
].first
, row
))
8922 pResMat
->PutDouble(pMatSource
->GetDouble(aResPos
[iPos
].first
, row
), iPos
, row
);
8926 pResMat
->PutString(pMatSource
->GetString(aResPos
[iPos
].first
, row
), iPos
, row
);
8934 PushIllegalArgument();
8938 PushMatrix(pResMat
);
8942 void ScInterpreter::getTokensAtParameter( std::unique_ptr
<ScTokenArray
>& pTokens
, short nPos
)
8944 sal_uInt16 nOpen
= 0;
8945 sal_uInt16 nSepCount
= 0;
8946 formula::FormulaTokenArrayPlainIterator
aIter(*pArr
);
8947 formula::FormulaToken
* t
= aIter
.First();
8948 for (t
= aIter
.NextNoSpaces(); t
; t
= aIter
.NextNoSpaces())
8950 OpCode aOpCode
= t
->GetOpCode();
8951 formula::StackVar aIntType
= t
->GetType();
8952 if ((aOpCode
== ocOpen
|| aOpCode
== ocArrayOpen
|| aOpCode
== ocTableRefOpen
) && aIntType
== formula::StackVar::svSep
)
8954 else if ((aOpCode
== ocClose
|| aOpCode
== ocArrayClose
|| aOpCode
== ocTableRefClose
) && aIntType
== formula::StackVar::svSep
)
8956 else if (aOpCode
== ocSep
&& aIntType
== formula::StackVar::svSep
&& nOpen
== 1)
8962 if (nSepCount
== nPos
&& nOpen
> 0)
8964 pTokens
->AddToken(*t
->Clone());
8969 void ScInterpreter::replaceNamesToResult( const std::unordered_map
<OUString
, formula::FormulaToken
*> nResultIndexes
,
8970 std::unique_ptr
<ScTokenArray
>& pTokens
)
8972 formula::FormulaTokenArrayPlainIterator
aIterResult(*pTokens
);
8973 for (FormulaToken
* t
= aIterResult
.GetNextStringName(); t
; t
= aIterResult
.GetNextStringName())
8975 auto iRes
= nResultIndexes
.find(t
->GetString().getString());
8976 if (iRes
!= nResultIndexes
.end())
8977 pTokens
->ReplaceToken(aIterResult
.GetIndex() - 1, iRes
->second
->Clone(),
8978 FormulaTokenArray::ReplaceMode::CODE_ONLY
);
8982 void ScInterpreter::ScLet()
8984 const short* pJump
= pCur
->GetJump();
8985 short nJumpCount
= pJump
[0];
8986 short nOrgJumpCount
= nJumpCount
;
8988 if (nJumpCount
< 3 || (nJumpCount
% 2 != 1))
8990 PushError(FormulaError::ParameterExpected
);
8991 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
8996 std::unordered_map
<OUString
, formula::FormulaToken
*> nResultIndexes
;
8997 formula::FormulaTokenArrayPlainIterator
aIter(*pArr
);
8998 unique_ptr
<ScTokenArray
> pValueTokens(new ScTokenArray(mrDoc
));
9000 // name and function pairs parameter
9001 while (nJumpCount
> 1)
9003 if (nJumpCount
== nOrgJumpCount
)
9005 aStrName
= GetString().getString();
9007 else if ((nOrgJumpCount
- nJumpCount
+ 1) % 2 == 1)
9009 aIter
.Jump(pJump
[static_cast<short>(nOrgJumpCount
- nJumpCount
+ 1)] - 1);
9010 FormulaToken
* t
= aIter
.NextRPN();
9011 aStrName
= t
->GetString().getString();
9015 PushError(FormulaError::ParameterExpected
);
9016 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9022 getTokensAtParameter(pValueTokens
, nOrgJumpCount
- nJumpCount
);
9025 // replace names with result tokens
9026 replaceNamesToResult(nResultIndexes
, pValueTokens
);
9028 // calculate the inner results unless we already have a push result token
9029 if (pValueTokens
->GetLen() == 1 && pValueTokens
->GetArray()[0]->GetOpCode() == ocPush
)
9031 if (!nResultIndexes
.insert(std::make_pair(aStrName
, pValueTokens
->GetArray()[0]->Clone())).second
)
9033 PushIllegalParameter();
9034 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9040 ScCompiler
aComp(mrDoc
, aPos
, *pValueTokens
, mrDoc
.GetGrammar(), false, false, &mrContext
);
9041 aComp
.CompileTokenArray();
9042 ScInterpreter
aInt(mrDoc
.GetFormulaCell(aPos
), mrDoc
, mrContext
, aPos
, *pValueTokens
);
9043 sfx2::LinkManager
aNewLinkMgr(mrDoc
.GetDocumentShell());
9044 aInt
.SetLinkManager(&aNewLinkMgr
);
9045 formula::StackVar aIntType
= aInt
.Interpret();
9047 if (aIntType
== formula::svMatrixCell
)
9049 ScConstMatrixRef
xMat(aInt
.GetResultToken()->GetMatrix());
9050 if (!nResultIndexes
.insert(std::make_pair(aStrName
, new ScMatrixToken(xMat
->Clone()))).second
)
9052 PushIllegalParameter();
9053 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9059 FormulaConstTokenRef
xTok(aInt
.GetResultToken());
9060 if (!nResultIndexes
.insert(std::make_pair(aStrName
, xTok
->Clone())).second
)
9062 PushIllegalParameter();
9063 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9068 pValueTokens
->Clear();
9071 // last parameter: calculation
9072 getTokensAtParameter(pValueTokens
, nOrgJumpCount
- nJumpCount
);
9075 // replace names with result tokens
9076 replaceNamesToResult(nResultIndexes
, pValueTokens
);
9078 // calculate the final result
9079 ScCompiler
aComp(mrDoc
, aPos
, *pValueTokens
, mrDoc
.GetGrammar(), false, false, &mrContext
);
9080 aComp
.CompileTokenArray();
9081 ScInterpreter
aInt(mrDoc
.GetFormulaCell(aPos
), mrDoc
, mrContext
, aPos
, *pValueTokens
);
9082 sfx2::LinkManager
aNewLinkMgr(mrDoc
.GetDocumentShell());
9083 aInt
.SetLinkManager(&aNewLinkMgr
);
9084 formula::StackVar aIntType
= aInt
.Interpret();
9086 if (aIntType
== formula::svMatrixCell
)
9088 ScConstMatrixRef
xMat(aInt
.GetResultToken()->GetMatrix());
9089 PushTokenRef(new ScMatrixToken(xMat
->Clone()));
9093 formula::FormulaConstTokenRef
xLambdaResult(aInt
.GetResultToken());
9096 nGlobalError
= xLambdaResult
->GetError();
9097 if (nGlobalError
== FormulaError::NONE
)
9098 PushTokenRef(xLambdaResult
);
9100 PushError(nGlobalError
);
9104 pValueTokens
.reset();
9105 aCode
.Jump(pJump
[nOrgJumpCount
], pJump
[nOrgJumpCount
]);
9108 void ScInterpreter::ScSubTotal()
9110 sal_uInt8 nParamCount
= GetByte();
9111 if ( !MustHaveParamCountMinWithStackCheck( nParamCount
, 2 ) )
9114 // We must fish the 1st parameter deep from the stack! And push it on top.
9115 const FormulaToken
* p
= pStack
[ sp
- nParamCount
];
9116 PushWithoutError( *p
);
9117 sal_Int32 nFunc
= GetInt32();
9118 mnSubTotalFlags
|= SubtotalFlags::IgnoreNestedStAg
| SubtotalFlags::IgnoreFiltered
;
9121 // For opcodes 101 through 111, we need to skip hidden cells.
9122 // Other than that these opcodes are identical to 1 through 11.
9123 mnSubTotalFlags
|= SubtotalFlags::IgnoreHidden
;
9127 if ( nGlobalError
!= FormulaError::NONE
|| nFunc
< 1 || nFunc
> 11 )
9128 PushIllegalArgument(); // simulate return on stack, not SetError(...)
9131 cPar
= nParamCount
- 1;
9134 case SUBTOTAL_FUNC_AVE
: ScAverage(); break;
9135 case SUBTOTAL_FUNC_CNT
: ScCount(); break;
9136 case SUBTOTAL_FUNC_CNT2
: ScCount2(); break;
9137 case SUBTOTAL_FUNC_MAX
: ScMax(); break;
9138 case SUBTOTAL_FUNC_MIN
: ScMin(); break;
9139 case SUBTOTAL_FUNC_PROD
: ScProduct(); break;
9140 case SUBTOTAL_FUNC_STD
: ScStDev(); break;
9141 case SUBTOTAL_FUNC_STDP
: ScStDevP(); break;
9142 case SUBTOTAL_FUNC_SUM
: ScSum(); break;
9143 case SUBTOTAL_FUNC_VAR
: ScVar(); break;
9144 case SUBTOTAL_FUNC_VARP
: ScVarP(); break;
9145 default : PushIllegalArgument(); break;
9148 mnSubTotalFlags
= SubtotalFlags::NONE
;
9149 // Get rid of the 1st (fished) parameter.
9150 FormulaConstTokenRef
xRef( PopToken());
9152 PushTokenRef( xRef
);
9155 void ScInterpreter::ScAggregate()
9157 sal_uInt8 nParamCount
= GetByte();
9158 if ( !MustHaveParamCountMinWithStackCheck( nParamCount
, 3 ) )
9161 const FormulaError nErr
= nGlobalError
;
9162 nGlobalError
= FormulaError::NONE
;
9164 // fish the 1st parameter from the stack and push it on top.
9165 const FormulaToken
* p
= pStack
[ sp
- nParamCount
];
9166 PushWithoutError( *p
);
9167 sal_Int32 nFunc
= GetInt32();
9168 // fish the 2nd parameter from the stack and push it on top.
9169 const FormulaToken
* p2
= pStack
[ sp
- ( nParamCount
- 1 ) ];
9170 PushWithoutError( *p2
);
9171 sal_Int32 nOption
= GetInt32();
9173 if ( nGlobalError
!= FormulaError::NONE
|| nFunc
< 1 || nFunc
> 19 )
9175 nGlobalError
= nErr
;
9176 PushIllegalArgument();
9182 case 0 : // ignore nested SUBTOTAL and AGGREGATE functions
9183 mnSubTotalFlags
= SubtotalFlags::IgnoreNestedStAg
;
9185 case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions
9186 mnSubTotalFlags
= SubtotalFlags::IgnoreHidden
| SubtotalFlags::IgnoreNestedStAg
;
9188 case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions
9189 mnSubTotalFlags
= SubtotalFlags::IgnoreErrVal
| SubtotalFlags::IgnoreNestedStAg
;
9191 case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions
9192 mnSubTotalFlags
= SubtotalFlags::IgnoreHidden
| SubtotalFlags::IgnoreErrVal
| SubtotalFlags::IgnoreNestedStAg
;
9194 case 4 : // ignore nothing
9195 mnSubTotalFlags
= SubtotalFlags::NONE
;
9197 case 5 : // ignore hidden rows
9198 mnSubTotalFlags
= SubtotalFlags::IgnoreHidden
;
9200 case 6 : // ignore error values
9201 mnSubTotalFlags
= SubtotalFlags::IgnoreErrVal
;
9203 case 7 : // ignore hidden rows and error values
9204 mnSubTotalFlags
= SubtotalFlags::IgnoreHidden
| SubtotalFlags::IgnoreErrVal
;
9207 nGlobalError
= nErr
;
9208 PushIllegalArgument();
9212 if ((mnSubTotalFlags
& SubtotalFlags::IgnoreErrVal
) == SubtotalFlags::NONE
)
9213 nGlobalError
= nErr
;
9215 cPar
= nParamCount
- 2;
9218 case AGGREGATE_FUNC_AVE
: ScAverage(); break;
9219 case AGGREGATE_FUNC_CNT
: ScCount(); break;
9220 case AGGREGATE_FUNC_CNT2
: ScCount2(); break;
9221 case AGGREGATE_FUNC_MAX
: ScMax(); break;
9222 case AGGREGATE_FUNC_MIN
: ScMin(); break;
9223 case AGGREGATE_FUNC_PROD
: ScProduct(); break;
9224 case AGGREGATE_FUNC_STD
: ScStDev(); break;
9225 case AGGREGATE_FUNC_STDP
: ScStDevP(); break;
9226 case AGGREGATE_FUNC_SUM
: ScSum(); break;
9227 case AGGREGATE_FUNC_VAR
: ScVar(); break;
9228 case AGGREGATE_FUNC_VARP
: ScVarP(); break;
9229 case AGGREGATE_FUNC_MEDIAN
: ScMedian(); break;
9230 case AGGREGATE_FUNC_MODSNGL
: ScModalValue(); break;
9231 case AGGREGATE_FUNC_LARGE
: ScLarge(); break;
9232 case AGGREGATE_FUNC_SMALL
: ScSmall(); break;
9233 case AGGREGATE_FUNC_PERCINC
: ScPercentile( true ); break;
9234 case AGGREGATE_FUNC_QRTINC
: ScQuartile( true ); break;
9235 case AGGREGATE_FUNC_PERCEXC
: ScPercentile( false ); break;
9236 case AGGREGATE_FUNC_QRTEXC
: ScQuartile( false ); break;
9238 nGlobalError
= nErr
;
9239 PushIllegalArgument();
9242 mnSubTotalFlags
= SubtotalFlags::NONE
;
9244 FormulaConstTokenRef
xRef( PopToken());
9245 // Get rid of the 1st and 2nd (fished) parameters.
9248 PushTokenRef( xRef
);
9251 std::unique_ptr
<ScDBQueryParamBase
> ScInterpreter::GetDBParams( bool& rMissingField
)
9253 bool bAllowMissingField
= false;
9254 if ( rMissingField
)
9256 bAllowMissingField
= true;
9257 rMissingField
= false;
9259 if ( GetByte() == 3 )
9261 // First, get the query criteria range.
9262 ::std::unique_ptr
<ScDBRangeBase
> pQueryRef( PopDBDoubleRef() );
9268 svl::SharedString aStr
;
9269 ScRange aMissingRange
;
9270 bool bRangeFake
= false;
9271 switch (GetStackType())
9274 nVal
= ::rtl::math::approxFloor( GetDouble() );
9275 if ( bAllowMissingField
&& nVal
== 0.0 )
9276 rMissingField
= true; // fake missing parameter
9285 PopSingleRef( aAdr
);
9286 ScRefCellValue
aCell(mrDoc
, aAdr
);
9287 if (aCell
.hasNumeric())
9288 nVal
= GetCellValue(aAdr
, aCell
);
9292 GetCellString(aStr
, aCell
);
9297 if ( bAllowMissingField
)
9298 { // fake missing parameter for old SO compatibility
9300 PopDoubleRef( aMissingRange
);
9305 SetError( FormulaError::IllegalParameter
);
9310 if ( bAllowMissingField
)
9311 rMissingField
= true;
9313 SetError( FormulaError::IllegalParameter
);
9317 SetError( FormulaError::IllegalParameter
);
9320 if (nGlobalError
!= FormulaError::NONE
)
9323 unique_ptr
<ScDBRangeBase
> pDBRef( PopDBDoubleRef() );
9325 if (nGlobalError
!= FormulaError::NONE
|| !pDBRef
)
9330 // range parameter must match entire database range
9331 if (pDBRef
->isRangeEqual(aMissingRange
))
9332 rMissingField
= true;
9334 SetError( FormulaError::IllegalParameter
);
9337 if (nGlobalError
!= FormulaError::NONE
)
9340 SCCOL nField
= pDBRef
->getFirstFieldColumn();
9344 nField
= pDBRef
->findFieldColumn(static_cast<SCCOL
>(nVal
));
9347 FormulaError nErr
= FormulaError::NONE
;
9348 nField
= pDBRef
->findFieldColumn(aStr
.getString(), &nErr
);
9352 if (!mrDoc
.ValidCol(nField
))
9355 unique_ptr
<ScDBQueryParamBase
> pParam( pDBRef
->createQueryParam(pQueryRef
.get()) );
9359 // An allowed missing field parameter sets the result field
9360 // to any of the query fields, just to be able to return
9361 // some cell from the iterator.
9362 if ( rMissingField
)
9363 nField
= static_cast<SCCOL
>(pParam
->GetEntry(0).nField
);
9364 pParam
->mnField
= nField
;
9366 SCSIZE nCount
= pParam
->GetEntryCount();
9367 for ( SCSIZE i
=0; i
< nCount
; i
++ )
9369 ScQueryEntry
& rEntry
= pParam
->GetEntry(i
);
9370 if (!rEntry
.bDoQuery
)
9373 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
9374 sal_uInt32 nIndex
= 0;
9375 OUString aQueryStr
= rItem
.maString
.getString();
9376 bool bNumber
= mrContext
.NFIsNumberFormat(
9377 aQueryStr
, nIndex
, rItem
.mfVal
);
9378 rItem
.meType
= bNumber
? ScQueryEntry::ByValue
: ScQueryEntry::ByString
;
9380 if (!bNumber
&& pParam
->eSearchType
== utl::SearchParam::SearchType::Normal
)
9381 pParam
->eSearchType
= DetectSearchType(aQueryStr
, mrDoc
);
9389 void ScInterpreter::DBIterator( ScIterFunc eFunc
)
9393 sal_uLong nCount
= 0;
9394 bool bMissingField
= false;
9395 unique_ptr
<ScDBQueryParamBase
> pQueryParam( GetDBParams(bMissingField
) );
9398 if (!pQueryParam
->IsValidFieldIndex())
9400 SetError(FormulaError::NoValue
);
9403 ScDBQueryDataIterator
aValIter(mrDoc
, mrContext
, std::move(pQueryParam
));
9404 ScDBQueryDataIterator::Value aValue
;
9405 if ( aValIter
.GetFirst(aValue
) && aValue
.mnError
== FormulaError::NONE
)
9409 case ifPRODUCT
: fRes
= 1; break;
9410 case ifMAX
: fRes
= -MAXDOUBLE
; break;
9411 case ifMIN
: fRes
= MAXDOUBLE
; break;
9412 default: ; // nothing
9422 fErg
+= aValue
.mfValue
;
9425 fErg
+= aValue
.mfValue
* aValue
.mfValue
;
9428 fRes
*= aValue
.mfValue
;
9431 if( aValue
.mfValue
> fRes
) fRes
= aValue
.mfValue
;
9434 if( aValue
.mfValue
< fRes
) fRes
= aValue
.mfValue
;
9436 default: ; // nothing
9439 while ( aValIter
.GetNext(aValue
) && aValue
.mnError
== FormulaError::NONE
);
9441 SetError(aValue
.mnError
);
9444 SetError( FormulaError::IllegalParameter
);
9447 case ifCOUNT
: fRes
= nCount
; break;
9448 case ifSUM
: fRes
= fErg
.get(); break;
9449 case ifSUMSQ
: fRes
= fErg
.get(); break;
9450 case ifAVERAGE
: fRes
= div(fErg
.get(), nCount
); break;
9451 default: ; // nothing
9456 void ScInterpreter::ScDBSum()
9458 DBIterator( ifSUM
);
9461 void ScInterpreter::ScDBCount()
9463 bool bMissingField
= true;
9464 unique_ptr
<ScDBQueryParamBase
> pQueryParam( GetDBParams(bMissingField
) );
9467 sal_uLong nCount
= 0;
9468 if ( bMissingField
&& pQueryParam
->GetType() == ScDBQueryParamBase::INTERNAL
)
9469 { // count all matching records
9470 // TODO: currently the QueryIterators only return cell pointers of
9471 // existing cells, so if a query matches an empty cell there's
9472 // nothing returned, and therefore not counted!
9473 // Since this has ever been the case and this code here only came
9474 // into existence to fix #i6899 and it never worked before we'll
9475 // have to live with it until we reimplement the iterators to also
9476 // return empty cells, which would mean to adapt all callers of
9478 ScDBQueryParamInternal
* p
= static_cast<ScDBQueryParamInternal
*>(pQueryParam
.get());
9479 p
->nCol2
= p
->nCol1
; // Don't forget to select only one column.
9480 SCTAB nTab
= p
->nTab
;
9481 // ScQueryCellIteratorDirect doesn't make use of ScDBQueryParamBase::mnField,
9482 // so the source range has to be restricted, like before the introduction
9483 // of ScDBQueryParamBase.
9484 p
->nCol1
= p
->nCol2
= p
->mnField
;
9485 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, nTab
, *p
, true, false);
9486 if ( aCellIter
.GetFirst() )
9491 } while ( aCellIter
.GetNext() );
9495 { // count only matching records with a value in the "result" field
9496 if (!pQueryParam
->IsValidFieldIndex())
9498 SetError(FormulaError::NoValue
);
9501 ScDBQueryDataIterator
aValIter(mrDoc
, mrContext
, std::move(pQueryParam
));
9502 ScDBQueryDataIterator::Value aValue
;
9503 if ( aValIter
.GetFirst(aValue
) && aValue
.mnError
== FormulaError::NONE
)
9509 while ( aValIter
.GetNext(aValue
) && aValue
.mnError
== FormulaError::NONE
);
9511 SetError(aValue
.mnError
);
9513 PushDouble( nCount
);
9516 PushIllegalParameter();
9519 void ScInterpreter::ScDBCount2()
9521 bool bMissingField
= true;
9522 unique_ptr
<ScDBQueryParamBase
> pQueryParam( GetDBParams(bMissingField
) );
9525 if (!pQueryParam
->IsValidFieldIndex())
9527 SetError(FormulaError::NoValue
);
9530 sal_uLong nCount
= 0;
9531 pQueryParam
->mbSkipString
= false;
9532 ScDBQueryDataIterator
aValIter(mrDoc
, mrContext
, std::move(pQueryParam
));
9533 ScDBQueryDataIterator::Value aValue
;
9534 if ( aValIter
.GetFirst(aValue
) && aValue
.mnError
== FormulaError::NONE
)
9540 while ( aValIter
.GetNext(aValue
) && aValue
.mnError
== FormulaError::NONE
);
9542 SetError(aValue
.mnError
);
9543 PushDouble( nCount
);
9546 PushIllegalParameter();
9549 void ScInterpreter::ScDBAverage()
9551 DBIterator( ifAVERAGE
);
9554 void ScInterpreter::ScDBMax()
9556 DBIterator( ifMAX
);
9559 void ScInterpreter::ScDBMin()
9561 DBIterator( ifMIN
);
9564 void ScInterpreter::ScDBProduct()
9566 DBIterator( ifPRODUCT
);
9569 void ScInterpreter::GetDBStVarParams( double& rVal
, double& rValCount
)
9571 std::vector
<double> values
;
9572 KahanSum vSum
= 0.0;
9573 KahanSum fSum
= 0.0;
9576 bool bMissingField
= false;
9577 unique_ptr
<ScDBQueryParamBase
> pQueryParam( GetDBParams(bMissingField
) );
9580 if (!pQueryParam
->IsValidFieldIndex())
9582 SetError(FormulaError::NoValue
);
9585 ScDBQueryDataIterator
aValIter(mrDoc
, mrContext
, std::move(pQueryParam
));
9586 ScDBQueryDataIterator::Value aValue
;
9587 if (aValIter
.GetFirst(aValue
) && aValue
.mnError
== FormulaError::NONE
)
9592 values
.push_back(aValue
.mfValue
);
9593 fSum
+= aValue
.mfValue
;
9595 while ((aValue
.mnError
== FormulaError::NONE
) && aValIter
.GetNext(aValue
));
9597 SetError(aValue
.mnError
);
9600 SetError( FormulaError::IllegalParameter
);
9602 double vMean
= fSum
.get() / values
.size();
9604 for (double v
: values
)
9605 vSum
+= (v
- vMean
) * (v
- vMean
);
9610 void ScInterpreter::ScDBStdDev()
9612 double fVal
, fCount
;
9613 GetDBStVarParams( fVal
, fCount
);
9614 PushDouble( sqrt(fVal
/(fCount
-1)));
9617 void ScInterpreter::ScDBStdDevP()
9619 double fVal
, fCount
;
9620 GetDBStVarParams( fVal
, fCount
);
9621 PushDouble( sqrt(fVal
/fCount
));
9624 void ScInterpreter::ScDBVar()
9626 double fVal
, fCount
;
9627 GetDBStVarParams( fVal
, fCount
);
9628 PushDouble(fVal
/(fCount
-1));
9631 void ScInterpreter::ScDBVarP()
9633 double fVal
, fCount
;
9634 GetDBStVarParams( fVal
, fCount
);
9635 PushDouble(fVal
/fCount
);
9638 static bool lcl_IsTableStructuredRef(const OUString
& sRefStr
, sal_Int32
& nIndex
)
9640 nIndex
= ScGlobal::FindUnquoted(sRefStr
, '[');
9641 return (nIndex
> 0 && ScGlobal::FindUnquoted(sRefStr
, ']', nIndex
+ 1) > nIndex
);
9644 void ScInterpreter::ScIndirect()
9646 sal_uInt8 nParamCount
= GetByte();
9647 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
9650 // Reference address syntax for INDIRECT is configurable.
9651 FormulaGrammar::AddressConvention eConv
= maCalcConfig
.meStringRefAddressSyntax
;
9652 if (eConv
== FormulaGrammar::CONV_UNSPECIFIED
)
9653 // Use the current address syntax if unspecified.
9654 eConv
= mrDoc
.GetAddressConvention();
9656 // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible
9657 // to determine which syntax to use during doc import
9658 bool bTryXlA1
= (eConv
== FormulaGrammar::CONV_A1_XL_A1
);
9660 if (nParamCount
== 2 && 0.0 == GetDouble() )
9662 // Overwrite the config and try Excel R1C1.
9663 eConv
= FormulaGrammar::CONV_XL_R1C1
;
9667 svl::SharedString sSharedRefStr
= GetString();
9668 const OUString
& sRefStr
= sSharedRefStr
.getString();
9669 if (sRefStr
.isEmpty())
9671 // Bail out early for empty cells, rely on "we do have a string" below.
9672 PushError( FormulaError::NoRef
);
9676 const ScAddress::Details
aDetails( bTryXlA1
? FormulaGrammar::CONV_OOO
: eConv
, aPos
);
9677 const ScAddress::Details
aDetailsXlA1( FormulaGrammar::CONV_XL_A1
, aPos
);
9678 SCTAB nTab
= aPos
.Tab();
9680 bool bTableRefNamed
= false;
9681 sal_Int32 nTableRefNamedIndex
= -1;
9682 OUString sTabRefStr
;
9684 // Named expressions and DB range names need to be tried first, as older 1K
9685 // columns allowed names that would now match a 16k columns cell address.
9688 ScRangeData
* pData
= ScRangeStringConverter::GetRangeDataFromString( sRefStr
, nTab
, mrDoc
, eConv
);
9692 // We need this in order to obtain a good range.
9693 pData
->ValidateTabRefs();
9697 // This is the usual way to treat named ranges containing
9698 // relative references.
9699 if (!pData
->IsReference(aRange
, aPos
))
9701 sTabRefStr
= pData
->GetSymbol();
9702 bTableRefNamed
= lcl_IsTableStructuredRef(sTabRefStr
, nTableRefNamedIndex
);
9703 // if bTableRefNamed is true, we have a name that maps to a table structured reference.
9704 // Such a case is handled below.
9708 if (aRange
.aStart
== aRange
.aEnd
)
9709 PushSingleRef( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
9710 aRange
.aStart
.Tab());
9712 PushDoubleRef( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
9713 aRange
.aStart
.Tab(), aRange
.aEnd
.Col(),
9714 aRange
.aEnd
.Row(), aRange
.aEnd
.Tab());
9726 const OUString
& aName( sSharedRefStr
.getIgnoreCaseString() );
9727 ScDBCollection::NamedDBs
& rDBs
= mrDoc
.GetDBCollection()->getNamedDBs();
9728 const ScDBData
* pData
= rDBs
.findByUpperName( aName
);
9733 pData
->GetArea( aRange
);
9735 // In Excel, specifying a table name without [] resolves to the
9736 // same as with [], a range that excludes header and totals
9737 // rows and contains only data rows. Do the same.
9738 if (pData
->HasHeader())
9739 aRange
.aStart
.IncRow();
9740 if (pData
->HasTotals())
9741 aRange
.aEnd
.IncRow(-1);
9743 if (aRange
.aStart
.Row() > aRange
.aEnd
.Row())
9746 if (aRange
.aStart
== aRange
.aEnd
)
9747 PushSingleRef( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
9748 aRange
.aStart
.Tab());
9750 PushDoubleRef( aRange
.aStart
.Col(), aRange
.aStart
.Row(),
9751 aRange
.aStart
.Tab(), aRange
.aEnd
.Col(),
9752 aRange
.aEnd
.Row(), aRange
.aEnd
.Tab());
9759 ScRefAddress aRefAd
, aRefAd2
;
9760 ScAddress::ExternalInfo aExtInfo
;
9761 if ( !bTableRefNamed
&&
9762 (ConvertDoubleRef(mrDoc
, sRefStr
, nTab
, aRefAd
, aRefAd2
, aDetails
, &aExtInfo
) ||
9763 ( bTryXlA1
&& ConvertDoubleRef(mrDoc
, sRefStr
, nTab
, aRefAd
,
9764 aRefAd2
, aDetailsXlA1
, &aExtInfo
) ) ) )
9766 if (aExtInfo
.mbExternal
)
9768 PushExternalDoubleRef(
9769 aExtInfo
.mnFileId
, aExtInfo
.maTabName
,
9770 aRefAd
.Col(), aRefAd
.Row(), aRefAd
.Tab(),
9771 aRefAd2
.Col(), aRefAd2
.Row(), aRefAd2
.Tab());
9774 PushDoubleRef( aRefAd
, aRefAd2
);
9776 else if ( !bTableRefNamed
&&
9777 (ConvertSingleRef(mrDoc
, sRefStr
, nTab
, aRefAd
, aDetails
, &aExtInfo
) ||
9778 ( bTryXlA1
&& ConvertSingleRef (mrDoc
, sRefStr
, nTab
, aRefAd
,
9779 aDetailsXlA1
, &aExtInfo
) ) ) )
9781 if (aExtInfo
.mbExternal
)
9783 PushExternalSingleRef(
9784 aExtInfo
.mnFileId
, aExtInfo
.maTabName
, aRefAd
.Col(), aRefAd
.Row(), aRefAd
.Tab());
9787 PushSingleRef( aRefAd
);
9791 // It may be even a TableRef or an external name.
9792 // Anything else that resolves to one reference could be added
9793 // here, but we don't want to compile every arbitrary string. This
9794 // is already nasty enough...
9795 sal_Int32 nIndex
= bTableRefNamed
? nTableRefNamedIndex
: -1;
9796 bool bTableRef
= bTableRefNamed
;
9797 if (!bTableRefNamed
)
9798 bTableRef
= lcl_IsTableStructuredRef(sRefStr
, nIndex
);
9799 bool bExternalName
= false; // External references would had been consumed above already.
9802 // This is our own file name reference representation centric.. but
9803 // would work also for XL '[doc]'!name and also for
9804 // '[doc]Sheet1'!name ... sickos.
9805 if (sRefStr
[0] == '\'')
9807 // Minimum 'a'#name or 'a'!name
9808 // bTryXlA1 means try both, first our own.
9809 if (bTryXlA1
|| eConv
== FormulaGrammar::CONV_OOO
)
9811 nIndex
= ScGlobal::FindUnquoted( sRefStr
, '#');
9812 if (nIndex
>= 3 && sRefStr
[nIndex
-1] == '\'')
9814 bExternalName
= true;
9815 eConv
= FormulaGrammar::CONV_OOO
;
9818 if (!bExternalName
&& (bTryXlA1
|| eConv
!= FormulaGrammar::CONV_OOO
))
9820 nIndex
= ScGlobal::FindUnquoted( sRefStr
, '!');
9821 if (nIndex
>= 3 && sRefStr
[nIndex
-1] == '\'')
9823 bExternalName
= true;
9829 if (bExternalName
|| bTableRef
)
9833 ScCompiler
aComp( mrDoc
, aPos
, mrDoc
.GetGrammar());
9834 aComp
.SetRefConvention( eConv
); // must be after grammar
9835 std::unique_ptr
<ScTokenArray
> pTokArr( aComp
.CompileString(bTableRefNamed
? sTabRefStr
: sRefStr
));
9837 if (pTokArr
->GetCodeError() != FormulaError::NONE
|| !pTokArr
->GetLen())
9840 // Whatever... use only the specific case.
9843 const formula::FormulaToken
* pTok
= pTokArr
->FirstToken();
9844 if (!pTok
|| pTok
->GetType() != svExternalName
)
9847 else if (!pTokArr
->HasOpCode( ocTableRef
))
9850 aComp
.CompileTokenArray();
9852 // A syntactically valid reference will generate exactly
9853 // one RPN token, a reference or error. Discard everything
9855 if (pTokArr
->GetCodeLen() != 1)
9858 ScTokenRef
xTok( pTokArr
->FirstRPNToken());
9862 switch (xTok
->GetType())
9866 case svExternalSingleRef
:
9867 case svExternalDoubleRef
:
9869 PushTokenRef( xTok
);
9879 PushError( FormulaError::NoRef
);
9883 void ScInterpreter::ScAddressFunc()
9887 sal_uInt8 nParamCount
= GetByte();
9888 if( !MustHaveParamCount( nParamCount
, 2, 5 ) )
9891 if( nParamCount
>= 5 )
9892 sTabStr
= GetString().getString();
9894 FormulaGrammar::AddressConvention eConv
= FormulaGrammar::CONV_OOO
; // default
9895 if (nParamCount
>= 4 && 0.0 == GetDoubleWithDefault( 1.0))
9896 eConv
= FormulaGrammar::CONV_XL_R1C1
;
9899 // If A1 syntax is requested then the actual sheet separator and format
9900 // convention depends on the syntax configured for INDIRECT to match
9901 // that, and if it is unspecified then the document's address syntax.
9902 FormulaGrammar::AddressConvention eForceConv
= maCalcConfig
.meStringRefAddressSyntax
;
9903 if (eForceConv
== FormulaGrammar::CONV_UNSPECIFIED
)
9904 eForceConv
= mrDoc
.GetAddressConvention();
9905 if (eForceConv
== FormulaGrammar::CONV_XL_A1
|| eForceConv
== FormulaGrammar::CONV_XL_R1C1
)
9906 eConv
= FormulaGrammar::CONV_XL_A1
; // for anything Excel use Excel A1
9909 ScRefFlags nFlags
= ScRefFlags::COL_ABS
| ScRefFlags::ROW_ABS
; // default
9910 if( nParamCount
>= 3 )
9912 sal_Int32 n
= GetInt32WithDefault(1);
9920 case 1 : break; // default
9922 case 2 : nFlags
= ScRefFlags::ROW_ABS
; break;
9924 case 3 : nFlags
= ScRefFlags::COL_ABS
; break;
9926 case 4 : nFlags
= ScRefFlags::ZERO
; break; // both relative
9929 nFlags
|= ScRefFlags::VALID
| ScRefFlags::ROW_VALID
| ScRefFlags::COL_VALID
;
9931 SCCOL nCol
= static_cast<SCCOL
>(GetInt16());
9932 SCROW nRow
= static_cast<SCROW
>(GetInt32());
9933 if( eConv
== FormulaGrammar::CONV_XL_R1C1
)
9935 // YUCK! The XL interface actually treats rel R1C1 refs differently
9937 if( !(nFlags
& ScRefFlags::COL_ABS
) )
9938 nCol
+= aPos
.Col() + 1;
9939 if( !(nFlags
& ScRefFlags::ROW_ABS
) )
9940 nRow
+= aPos
.Row() + 1;
9945 if (nGlobalError
!= FormulaError::NONE
|| !mrDoc
.ValidCol( nCol
) || !mrDoc
.ValidRow( nRow
))
9947 PushIllegalArgument();
9951 const ScAddress::Details
aDetails( eConv
, aPos
);
9952 const ScAddress
aAdr( nCol
, nRow
, 0);
9953 OUString
aRefStr(aAdr
.Format(nFlags
, &mrDoc
, aDetails
));
9955 if( nParamCount
>= 5 && !sTabStr
.isEmpty() )
9958 if (eConv
== FormulaGrammar::CONV_OOO
)
9960 // Isolate Tab from 'Doc'#Tab
9961 sal_Int32 nPos
= ScCompiler::GetDocTabPos( sTabStr
);
9964 if (sTabStr
[nPos
+1] == '$')
9965 ++nPos
; // also split 'Doc'#$Tab
9966 aDoc
= sTabStr
.copy( 0, nPos
+1);
9967 sTabStr
= sTabStr
.copy( nPos
+1);
9970 /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
9971 * need some extra handling to isolate Tab from Doc. */
9972 if (sTabStr
[0] != '\'' || !sTabStr
.endsWith("'"))
9973 ScCompiler::CheckTabQuotes( sTabStr
, eConv
);
9974 if (!aDoc
.isEmpty())
9975 sTabStr
= aDoc
+ sTabStr
;
9976 sTabStr
+= (eConv
== FormulaGrammar::CONV_XL_R1C1
|| eConv
== FormulaGrammar::CONV_XL_A1
) ?
9977 std::u16string_view(u
"!") : std::u16string_view(u
".");
9979 PushString( sTabStr
);
9982 PushString( aRefStr
);
9985 void ScInterpreter::ScOffset()
9987 sal_uInt8 nParamCount
= GetByte();
9988 if ( !MustHaveParamCount( nParamCount
, 3, 5 ) )
9991 bool bNewWidth
= false;
9992 bool bNewHeight
= false;
9993 sal_Int32 nColNew
= 1, nRowNew
= 1;
9994 if (nParamCount
== 5)
10000 nColNew
= GetInt32();
10004 if (nParamCount
>= 4)
10010 nRowNew
= GetInt32();
10014 sal_Int32 nColPlus
= GetInt32();
10015 sal_Int32 nRowPlus
= GetInt32();
10016 if (nGlobalError
!= FormulaError::NONE
)
10018 PushError( nGlobalError
);
10021 if (nColNew
<= 0 || nRowNew
<= 0)
10023 PushIllegalArgument();
10032 switch (GetStackType())
10036 PopSingleRef(nCol1
, nRow1
, nTab1
);
10037 if (!bNewWidth
&& !bNewHeight
)
10039 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
) + nColPlus
);
10040 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
) + nRowPlus
);
10041 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
))
10042 PushIllegalArgument();
10044 PushSingleRef(nCol1
, nRow1
, nTab1
);
10048 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColPlus
);
10049 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowPlus
);
10050 nCol2
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColNew
-1);
10051 nRow2
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowNew
-1);
10052 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
) ||
10053 !mrDoc
.ValidCol(nCol2
) || !mrDoc
.ValidRow(nRow2
))
10054 PushIllegalArgument();
10056 PushDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab1
);
10060 case svExternalSingleRef
:
10062 sal_uInt16 nFileId
;
10064 ScSingleRefData aRef
;
10065 PopExternalSingleRef(nFileId
, aTabName
, aRef
);
10066 ScAddress aAbsRef
= aRef
.toAbs(mrDoc
, aPos
);
10067 nCol1
= aAbsRef
.Col();
10068 nRow1
= aAbsRef
.Row();
10069 nTab1
= aAbsRef
.Tab();
10071 if (!bNewWidth
&& !bNewHeight
)
10073 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
) + nColPlus
);
10074 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
) + nRowPlus
);
10075 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
))
10076 PushIllegalArgument();
10078 PushExternalSingleRef(nFileId
, aTabName
, nCol1
, nRow1
, nTab1
);
10082 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColPlus
);
10083 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowPlus
);
10084 nCol2
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColNew
-1);
10085 nRow2
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowNew
-1);
10087 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
) ||
10088 !mrDoc
.ValidCol(nCol2
) || !mrDoc
.ValidRow(nRow2
))
10089 PushIllegalArgument();
10091 PushExternalDoubleRef(nFileId
, aTabName
, nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10097 PopDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10099 nColNew
= nCol2
- nCol1
+ 1;
10101 nRowNew
= nRow2
- nRow1
+ 1;
10102 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColPlus
);
10103 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowPlus
);
10104 nCol2
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColNew
-1);
10105 nRow2
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowNew
-1);
10106 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
) ||
10107 !mrDoc
.ValidCol(nCol2
) || !mrDoc
.ValidRow(nRow2
) || nTab1
!= nTab2
)
10108 PushIllegalArgument();
10110 PushDoubleRef(nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab1
);
10113 case svExternalDoubleRef
:
10115 sal_uInt16 nFileId
;
10117 ScComplexRefData aRef
;
10118 PopExternalDoubleRef(nFileId
, aTabName
, aRef
);
10119 ScRange aAbs
= aRef
.toAbs(mrDoc
, aPos
);
10120 nCol1
= aAbs
.aStart
.Col();
10121 nRow1
= aAbs
.aStart
.Row();
10122 nTab1
= aAbs
.aStart
.Tab();
10123 nCol2
= aAbs
.aEnd
.Col();
10124 nRow2
= aAbs
.aEnd
.Row();
10125 nTab2
= aAbs
.aEnd
.Tab();
10127 nColNew
= nCol2
- nCol1
+ 1;
10129 nRowNew
= nRow2
- nRow1
+ 1;
10130 nCol1
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColPlus
);
10131 nRow1
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowPlus
);
10132 nCol2
= static_cast<SCCOL
>(static_cast<tools::Long
>(nCol1
)+nColNew
-1);
10133 nRow2
= static_cast<SCROW
>(static_cast<tools::Long
>(nRow1
)+nRowNew
-1);
10134 if (!mrDoc
.ValidCol(nCol1
) || !mrDoc
.ValidRow(nRow1
) ||
10135 !mrDoc
.ValidCol(nCol2
) || !mrDoc
.ValidRow(nRow2
) || nTab1
!= nTab2
)
10136 PushIllegalArgument();
10138 PushExternalDoubleRef(nFileId
, aTabName
, nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10142 PushIllegalParameter();
10147 void ScInterpreter::ScIndex()
10149 sal_uInt8 nParamCount
= GetByte();
10150 if ( !MustHaveParamCount( nParamCount
, 1, 4 ) )
10157 if (nParamCount
== 4)
10158 nArea
= GetUInt32();
10162 if (nParamCount
>= 3)
10164 bColMissing
= IsMissing();
10165 nCol
= static_cast<SCCOL
>(GetInt16());
10169 bColMissing
= false;
10172 if (nParamCount
>= 2)
10173 nRow
= static_cast<SCROW
>(GetInt32());
10176 if (GetStackType() == svRefList
)
10177 nAreaCount
= (sp
? pStack
[sp
-1]->GetRefList()->size() : 0);
10179 nAreaCount
= 1; // one reference or array or whatever
10180 if (nGlobalError
!= FormulaError::NONE
|| nAreaCount
== 0 || static_cast<size_t>(nArea
) > nAreaCount
)
10182 PushError( FormulaError::NoRef
);
10185 else if (nArea
< 1 || nCol
< 0 || nRow
< 0)
10187 PushIllegalArgument();
10190 switch (GetStackType())
10193 case svExternalSingleRef
:
10194 case svExternalDoubleRef
:
10197 SetError(FormulaError::IllegalArgument
);
10198 sal_uInt16 nOldSp
= sp
;
10199 ScMatrixRef pMat
= GetMatrix();
10203 pMat
->GetDimensions(nC
, nR
);
10205 // Access one element of a vector independent of col/row
10206 // orientation. Excel documentation does not mention, but
10207 // i62850 had a .xls example of a row vector accessed by
10208 // row number returning one element. This
10209 // INDEX(row_vector;element) behaves the same as
10210 // INDEX(row_vector;0;element) and thus contradicts Excel
10211 // documentation where the second parameter is always
10214 // ODFF v1.3 in 6.14.6 INDEX states "If DataSource is a
10215 // one-dimensional row vector, Row is optional, which
10216 // effectively makes Row act as the column offset into the
10217 // vector". Guess the first Row is a typo and should read
10220 const bool bRowVectorSpecial
= (nParamCount
== 2 || bColMissing
);
10221 const bool bRowVectorElement
= (nR
== 1 && (nCol
!= 0 || (bRowVectorSpecial
&& nRow
!= 0)));
10222 const bool bVectorElement
= (bRowVectorElement
|| (nC
== 1 && nRow
!= 0));
10224 if (nC
== 0 || nR
== 0 ||
10225 (!bVectorElement
&& (o3tl::make_unsigned(nCol
) > nC
||
10226 o3tl::make_unsigned(nRow
) > nR
)))
10227 PushError( FormulaError::NoRef
);
10228 else if (nCol
== 0 && nRow
== 0)
10230 else if (bVectorElement
)
10232 // Vectors here don't replicate to the other dimension.
10233 SCSIZE nElement
, nOtherDimension
;
10234 if (bRowVectorElement
&& !bRowVectorSpecial
)
10236 nElement
= o3tl::make_unsigned(nCol
);
10237 nOtherDimension
= o3tl::make_unsigned(nRow
);
10241 nElement
= o3tl::make_unsigned(nRow
);
10242 nOtherDimension
= o3tl::make_unsigned(nCol
);
10245 if (nElement
== 0 || nElement
> nC
* nR
|| nOtherDimension
> 1)
10246 PushError( FormulaError::NoRef
);
10250 if (pMat
->IsStringOrEmpty( nElement
))
10251 PushString( pMat
->GetString(nElement
).getString());
10253 PushDouble( pMat
->GetDouble( nElement
));
10256 else if (nCol
== 0)
10258 ScMatrixRef pResMat
= GetNewMat(nC
, 1, /*bEmpty*/true);
10261 SCSIZE nRowMinus1
= static_cast<SCSIZE
>(nRow
- 1);
10262 for (SCSIZE i
= 0; i
< nC
; i
++)
10263 if (!pMat
->IsStringOrEmpty(i
, nRowMinus1
))
10264 pResMat
->PutDouble(pMat
->GetDouble(i
,
10265 nRowMinus1
), i
, 0);
10267 pResMat
->PutString(pMat
->GetString(i
, nRowMinus1
), i
, 0);
10269 PushMatrix(pResMat
);
10272 PushError( FormulaError::NoRef
);
10274 else if (nRow
== 0)
10276 ScMatrixRef pResMat
= GetNewMat(1, nR
, /*bEmpty*/true);
10279 SCSIZE nColMinus1
= static_cast<SCSIZE
>(nCol
- 1);
10280 for (SCSIZE i
= 0; i
< nR
; i
++)
10281 if (!pMat
->IsStringOrEmpty(nColMinus1
, i
))
10282 pResMat
->PutDouble(pMat
->GetDouble(nColMinus1
,
10285 pResMat
->PutString(pMat
->GetString(nColMinus1
, i
), i
);
10286 PushMatrix(pResMat
);
10289 PushError( FormulaError::NoRef
);
10293 if (!pMat
->IsStringOrEmpty( static_cast<SCSIZE
>(nCol
-1),
10294 static_cast<SCSIZE
>(nRow
-1)))
10295 PushDouble( pMat
->GetDouble(
10296 static_cast<SCSIZE
>(nCol
-1),
10297 static_cast<SCSIZE
>(nRow
-1)));
10299 PushString( pMat
->GetString(
10300 static_cast<SCSIZE
>(nCol
-1),
10301 static_cast<SCSIZE
>(nRow
-1)).getString());
10311 PopSingleRef( nCol1
, nRow1
, nTab1
);
10312 if (nCol
> 1 || nRow
> 1)
10313 PushError( FormulaError::NoRef
);
10315 PushSingleRef( nCol1
, nRow1
, nTab1
);
10327 bool bRowArray
= false;
10328 if (GetStackType() == svRefList
)
10330 FormulaConstTokenRef xRef
= PopToken();
10331 if (nGlobalError
!= FormulaError::NONE
|| !xRef
)
10333 PushError( FormulaError::NoRef
);
10336 ScRange
aRange( ScAddress::UNINITIALIZED
);
10337 DoubleRefToRange( (*(xRef
->GetRefList()))[nArea
-1], aRange
);
10338 aRange
.GetVars( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10339 if ( nParamCount
== 2 && nRow1
== nRow2
)
10344 PopDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab2
);
10345 if ( nParamCount
== 2 && nRow1
== nRow2
)
10348 if ( nTab1
!= nTab2
||
10349 (nCol
> 0 && nCol1
+nCol
-1 > nCol2
) ||
10350 (nRow
> 0 && nRow1
+nRow
-1 > nRow2
&& !bRowArray
) ||
10351 ( nRow
> nCol2
- nCol1
+ 1 && bRowArray
))
10352 PushError( FormulaError::NoRef
);
10353 else if (nCol
== 0 && nRow
== 0)
10355 if ( nCol1
== nCol2
&& nRow1
== nRow2
)
10356 PushSingleRef( nCol1
, nRow1
, nTab1
);
10358 PushDoubleRef( nCol1
, nRow1
, nTab1
, nCol2
, nRow2
, nTab1
);
10360 else if (nRow
== 0)
10362 if ( nRow1
== nRow2
)
10363 PushSingleRef( nCol1
+nCol
-1, nRow1
, nTab1
);
10365 PushDoubleRef( nCol1
+nCol
-1, nRow1
, nTab1
,
10366 nCol1
+nCol
-1, nRow2
, nTab1
);
10368 else if (nCol
== 0)
10370 if ( nCol1
== nCol2
)
10371 PushSingleRef( nCol1
, nRow1
+nRow
-1, nTab1
);
10372 else if ( bRowArray
)
10374 nCol
=static_cast<SCCOL
>(nRow
);
10376 PushSingleRef( nCol1
+nCol
-1, nRow1
+nRow
-1, nTab1
);
10379 PushDoubleRef( nCol1
, nRow1
+nRow
-1, nTab1
,
10380 nCol2
, nRow1
+nRow
-1, nTab1
);
10383 PushSingleRef( nCol1
+nCol
-1, nRow1
+nRow
-1, nTab1
);
10388 PushError( FormulaError::NoRef
);
10392 void ScInterpreter::ScMultiArea()
10394 // Legacy support, convert to RefList
10395 sal_uInt8 nParamCount
= GetByte();
10396 if (MustHaveParamCountMin( nParamCount
, 1))
10398 while (nGlobalError
== FormulaError::NONE
&& nParamCount
-- > 1)
10405 void ScInterpreter::ScAreas()
10407 sal_uInt8 nParamCount
= GetByte();
10408 if (!MustHaveParamCount( nParamCount
, 1))
10412 switch (GetStackType())
10416 FormulaConstTokenRef xT
= PopToken();
10417 ValidateRef( *xT
->GetSingleRef());
10423 FormulaConstTokenRef xT
= PopToken();
10424 ValidateRef( *xT
->GetDoubleRef());
10430 FormulaConstTokenRef xT
= PopToken();
10431 ValidateRef( *(xT
->GetRefList()));
10432 nCount
+= xT
->GetRefList()->size();
10436 SetError( FormulaError::IllegalParameter
);
10438 PushDouble( double(nCount
));
10441 void ScInterpreter::ScCurrency()
10443 sal_uInt8 nParamCount
= GetByte();
10444 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10449 if (nParamCount
== 2)
10451 fDec
= ::rtl::math::approxFloor(GetDouble());
10452 if (fDec
< -15.0 || fDec
> 15.0)
10454 PushIllegalArgument();
10460 double fVal
= GetDouble();
10463 fFac
= pow( double(10), fDec
);
10467 fVal
= ceil(fVal
*fFac
-0.5)/fFac
;
10469 fVal
= floor(fVal
*fFac
+0.5)/fFac
;
10470 const Color
* pColor
= nullptr;
10473 sal_uLong nIndex
= mrContext
.NFGetStandardFormat(
10474 SvNumFormatType::CURRENCY
,
10476 if ( static_cast<sal_uInt16
>(fDec
) != mrContext
.NFGetFormatPrecision( nIndex
) )
10478 OUString sFormatString
= mrContext
.NFGenerateFormat(
10481 true, // with thousands separator
10483 static_cast<sal_uInt16
>(fDec
));// decimal places
10484 if (!mrContext
.NFGetPreviewString(sFormatString
,
10489 SetError(FormulaError::IllegalArgument
);
10493 mrContext
.NFGetOutputString(fVal
, nIndex
, aStr
, &pColor
);
10498 void ScInterpreter::ScReplace()
10500 if ( !MustHaveParamCount( GetByte(), 4 ) )
10503 OUString aNewStr
= GetString().getString();
10504 sal_Int32 nCount
= GetStringPositionArgument();
10505 sal_Int32 nPos
= GetStringPositionArgument();
10506 OUString aOldStr
= GetString().getString();
10507 if (nPos
< 1 || nCount
< 0)
10508 PushIllegalArgument();
10511 sal_Int32 nLen
= aOldStr
.getLength();
10512 if (nPos
> nLen
+ 1)
10514 if (nCount
> nLen
- nPos
+ 1)
10515 nCount
= nLen
- nPos
+ 1;
10516 sal_Int32 nIdx
= 0;
10517 sal_Int32 nCnt
= 0;
10518 while ( nIdx
< nLen
&& nPos
> nCnt
+ 1 )
10520 aOldStr
.iterateCodePoints( &nIdx
);
10523 sal_Int32 nStart
= nIdx
;
10524 while ( nIdx
< nLen
&& nPos
+ nCount
- 1 > nCnt
)
10526 aOldStr
.iterateCodePoints( &nIdx
);
10529 if ( CheckStringResultLen( aOldStr
, aNewStr
.getLength() - (nIdx
- nStart
) ) )
10530 aOldStr
= aOldStr
.replaceAt( nStart
, nIdx
- nStart
, aNewStr
);
10531 PushString( aOldStr
);
10535 void ScInterpreter::ScFixed()
10537 sal_uInt8 nParamCount
= GetByte();
10538 if ( !MustHaveParamCount( nParamCount
, 1, 3 ) )
10544 if (nParamCount
== 3)
10545 bThousand
= !GetBool(); // Param true: no thousands separator
10548 if (nParamCount
>= 2)
10550 fDec
= ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
10551 if (fDec
< -15.0 || fDec
> 15.0)
10553 PushIllegalArgument();
10559 double fVal
= GetDouble();
10562 fFac
= pow( double(10), fDec
);
10566 fVal
= ceil(fVal
*fFac
-0.5)/fFac
;
10568 fVal
= floor(fVal
*fFac
+0.5)/fFac
;
10569 const Color
* pColor
= nullptr;
10572 sal_uLong nIndex
= mrContext
.NFGetStandardFormat(
10573 SvNumFormatType::NUMBER
,
10575 OUString sFormatString
= mrContext
.NFGenerateFormat(
10578 bThousand
, // with thousands separator
10580 static_cast<sal_uInt16
>(fDec
));// decimal places
10581 if (!mrContext
.NFGetPreviewString(sFormatString
,
10586 PushIllegalArgument();
10591 void ScInterpreter::ScFind()
10593 sal_uInt8 nParamCount
= GetByte();
10594 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
10598 if (nParamCount
== 3)
10599 nCnt
= GetDouble();
10602 OUString sStr
= GetString().getString();
10603 if (nCnt
< 1 || nCnt
> sStr
.getLength())
10607 sal_Int32 nPos
= sStr
.indexOf(GetString().getString(), nCnt
- 1);
10612 sal_Int32 nIdx
= 0;
10614 while ( nIdx
< nPos
)
10616 sStr
.iterateCodePoints( &nIdx
);
10619 PushDouble( static_cast<double>(nCnt
+ 1) );
10624 void ScInterpreter::ScExact()
10626 nFuncFmtType
= SvNumFormatType::LOGICAL
;
10627 if ( MustHaveParamCount( GetByte(), 2 ) )
10629 svl::SharedString s1
= GetString();
10630 svl::SharedString s2
= GetString();
10631 PushInt( int(s1
.getData() == s2
.getData()) );
10635 void ScInterpreter::ScLeft()
10637 sal_uInt8 nParamCount
= GetByte();
10638 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10642 if (nParamCount
== 2)
10644 n
= GetStringPositionArgument();
10647 PushIllegalArgument();
10653 OUString aStr
= GetString().getString();
10654 sal_Int32 nIdx
= 0;
10655 sal_Int32 nCnt
= 0;
10656 while ( nIdx
< aStr
.getLength() && n
> nCnt
++ )
10657 aStr
.iterateCodePoints( &nIdx
);
10658 aStr
= aStr
.copy( 0, nIdx
);
10659 PushString( aStr
);
10664 struct UBlockScript
{
10671 const UBlockScript scriptList
[] = {
10672 {UBLOCK_HANGUL_JAMO
, UBLOCK_HANGUL_JAMO
},
10673 {UBLOCK_CJK_RADICALS_SUPPLEMENT
, UBLOCK_HANGUL_SYLLABLES
},
10674 {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS
,UBLOCK_CJK_RADICALS_SUPPLEMENT
},
10675 {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS
,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS
},
10676 {UBLOCK_CJK_COMPATIBILITY_FORMS
, UBLOCK_CJK_COMPATIBILITY_FORMS
},
10677 {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS
, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS
},
10678 {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B
, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT
},
10679 {UBLOCK_CJK_STROKES
, UBLOCK_CJK_STROKES
}
10681 static bool IsDBCS(sal_Unicode currentChar
)
10683 // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
10684 if( (currentChar
== 0x005c || currentChar
== 0x20ac) &&
10685 (MsLangId::getConfiguredSystemLanguage() == LANGUAGE_JAPANESE
) )
10689 UBlockCode block
= ublock_getCode(currentChar
);
10690 for ( i
= 0; i
< SAL_N_ELEMENTS(scriptList
); i
++) {
10691 if (block
<= scriptList
[i
].to
) break;
10693 bRet
= (i
< SAL_N_ELEMENTS(scriptList
) && block
>= scriptList
[i
].from
);
10696 static sal_Int32
lcl_getLengthB( std::u16string_view str
, sal_Int32 nPos
)
10698 sal_Int32 index
= 0;
10699 sal_Int32 length
= 0;
10700 while ( index
< nPos
)
10702 if (IsDBCS(str
[index
]))
10710 static sal_Int32
getLengthB(std::u16string_view str
)
10715 return lcl_getLengthB( str
, str
.size() );
10717 void ScInterpreter::ScLenB()
10719 PushDouble( getLengthB(GetString().getString()) );
10721 static OUString
lcl_RightB(const OUString
&rStr
, sal_Int32 n
)
10723 if( n
< getLengthB(rStr
) )
10725 OUStringBuffer
aBuf(rStr
);
10726 sal_Int32 index
= aBuf
.getLength();
10727 while(index
-- >= 0)
10731 aBuf
.remove( 0, index
+ 1);
10736 aBuf
.remove( 0, index
+ 2 );
10737 aBuf
.insert( 0, " ");
10740 if(IsDBCS(aBuf
[index
]))
10745 return aBuf
.makeStringAndClear();
10749 void ScInterpreter::ScRightB()
10751 sal_uInt8 nParamCount
= GetByte();
10752 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10756 if (nParamCount
== 2)
10758 n
= GetStringPositionArgument();
10761 PushIllegalArgument();
10767 OUString
aStr(lcl_RightB(GetString().getString(), n
));
10768 PushString( aStr
);
10770 static OUString
lcl_LeftB(const OUString
&rStr
, sal_Int32 n
)
10772 if( n
< getLengthB(rStr
) )
10774 OUStringBuffer
aBuf(rStr
);
10775 sal_Int32 index
= -1;
10776 while(index
++ < aBuf
.getLength())
10780 aBuf
.truncate(index
);
10785 aBuf
.truncate( index
- 1 );
10789 if(IsDBCS(aBuf
[index
]))
10794 return aBuf
.makeStringAndClear();
10798 void ScInterpreter::ScLeftB()
10800 sal_uInt8 nParamCount
= GetByte();
10801 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10805 if (nParamCount
== 2)
10807 n
= GetStringPositionArgument();
10810 PushIllegalArgument();
10816 OUString
aStr(lcl_LeftB(GetString().getString(), n
));
10817 PushString( aStr
);
10819 void ScInterpreter::ScMidB()
10821 if ( !MustHaveParamCount( GetByte(), 3 ) )
10824 const sal_Int32 nCount
= GetStringPositionArgument();
10825 const sal_Int32 nStart
= GetStringPositionArgument();
10826 OUString aStr
= GetString().getString();
10827 if (nStart
< 1 || nCount
< 0)
10828 PushIllegalArgument();
10832 aStr
= lcl_LeftB(aStr
, nStart
+ nCount
- 1);
10833 sal_Int32 nCnt
= getLengthB(aStr
) - nStart
+ 1;
10834 aStr
= lcl_RightB(aStr
, std::max
<sal_Int32
>(nCnt
,0));
10839 void ScInterpreter::ScReplaceB()
10841 if ( !MustHaveParamCount( GetByte(), 4 ) )
10844 OUString aNewStr
= GetString().getString();
10845 const sal_Int32 nCount
= GetStringPositionArgument();
10846 const sal_Int32 nPos
= GetStringPositionArgument();
10847 OUString aOldStr
= GetString().getString();
10848 int nLen
= getLengthB( aOldStr
);
10849 if (nPos
< 1.0 || nPos
> nLen
|| nCount
< 0.0 || nPos
+ nCount
-1 > nLen
)
10850 PushIllegalArgument();
10853 // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as
10854 // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount)
10855 OUString aStr1
= lcl_LeftB( aOldStr
, nPos
- 1 );
10856 OUString aStr3
= lcl_RightB( aOldStr
, nLen
- nPos
- nCount
+ 1);
10858 PushString( aStr1
+ aNewStr
+ aStr3
);
10862 void ScInterpreter::ScFindB()
10864 sal_uInt8 nParamCount
= GetByte();
10865 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
10869 if ( nParamCount
== 3 )
10870 nStart
= GetStringPositionArgument();
10873 OUString aStr
= GetString().getString();
10874 int nLen
= getLengthB( aStr
);
10875 OUString asStr
= GetString().getString();
10876 int nsLen
= getLengthB( asStr
);
10877 if ( nStart
< 1 || nStart
> nLen
- nsLen
+ 1 )
10878 PushIllegalArgument();
10881 // create a string from sStr starting at nStart
10882 OUString aBuf
= lcl_RightB( aStr
, nLen
- nStart
+ 1 );
10883 // search aBuf for asStr
10884 sal_Int32 nPos
= aBuf
.indexOf( asStr
, 0 );
10889 // obtain byte value of nPos
10890 int nBytePos
= lcl_getLengthB( aBuf
, nPos
);
10891 PushDouble( nBytePos
+ nStart
);
10896 void ScInterpreter::ScSearchB()
10898 sal_uInt8 nParamCount
= GetByte();
10899 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
10903 if ( nParamCount
== 3 )
10905 nStart
= GetStringPositionArgument();
10908 PushIllegalArgument();
10914 OUString aStr
= GetString().getString();
10915 sal_Int32 nLen
= getLengthB( aStr
);
10916 OUString asStr
= GetString().getString();
10917 sal_Int32 nsLen
= nStart
- 1;
10918 if( nsLen
>= nLen
)
10922 // create a string from sStr starting at nStart
10923 OUString
aSubStr( lcl_RightB( aStr
, nLen
- nStart
+ 1 ) );
10924 // search aSubStr for asStr
10925 sal_Int32 nPos
= 0;
10926 sal_Int32 nEndPos
= aSubStr
.getLength();
10927 utl::SearchParam::SearchType eSearchType
= DetectSearchType( asStr
, mrDoc
);
10928 utl::SearchParam
sPar( asStr
, eSearchType
, false, '~', false );
10929 utl::TextSearch
sT( sPar
, ScGlobal::getCharClass() );
10930 if ( !sT
.SearchForward( aSubStr
, &nPos
, &nEndPos
) )
10934 // obtain byte value of nPos
10935 int nBytePos
= lcl_getLengthB( aSubStr
, nPos
);
10936 PushDouble( nBytePos
+ nStart
);
10941 void ScInterpreter::ScRight()
10943 sal_uInt8 nParamCount
= GetByte();
10944 if ( !MustHaveParamCount( nParamCount
, 1, 2 ) )
10948 if (nParamCount
== 2)
10950 n
= GetStringPositionArgument();
10953 PushIllegalArgument();
10959 OUString aStr
= GetString().getString();
10960 sal_Int32 nLen
= aStr
.getLength();
10962 PushString( aStr
);
10965 sal_Int32 nIdx
= nLen
;
10966 sal_Int32 nCnt
= 0;
10967 while ( nIdx
> 0 && n
> nCnt
)
10969 aStr
.iterateCodePoints( &nIdx
, -1 );
10972 aStr
= aStr
.copy( nIdx
, nLen
- nIdx
);
10973 PushString( aStr
);
10977 void ScInterpreter::ScSearch()
10979 sal_uInt8 nParamCount
= GetByte();
10980 if ( !MustHaveParamCount( nParamCount
, 2, 3 ) )
10984 if (nParamCount
== 3)
10986 nStart
= GetStringPositionArgument();
10989 PushIllegalArgument();
10995 OUString sStr
= GetString().getString();
10996 OUString SearchStr
= GetString().getString();
10997 sal_Int32 nPos
= nStart
- 1;
10998 sal_Int32 nEndPos
= sStr
.getLength();
10999 if( nPos
>= nEndPos
)
11003 utl::SearchParam::SearchType eSearchType
= DetectSearchType( SearchStr
, mrDoc
);
11004 utl::SearchParam
sPar(SearchStr
, eSearchType
, false, '~', false);
11005 utl::TextSearch
sT( sPar
, ScGlobal::getCharClass() );
11006 bool bBool
= sT
.SearchForward(sStr
, &nPos
, &nEndPos
);
11011 sal_Int32 nIdx
= 0;
11012 sal_Int32 nCnt
= 0;
11013 while ( nIdx
< nPos
)
11015 sStr
.iterateCodePoints( &nIdx
);
11018 PushDouble( static_cast<double>(nCnt
+ 1) );
11023 void ScInterpreter::ScRegex()
11025 const sal_uInt8 nParamCount
= GetByte();
11026 if (!MustHaveParamCount( nParamCount
, 2, 4))
11029 // Flags are supported only for replacement, search match flags can be
11030 // individually and much more flexible set in the regular expression
11031 // pattern using (?ismwx-ismwx)
11032 bool bGlobalReplacement
= false;
11033 sal_Int32 nOccurrence
= 1; // default first occurrence, if any
11034 if (nParamCount
== 4)
11036 // Argument can be either string or double.
11037 double fOccurrence
;
11038 svl::SharedString aFlagsString
;
11041 bDouble
= GetDoubleOrString( fOccurrence
, aFlagsString
);
11044 // For an omitted argument keep the default.
11047 fOccurrence
= nOccurrence
;
11049 if (nGlobalError
!= FormulaError::NONE
)
11051 PushError( nGlobalError
);
11056 if (!CheckStringPositionArgument( fOccurrence
))
11058 PushError( FormulaError::IllegalArgument
);
11061 nOccurrence
= static_cast<sal_Int32
>(fOccurrence
);
11065 const OUString
aFlags( aFlagsString
.getString());
11066 // Empty flags string is valid => no flag set.
11067 if (aFlags
.getLength() > 1)
11069 // Only one flag supported.
11070 PushIllegalArgument();
11073 if (aFlags
.getLength() == 1)
11075 if (aFlags
.indexOf('g') >= 0)
11076 bGlobalReplacement
= true;
11079 // Unsupported flag.
11080 PushIllegalArgument();
11087 bool bReplacement
= false;
11088 OUString aReplacement
;
11089 if (nParamCount
>= 3)
11091 // A missing argument is not an empty string to replace the match.
11092 // nOccurrence==0 forces no replacement, so simply discard the
11094 if (IsMissing() || nOccurrence
== 0)
11098 aReplacement
= GetString().getString();
11099 bReplacement
= true;
11102 // If bGlobalReplacement==true and bReplacement==false then
11103 // bGlobalReplacement is silently ignored.
11105 const OUString aExpression
= GetString().getString();
11106 const OUString aText
= GetString().getString();
11108 if (nGlobalError
!= FormulaError::NONE
)
11110 PushError( nGlobalError
);
11114 // 0-th match or replacement is none, return original string early.
11115 if (nOccurrence
== 0)
11117 PushString( aText
);
11121 const icu::UnicodeString
aIcuExpression(
11122 false, reinterpret_cast<const UChar
*>(aExpression
.getStr()), aExpression
.getLength());
11123 UErrorCode status
= U_ZERO_ERROR
;
11124 icu::RegexMatcher
aRegexMatcher( aIcuExpression
, 0, status
);
11125 if (U_FAILURE(status
))
11128 PushIllegalArgument();
11131 // Guard against pathological patterns, limit steps of engine, see
11132 // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00
11133 aRegexMatcher
.setTimeLimit( 23*1000, status
);
11135 const icu::UnicodeString
aIcuText(false, reinterpret_cast<const UChar
*>(aText
.getStr()), aText
.getLength());
11136 aRegexMatcher
.reset( aIcuText
);
11140 // Find n-th occurrence.
11141 sal_Int32 nCount
= 0;
11142 while (aRegexMatcher
.find(status
) && U_SUCCESS(status
) && ++nCount
< nOccurrence
)
11144 if (U_FAILURE(status
))
11147 PushIllegalArgument();
11150 // n-th match found?
11151 if (nCount
!= nOccurrence
)
11153 PushError( FormulaError::NotAvailable
);
11156 // Extract matched text.
11157 icu::UnicodeString
aMatch( aRegexMatcher
.group( status
));
11158 if (U_FAILURE(status
))
11161 PushIllegalArgument();
11164 OUString
aResult( reinterpret_cast<const sal_Unicode
*>(aMatch
.getBuffer()), aMatch
.length());
11165 PushString( aResult
);
11169 const icu::UnicodeString
aIcuReplacement(
11170 false, reinterpret_cast<const UChar
*>(aReplacement
.getStr()), aReplacement
.getLength());
11171 icu::UnicodeString aReplaced
;
11172 if (bGlobalReplacement
)
11173 // Replace all occurrences of match with replacement.
11174 aReplaced
= aRegexMatcher
.replaceAll( aIcuReplacement
, status
);
11175 else if (nOccurrence
== 1)
11176 // Replace first occurrence of match with replacement.
11177 aReplaced
= aRegexMatcher
.replaceFirst( aIcuReplacement
, status
);
11180 // Replace n-th occurrence of match with replacement.
11181 sal_Int32 nCount
= 0;
11182 while (aRegexMatcher
.find(status
) && U_SUCCESS(status
))
11184 // XXX NOTE: After several RegexMatcher::find() the
11185 // RegexMatcher::appendReplacement() still starts at the
11186 // beginning (or after the last appendReplacement() position
11187 // which is none here) and copies the original text up to the
11188 // current found match and then replaces the found match.
11189 if (++nCount
== nOccurrence
)
11191 aRegexMatcher
.appendReplacement( aReplaced
, aIcuReplacement
, status
);
11195 aRegexMatcher
.appendTail( aReplaced
);
11197 if (U_FAILURE(status
))
11199 // Some error, e.g. extraneous $1 without group.
11200 PushIllegalArgument();
11203 OUString
aResult( reinterpret_cast<const sal_Unicode
*>(aReplaced
.getBuffer()), aReplaced
.length());
11204 PushString( aResult
);
11207 void ScInterpreter::ScMid()
11209 if ( !MustHaveParamCount( GetByte(), 3 ) )
11212 const sal_Int32 nSubLen
= GetStringPositionArgument();
11213 const sal_Int32 nStart
= GetStringPositionArgument();
11214 OUString aStr
= GetString().getString();
11215 if ( nStart
< 1 || nSubLen
< 0 )
11216 PushIllegalArgument();
11217 else if (nStart
> kScInterpreterMaxStrLen
|| nSubLen
> kScInterpreterMaxStrLen
)
11218 PushError(FormulaError::StringOverflow
);
11221 sal_Int32 nLen
= aStr
.getLength();
11222 sal_Int32 nIdx
= 0;
11223 sal_Int32 nCnt
= 0;
11224 while ( nIdx
< nLen
&& nStart
- 1 > nCnt
)
11226 aStr
.iterateCodePoints( &nIdx
);
11229 sal_Int32 nIdx0
= nIdx
; //start position
11231 while ( nIdx
< nLen
&& nStart
+ nSubLen
- 1 > nCnt
)
11233 aStr
.iterateCodePoints( &nIdx
);
11236 aStr
= aStr
.copy( nIdx0
, nIdx
- nIdx0
);
11237 PushString( aStr
);
11241 void ScInterpreter::ScText()
11243 if ( !MustHaveParamCount( GetByte(), 2 ) )
11246 OUString sFormatString
= GetString().getString();
11247 svl::SharedString aStr
;
11248 bool bString
= false;
11250 switch (GetStackType())
11256 fVal
= PopDouble();
11260 FormulaConstTokenRef
xTok( PopToken());
11261 if (nGlobalError
== FormulaError::NONE
)
11263 PushTokenRef( xTok
);
11264 // Temporarily override the ConvertStringToValue()
11265 // error for GetCellValue() / GetCellValueOrZero()
11266 FormulaError nSErr
= mnStringNoValueError
;
11267 mnStringNoValueError
= FormulaError::NotNumericString
;
11268 fVal
= GetDouble();
11269 mnStringNoValueError
= nSErr
;
11270 if (nGlobalError
== FormulaError::NotNumericString
)
11273 nGlobalError
= FormulaError::NONE
;
11274 PushTokenRef( xTok
);
11275 aStr
= GetString();
11281 if (nGlobalError
!= FormulaError::NONE
)
11282 PushError( nGlobalError
);
11283 else if (sFormatString
.isEmpty())
11285 // Mimic the Excel behaviour that
11286 // * anything numeric returns an empty string
11287 // * text convertible to numeric returns an empty string
11288 // * any other text returns that text
11289 // Conversion was detected above.
11293 PushString( OUString());
11298 const Color
* pColor
= nullptr;
11299 LanguageType eCellLang
;
11300 const ScPatternAttr
* pPattern
= mrDoc
.GetPattern(
11301 aPos
.Col(), aPos
.Row(), aPos
.Tab() );
11303 eCellLang
= pPattern
->GetItem( ATTR_LANGUAGE_FORMAT
).GetValue();
11305 eCellLang
= ScGlobal::eLnge
;
11308 if (!mrContext
.NFGetPreviewString( sFormatString
, aStr
.getString(),
11309 aResult
, &pColor
, eCellLang
))
11310 PushIllegalArgument();
11312 PushString( aResult
);
11316 if (!mrContext
.NFGetPreviewStringGuess( sFormatString
, fVal
,
11317 aResult
, &pColor
, eCellLang
))
11318 PushIllegalArgument();
11320 PushString( aResult
);
11325 void ScInterpreter::ScSubstitute()
11327 sal_uInt8 nParamCount
= GetByte();
11328 if ( !MustHaveParamCount( nParamCount
, 3, 4 ) )
11332 if (nParamCount
== 4)
11334 nCnt
= GetStringPositionArgument();
11337 PushIllegalArgument();
11343 OUString sNewStr
= GetString().getString();
11344 OUString sOldStr
= GetString().getString();
11345 OUString sStr
= GetString().getString();
11346 sal_Int32 nPos
= 0;
11347 sal_Int32 nCount
= 0;
11348 std::optional
<OUStringBuffer
> oResult
;
11349 for (sal_Int32 nEnd
= sStr
.indexOf(sOldStr
); nEnd
>= 0; nEnd
= sStr
.indexOf(sOldStr
, nEnd
))
11351 if (nCnt
== 0 || ++nCount
== nCnt
) // Found a replacement cite
11353 if (!oResult
) // Only allocate buffer when needed
11354 oResult
.emplace(sStr
.getLength() + sNewStr
.getLength() - sOldStr
.getLength());
11356 oResult
->append(sStr
.subView(nPos
, nEnd
- nPos
)); // Copy leading unchanged text
11357 if (!CheckStringResultLen(*oResult
, sNewStr
.getLength()))
11358 return PushError(GetError());
11359 oResult
->append(sNewStr
); // Copy the replacement
11360 nPos
= nEnd
+ sOldStr
.getLength();
11361 if (nCnt
> 0) // Found the single replacement site - end the loop
11364 nEnd
+= sOldStr
.getLength();
11366 if (oResult
) // If there were prior replacements, copy the rest, otherwise use original
11367 oResult
->append(sStr
.subView(nPos
, sStr
.getLength() - nPos
));
11368 PushString(oResult
? oResult
->makeStringAndClear() : sStr
);
11371 void ScInterpreter::ScRept()
11373 if ( !MustHaveParamCount( GetByte(), 2 ) )
11376 sal_Int32 nCnt
= GetStringPositionArgument();
11377 OUString aStr
= GetString().getString();
11379 PushIllegalArgument();
11380 else if (static_cast<double>(nCnt
) * aStr
.getLength() > kScInterpreterMaxStrLen
)
11382 PushError( FormulaError::StringOverflow
);
11384 else if (nCnt
== 0)
11385 PushString( OUString() );
11388 const sal_Int32 nLen
= aStr
.getLength();
11389 OUStringBuffer
aRes(nCnt
*nLen
);
11392 PushString( aRes
.makeStringAndClear() );
11396 void ScInterpreter::ScConcat()
11398 sal_uInt8 nParamCount
= GetByte();
11400 //reverse order of parameter stack to simplify processing
11401 ReverseStack(nParamCount
);
11403 OUStringBuffer aRes
;
11404 while( nParamCount
-- > 0)
11406 OUString aStr
= GetString().getString();
11407 if (CheckStringResultLen(aRes
, aStr
.getLength()))
11412 PushString( aRes
.makeStringAndClear() );
11415 FormulaError
ScInterpreter::GetErrorType()
11418 FormulaError nOldError
= nGlobalError
;
11419 nGlobalError
= FormulaError::NONE
;
11420 switch ( GetStackType() )
11424 FormulaConstTokenRef x
= PopToken();
11425 if (nGlobalError
!= FormulaError::NONE
)
11426 nErr
= nGlobalError
;
11429 const ScRefList
* pRefList
= x
->GetRefList();
11430 size_t n
= pRefList
->size();
11432 nErr
= FormulaError::NoRef
;
11434 nErr
= FormulaError::NoValue
;
11438 DoubleRefToRange( (*pRefList
)[0], aRange
);
11439 if (nGlobalError
!= FormulaError::NONE
)
11440 nErr
= nGlobalError
;
11444 if ( DoubleRefToPosSingleRef( aRange
, aAdr
) )
11445 nErr
= mrDoc
.GetErrCode( aAdr
);
11447 nErr
= nGlobalError
;
11456 PopDoubleRef( aRange
);
11457 if ( nGlobalError
!= FormulaError::NONE
)
11458 nErr
= nGlobalError
;
11462 if ( DoubleRefToPosSingleRef( aRange
, aAdr
) )
11463 nErr
= mrDoc
.GetErrCode( aAdr
);
11465 nErr
= nGlobalError
;
11472 PopSingleRef( aAdr
);
11473 if ( nGlobalError
!= FormulaError::NONE
)
11474 nErr
= nGlobalError
;
11476 nErr
= mrDoc
.GetErrCode( aAdr
);
11481 nErr
= nGlobalError
;
11483 nGlobalError
= nOldError
;
11487 void ScInterpreter::ScErrorType()
11489 FormulaError nErr
= GetErrorType();
11490 if ( nErr
!= FormulaError::NONE
)
11492 nGlobalError
= FormulaError::NONE
;
11493 PushDouble( static_cast<double>(nErr
) );
11501 void ScInterpreter::ScErrorType_ODF()
11503 FormulaError nErr
= GetErrorType();
11504 sal_uInt16 nErrType
;
11508 case FormulaError::NoCode
: // #NULL!
11511 case FormulaError::DivisionByZero
: // #DIV/0!
11514 case FormulaError::NoValue
: // #VALUE!
11517 case FormulaError::NoRef
: // #REF!
11520 case FormulaError::NoName
: // #NAME?
11523 case FormulaError::IllegalFPOperation
: // #NUM!
11526 case FormulaError::NotAvailable
: // #N/A
11530 #GETTING_DATA is a message that can appear in Excel when a large or
11531 complex worksheet is being calculated. In Excel 2007 and newer,
11532 operations are grouped so more complicated cells may finish after
11533 earlier ones do. While the calculations are still processing, the
11534 unfinished cells may display #GETTING_DATA.
11535 Because the message is temporary and disappears when the calculations
11536 complete, this isn’t a true error.
11537 No calc error code known (yet).
11539 case : // GETTING_DATA
11550 nGlobalError
=FormulaError::NONE
;
11551 PushDouble( nErrType
);
11557 static bool MayBeRegExp( std::u16string_view rStr
)
11559 if ( rStr
.empty() || (rStr
.size() == 1 && rStr
[0] != '.') )
11560 return false; // single meta characters can not be a regexp
11561 // First two characters are wildcard '?' and '*' characters.
11562 std::u16string_view
cre(u
"?*+.[]^$\\<>()|");
11563 return rStr
.find_first_of(cre
) != std::u16string_view::npos
;
11566 static bool MayBeWildcard( std::u16string_view rStr
)
11568 // Wildcards with '~' escape, if there are no wildcards then an escaped
11569 // character does not make sense, but it modifies the search pattern in an
11570 // Excel compatible wildcard search...
11571 std::u16string_view
cw(u
"*?~");
11572 return rStr
.find_first_of(cw
) != std::u16string_view::npos
;
11575 utl::SearchParam::SearchType
ScInterpreter::DetectSearchType( std::u16string_view rStr
, const ScDocument
& rDoc
)
11577 const auto eType
= rDoc
.GetDocOptions().GetFormulaSearchType();
11578 if ((eType
== utl::SearchParam::SearchType::Wildcard
&& MayBeWildcard(rStr
))
11579 || (eType
== utl::SearchParam::SearchType::Regexp
&& MayBeRegExp(rStr
)))
11581 return utl::SearchParam::SearchType::Normal
;
11584 bool ScInterpreter::SearchMatrixForValue( VectorSearchArguments
& vsa
, ScQueryParam
& rParam
, ScQueryEntry
& rEntry
, ScQueryEntry::Item
& rItem
)
11587 vsa
.pMatSrc
->GetDimensions( nC
, nR
);
11588 if (nC
> 1 && nR
> 1)
11590 // The source matrix must be a vector.
11591 PushIllegalParameter();
11594 vsa
.bVLookup
= ( nC
== 1 );
11596 // Do not propagate errors from matrix while searching.
11597 vsa
.pMatSrc
->SetErrorInterpreter( nullptr );
11599 SCSIZE nMatCount
= (vsa
.bVLookup
? nR
: nC
);
11600 VectorMatrixAccessor
aMatAcc(*(vsa
.pMatSrc
), vsa
.bVLookup
);
11601 bool bMatchWholeCell
= mrDoc
.GetDocOptions().IsMatchWholeCell();
11603 switch ( vsa
.eSearchMode
)
11607 switch ( vsa
.eMatchMode
)
11611 // simple serial search for equality mode (source data doesn't
11612 // need to be sorted).
11613 for (SCSIZE i
= 0; i
< nMatCount
; ++i
)
11615 if (lcl_CompareMatrix2Query( i
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
) == 0)
11617 vsa
.nHitIndex
= i
+1; // found !
11625 for (SCSIZE i
= 0; i
< nMatCount
; ++i
)
11627 sal_Int32 result
= lcl_CompareMatrix2Query( i
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
);
11630 vsa
.nHitIndex
= i
+1; // found !
11633 else if (vsa
.eMatchMode
== exactorS
&& result
== -1)
11635 if ( vsa
.nBestFit
== SCSIZE_MAX
)
11639 // replace value of vsa.nBestFit if value(i) > value(vsa.nBestFit)
11640 if ( lcl_Compare2MatrixCells( i
, aMatAcc
, vsa
.nBestFit
) == 1 )
11644 else if (vsa
.eMatchMode
== exactorG
&& result
== 1)
11646 if ( vsa
.nBestFit
== SCSIZE_MAX
)
11650 // replace value of vsa.nBestFit if value(i) < value(vsa.nBestFit)
11651 if ( lcl_Compare2MatrixCells( i
, aMatAcc
, vsa
.nBestFit
) == -1 )
11660 PushIllegalParameter();
11668 switch ( vsa
.eMatchMode
)
11672 // simple serial search for equality mode (source data doesn't
11673 // need to be sorted).
11674 for ( SCSIZE i
= nMatCount
- 1; i
> 0; i
-- )
11676 if (lcl_CompareMatrix2Query(i
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
) == 0)
11678 vsa
.nHitIndex
= i
+ 1; // found !
11686 for (SCSIZE i
= nMatCount
- 1; i
-- > 0; )
11688 sal_Int32 result
= lcl_CompareMatrix2Query( i
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
);
11691 vsa
.nHitIndex
= i
+ 1; // found !
11694 else if (vsa
.eMatchMode
== exactorS
&& result
== -1)
11696 if ( vsa
.nBestFit
== SCSIZE_MAX
)
11700 // replace value of vsa.nBestFit if value(i) > value(vsa.nBestFit)
11701 if ( lcl_Compare2MatrixCells( i
, aMatAcc
, vsa
.nBestFit
) == 1 )
11705 else if (vsa
.eMatchMode
== exactorG
&& result
== 1)
11707 if ( vsa
.nBestFit
== SCSIZE_MAX
)
11711 // replace value of vsa.nBestFit if value(i) < value(vsa.nBestFit)
11712 if ( lcl_Compare2MatrixCells( i
, aMatAcc
, vsa
.nBestFit
) == -1 )
11721 PushIllegalParameter();
11730 // binary search for non-equality mode (the source data is sorted)
11731 bool bAscOrder
= ( vsa
.eSearchMode
== searchbasc
);
11733 SCSIZE nLast
= nMatCount
- 1;
11734 for ( SCSIZE nLen
= nLast
- nFirst
; nLen
> 0; nLen
= nLast
- nFirst
)
11736 SCSIZE nMid
= nFirst
+ nLen
/ 2;
11737 sal_Int32 nCmp
= lcl_CompareMatrix2Query( nMid
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
);
11740 // exact match. find the last item with the same value.
11741 lcl_GetLastMatch( nMid
, aMatAcc
, nMatCount
);
11742 vsa
.nHitIndex
= nMid
+ 1;
11746 if ( nLen
== 1 ) // first and last items are next to each other.
11748 if ( bAscOrder
&& vsa
.eMatchMode
== exactorS
)
11749 vsa
.nHitIndex
= ( nCmp
> 0 ? nFirst
: nLast
);
11750 else if ( !bAscOrder
&& vsa
.eMatchMode
== exactorG
)
11751 vsa
.nHitIndex
= ( nCmp
< 0 ? nFirst
: nLast
);
11771 if ( vsa
.nHitIndex
== nMatCount
- 1 ) // last item
11773 nCmp
= lcl_CompareMatrix2Query( vsa
.nHitIndex
, aMatAcc
, rParam
, rEntry
, bMatchWholeCell
);
11774 if ( ( vsa
.eMatchMode
== exactorS
&& nCmp
<= 0 ) ||
11775 ( vsa
.eMatchMode
== exactorG
&& nCmp
>= 0 ) )
11777 // either the last item is an exact match or the real
11778 // hit is beyond the last item.
11790 PushIllegalParameter();
11794 if ((vsa
.nHitIndex
> 0) && ((rItem
.meType
== ScQueryEntry::ByString
&& aMatAcc
.IsValue(vsa
.nHitIndex
- 1)) ||
11795 (rItem
.meType
== ScQueryEntry::ByValue
&& !aMatAcc
.IsValue(vsa
.nHitIndex
- 1))))
11798 vsa
.isResultNA
= true;
11804 bool ScInterpreter::SearchRangeForValue( VectorSearchArguments
& vsa
, ScQueryParam
& rParam
, ScQueryEntry
& rEntry
)
11806 vsa
.bVLookup
= ( vsa
.nCol1
== vsa
.nCol2
);
11807 switch ( vsa
.eSearchMode
)
11816 // search of rows in column
11817 rParam
.bByRow
= true;
11818 ScAddress
aResultPos( vsa
.nCol1
, vsa
.nRow1
, vsa
.nTab1
);
11819 const ScComplexRefData
* refData
= nullptr;
11820 if ( LookupQueryWithCache( aResultPos
, rParam
, refData
, vsa
.eSearchMode
, vsa
.nSearchOpCode
) )
11821 vsa
.nHitIndex
= aResultPos
.Row() - vsa
.nRow1
+ 1;
11825 rParam
.bByRow
= false;
11826 bool bBinarySearch
= vsa
.eSearchMode
== searchbasc
|| vsa
.eSearchMode
== searchbdesc
;
11827 if (bBinarySearch
&& (vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
|| vsa
.nSearchOpCode
== SC_OPCODE_X_MATCH
))
11829 ScQueryCellIteratorSortedCache
aCellIter(mrDoc
, mrContext
, rParam
.nTab
, rParam
, false, false);
11830 // Advance Entry.nField in Iterator if column changed
11831 aCellIter
.SetAdvanceQueryParamEntryField(true);
11832 aCellIter
.SetSortedBinarySearchMode(vsa
.eSearchMode
);
11833 aCellIter
.SetLookupMode(vsa
.nSearchOpCode
);
11834 if (aCellIter
.GetFirst())
11836 vsa
.nHitIndex
= aCellIter
.GetCol() - vsa
.nCol1
+ 1;
11841 // search of columns in row
11842 bool bReverseSearch
= (vsa
.eSearchMode
== searchrev
);
11843 ScQueryCellIteratorDirect
aCellIter(mrDoc
, mrContext
, vsa
.nTab1
, rParam
, false, bReverseSearch
);
11844 // Advance Entry.nField in Iterator if column changed
11845 aCellIter
.SetAdvanceQueryParamEntryField(true);
11846 aCellIter
.SetLookupMode(vsa
.nSearchOpCode
);
11847 aCellIter
.SetSortedBinarySearchMode(vsa
.eSearchMode
);
11848 if (rEntry
.eOp
== SC_EQUAL
)
11850 if (aCellIter
.GetFirst())
11851 vsa
.nHitIndex
= aCellIter
.GetCol() - vsa
.nCol1
+ 1;
11857 if (aCellIter
.FindEqualOrSortedLastInRange(nC
, nR
))
11858 vsa
.nHitIndex
= nC
- vsa
.nCol1
+ 1;
11866 PushIllegalParameter();
11873 /** When search value is found, the index is stored in struct VectorSearchArguments.nIndex
11874 and SearchVectorForValue() returns true. When search value is not found or an error
11875 occurs, SearchVectorForValue() pushes the relevant (error)message and returns false,
11876 expect when SearchVectorForValue() is called by ScXLookup and the search value is not
11878 This difference in behaviour is because MATCH returns the found index and XLOOKUP
11879 uses the found index to determine the result(s) to be pushed and may return a custom
11880 value when the search value is not found.
11882 bool ScInterpreter::SearchVectorForValue( VectorSearchArguments
& vsa
)
11885 ScQueryParam rParam
;
11886 rParam
.nCol1
= vsa
.nCol1
;
11887 rParam
.nRow1
= vsa
.nRow1
;
11888 rParam
.nCol2
= vsa
.nCol2
;
11889 rParam
.nRow2
= vsa
.nRow2
;
11890 rParam
.nTab
= vsa
.nTab1
;
11892 ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
11893 rEntry
.nField
= vsa
.eSearchMode
!= searchrev
? vsa
.nCol1
: vsa
.nCol2
;
11894 rEntry
.bDoQuery
= true;
11895 switch ( vsa
.eMatchMode
)
11898 rEntry
.eOp
= SC_EQUAL
;
11902 rEntry
.eOp
= SC_LESS_EQUAL
;
11906 rEntry
.eOp
= SC_GREATER_EQUAL
;
11911 // this mode can only used with XLOOKUP/XMATCH
11912 if ( vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
|| vsa
.nSearchOpCode
== SC_OPCODE_X_MATCH
)
11914 // Wildcard/Regex search mode with binary search is not allowed
11915 if (vsa
.eSearchMode
== searchbasc
|| vsa
.eSearchMode
== searchbdesc
)
11921 rEntry
.eOp
= SC_EQUAL
;
11922 if ( vsa
.isStringSearch
)
11924 if (vsa
.eMatchMode
== wildcard
&& MayBeWildcard(vsa
.sSearchStr
.getString()))
11925 rParam
.eSearchType
= utl::SearchParam::SearchType::Wildcard
;
11926 else if (vsa
.eMatchMode
== regex
&& MayBeRegExp(vsa
.sSearchStr
.getString()))
11927 rParam
.eSearchType
= utl::SearchParam::SearchType::Regexp
;
11929 rParam
.eSearchType
= utl::SearchParam::SearchType::Normal
;
11934 PushIllegalParameter();
11940 PushIllegalParameter();
11944 ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
11945 // allow to match empty cells as result if we are looking for the next smaller
11946 // or larger values in case of the new lookup functions
11947 if (rEntry
.eOp
!= SC_EQUAL
&& (vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
||
11948 vsa
.nSearchOpCode
== SC_OPCODE_X_MATCH
))
11949 rItem
.mbMatchEmpty
= true;
11951 if ( vsa
.isStringSearch
)
11953 rItem
.meType
= ScQueryEntry::ByString
;
11954 rItem
.maString
= vsa
.sSearchStr
;
11955 if ( vsa
.nSearchOpCode
== SC_OPCODE_MATCH
)
11957 if ( mrDoc
.IsInVBAMode() )
11958 rParam
.eSearchType
= utl::SearchParam::SearchType::Wildcard
;
11960 rParam
.eSearchType
= DetectSearchType(rEntry
.GetQueryItem().maString
.getString(), mrDoc
);
11963 else if ( vsa
.isEmptySearch
&& (vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
||
11964 vsa
.nSearchOpCode
== SC_OPCODE_X_MATCH
) )
11966 rEntry
.SetQueryByEmpty();
11967 rItem
.mbMatchEmpty
= true;
11971 rItem
.mfVal
= vsa
.fSearchVal
;
11972 rItem
.meType
= ScQueryEntry::ByValue
;
11976 if (vsa
.pMatSrc
) // The source data is matrix array.
11979 if ( !SearchMatrixForValue( vsa
, rParam
, rEntry
, rItem
) )
11985 if ( !SearchRangeForValue( vsa
, rParam
, rEntry
) )
11989 // MATCH expects index starting with 1, XLOOKUP expects index starting with 0
11990 if ( vsa
.nHitIndex
> 0 )
11992 vsa
.nIndex
= ( vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
? --vsa
.nHitIndex
: vsa
.nHitIndex
);
11995 else if ( vsa
.nHitIndex
== 0 && vsa
.nBestFit
!= SCSIZE_MAX
)
11997 if ( vsa
.nSearchOpCode
== SC_OPCODE_X_LOOKUP
)
11999 vsa
.nIndex
= vsa
.nBestFit
;
12000 if ( !vsa
.pMatSrc
)
12002 vsa
.nIndex
-= ( vsa
.bVLookup
? vsa
.nRow1
: vsa
.nCol1
);
12007 vsa
.nIndex
= ++vsa
.nBestFit
;
12013 vsa
.isResultNA
= true;
12017 static bool lcl_LookupQuery( ScAddress
& o_rResultPos
, ScDocument
& rDoc
, ScInterpreterContext
& rContext
,
12018 const ScQueryParam
& rParam
, const ScQueryEntry
& rEntry
, const ScFormulaCell
* cell
,
12019 const ScComplexRefData
* refData
, sal_Int8 nSearchMode
, sal_uInt16 nOpCode
)
12021 if (rEntry
.eOp
!= SC_EQUAL
)
12023 // range lookup <= or >=
12026 bool bBinarySearch
= static_cast<SearchMode
>(nSearchMode
) == searchbasc
|| static_cast<SearchMode
>(nSearchMode
) == searchbdesc
;
12027 if ((bBinarySearch
&& (nOpCode
== SC_OPCODE_X_LOOKUP
|| nOpCode
== SC_OPCODE_X_MATCH
)) ||
12028 ScQueryCellIteratorSortedCache::CanBeUsed(rDoc
, rParam
, rParam
.nTab
, cell
, refData
, rContext
))
12030 ScQueryCellIteratorSortedCache
aCellIter(rDoc
, rContext
, rParam
.nTab
, rParam
, false, false);
12031 aCellIter
.SetSortedBinarySearchMode(nSearchMode
);
12032 aCellIter
.SetLookupMode(nOpCode
);
12033 if (aCellIter
.GetFirst())
12035 o_rResultPos
.SetCol(aCellIter
.GetCol());
12036 o_rResultPos
.SetRow(aCellIter
.GetRow());
12042 bool bReverse
= (static_cast<SearchMode
>(nSearchMode
) == searchrev
);
12043 ScQueryCellIteratorDirect
aCellIter(rDoc
, rContext
, rParam
.nTab
, rParam
, false, bReverse
);
12045 aCellIter
.SetSortedBinarySearchMode(nSearchMode
);
12046 aCellIter
.SetLookupMode(nOpCode
);
12047 if (aCellIter
.FindEqualOrSortedLastInRange(nCol
, nRow
))
12049 o_rResultPos
.SetCol(nCol
);
12050 o_rResultPos
.SetRow(nRow
);
12057 // we can use binary search for rows if the SearchMode is searchbasc or searchbdesc
12058 bool bLiteral
= rParam
.eSearchType
== utl::SearchParam::SearchType::Normal
&&
12059 rEntry
.GetQueryItem().meType
== ScQueryEntry::ByString
;
12060 bool bBinary
= rParam
.bByRow
&&
12061 (bLiteral
|| rEntry
.GetQueryItem().meType
== ScQueryEntry::ByValue
);
12063 if( bBinary
&& (static_cast<SearchMode
>(nSearchMode
) == searchbasc
|| static_cast<SearchMode
>(nSearchMode
) == searchbdesc
||
12064 ScQueryCellIteratorSortedCache::CanBeUsed(rDoc
, rParam
, rParam
.nTab
, cell
, refData
, rContext
)))
12066 ScQueryCellIteratorSortedCache
aCellIter( rDoc
, rContext
, rParam
.nTab
, rParam
, false, false );
12067 aCellIter
.SetSortedBinarySearchMode(nSearchMode
);
12068 aCellIter
.SetLookupMode(nOpCode
);
12069 if (aCellIter
.GetFirst())
12071 o_rResultPos
.SetCol( aCellIter
.GetCol());
12072 o_rResultPos
.SetRow( aCellIter
.GetRow());
12078 ScQueryCellIteratorDirect
aCellIter( rDoc
, rContext
, rParam
.nTab
, rParam
, false,
12079 static_cast<SearchMode
>(nSearchMode
) == searchrev
);
12080 aCellIter
.SetSortedBinarySearchMode(nSearchMode
);
12081 aCellIter
.SetLookupMode(nOpCode
);
12082 if (aCellIter
.GetFirst())
12084 o_rResultPos
.SetCol( aCellIter
.GetCol());
12085 o_rResultPos
.SetRow( aCellIter
.GetRow());
12094 // =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted)
12095 // [SearchCriterion] is the value searched for in the first column of the array.
12096 // [RangeArray] is the reference, which is to comprise at least two columns.
12097 // [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1.
12099 // Prerequisite of lcl_getPrevRowWithEmptyValueLookup():
12100 // Value referenced by [SearchCriterion] is empty.
12101 // lcl_getPrevRowWithEmptyValueLookup() performs following checks:
12102 // - if we run query with "exact match" mode (i.e. VLOOKUP)
12103 // - and if we already have the same lookup done before but for another row
12104 // which is also had empty [SearchCriterion]
12107 // we could say, that for current row we could reuse results of the cached call which was done for the row2
12108 // In this case we return row index, which is >= 0.
12111 // -1 is returned, which will lead to default behavior =>
12112 // complete lookup will be done in RangeArray inside lcl_LookupQuery() method.
12114 // This method was added only for speed up to avoid several useless complete
12115 // lookups inside [RangeArray] for searching empty strings.
12117 static SCROW
lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache
& rCache
,
12118 const ScLookupCache::QueryCriteria
& rCriteria
, const ScQueryParam
& rParam
)
12120 // is lookup value empty?
12121 const ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
12122 const ScQueryEntry::Item
& rItem
= rEntry
.GetQueryItem();
12123 if (! rItem
.maString
.getString().isEmpty())
12124 return -1; // not found
12126 // try to find the row index for which we have already performed lookup
12127 // and have some result of it inside cache
12128 return rCache
.lookup( rCriteria
);
12131 bool ScInterpreter::LookupQueryWithCache( ScAddress
& o_rResultPos
,
12132 const ScQueryParam
& rParam
, const ScComplexRefData
* refData
,
12133 sal_Int8 nSearchMode
, sal_uInt16 nOpCode
) const
12135 bool bFound
= false;
12136 const ScQueryEntry
& rEntry
= rParam
.GetEntry(0);
12137 bool bColumnsMatch
= (rParam
.nCol1
== rEntry
.nField
);
12138 // At least all volatile functions that generate indirect references have
12139 // to force non-cached lookup.
12140 /* TODO: We could further classify volatile functions into reference
12141 * generating and not reference generating functions to have to force less
12142 * direct lookups here. We could even further attribute volatility per
12143 * parameter so it would affect only the lookup range parameter. */
12144 if (!bColumnsMatch
|| GetVolatileType() != NOT_VOLATILE
)
12145 bFound
= lcl_LookupQuery( o_rResultPos
, mrDoc
, mrContext
, rParam
, rEntry
, pMyFormulaCell
,
12146 refData
, nSearchMode
, nOpCode
);
12149 ScRange
aLookupRange( rParam
.nCol1
, rParam
.nRow1
, rParam
.nTab
,
12150 rParam
.nCol2
, rParam
.nRow2
, rParam
.nTab
);
12151 ScLookupCache
& rCache
= mrDoc
.GetLookupCache( aLookupRange
, &mrContext
);
12152 ScLookupCache::QueryCriteria
aCriteria( rEntry
);
12153 ScLookupCache::Result eCacheResult
= rCache
.lookup( o_rResultPos
,
12156 // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells
12157 // This check was added only for speed up to avoid several useless complete
12158 // lookups inside [RangeArray] for searching empty strings.
12159 if (eCacheResult
== ScLookupCache::NOT_CACHED
&& aCriteria
.isEmptyStringQuery())
12161 const SCROW nPrevRowWithEmptyValueLookup
= lcl_getPrevRowWithEmptyValueLookup(rCache
, aCriteria
, rParam
);
12162 if (nPrevRowWithEmptyValueLookup
>= 0)
12164 // make the same lookup using cache with different row index
12165 // (this lookup was already cached)
12166 ScAddress
aPosPrev(aPos
);
12167 aPosPrev
.SetRow(nPrevRowWithEmptyValueLookup
);
12169 eCacheResult
= rCache
.lookup( o_rResultPos
, aCriteria
, aPosPrev
);
12173 switch (eCacheResult
)
12175 case ScLookupCache::NOT_CACHED
:
12176 case ScLookupCache::CRITERIA_DIFFERENT
:
12177 bFound
= lcl_LookupQuery( o_rResultPos
, mrDoc
, mrContext
, rParam
, rEntry
,
12178 pMyFormulaCell
, refData
, nSearchMode
, nOpCode
);
12179 if (eCacheResult
== ScLookupCache::NOT_CACHED
)
12180 rCache
.insert( o_rResultPos
, aCriteria
, aPos
, bFound
);
12182 case ScLookupCache::FOUND
:
12185 case ScLookupCache::NOT_AVAILABLE
:
12186 ; // nothing, bFound remains FALSE
12193 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */