1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
4 *
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/.
8 *
9 * This file incorporates work covered by the following license notice:
10 *
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 .
18 */
19
20 #include <interpre.hxx>
21
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/zforlist.hxx>
30 #include <svl/zformat.hxx>
31 #include <tools/urlobj.hxx>
32 #include <unotools/charclass.hxx>
33 #include <sfx2/docfile.hxx>
34 #include <sfx2/printer.hxx>
35 #include <unotools/collatorwrapper.hxx>
36 #include <unotools/transliterationwrapper.hxx>
37 #include <rtl/character.hxx>
38 #include <rtl/ustring.hxx>
39 #include <sal/log.hxx>
40 #include <osl/diagnose.h>
41 #include <unicode/uchar.h>
42 #include <unicode/regex.h>
43 #include <i18nlangtag/mslangid.hxx>
44
45 #include <patattr.hxx>
46 #include <global.hxx>
47 #include <document.hxx>
48 #include <dociter.hxx>
49 #include <formulacell.hxx>
50 #include <scmatrix.hxx>
51 #include <docoptio.hxx>
52 #include <attrib.hxx>
53 #include <jumpmatrix.hxx>
54 #include <cellkeytranslator.hxx>
55 #include <lookupcache.hxx>
56 #include <rangenam.hxx>
57 #include <rangeutl.hxx>
58 #include <compiler.hxx>
59 #include <externalrefmgr.hxx>
60 #include <doubleref.hxx>
61 #include <queryparam.hxx>
62 #include <queryentry.hxx>
63 #include <tokenarray.hxx>
64 #include <compare.hxx>
65
66 #include <comphelper/processfactory.hxx>
67 #include <comphelper/random.hxx>
68 #include <comphelper/string.hxx>
69 #include <svl/sharedstringpool.hxx>
70
71 #include <stdlib.h>
72 #include <vector>
73 #include <memory>
74 #include <limits>
75 #include <string_view>
76
77 const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48
78
79 ScCalcConfig *ScInterpreter::mpGlobalConfig = nullptr;
80
81 using namespace formula;
82 using ::std::unique_ptr;
83
ScIfJump()84 void ScInterpreter::ScIfJump()
85 {
86 const short* pJump = pCur->GetJump();
87 short nJumpCount = pJump[ 0 ];
88 MatrixJumpConditionToMatrix();
89 switch ( GetStackType() )
90 {
91 case svMatrix:
92 {
93 ScMatrixRef pMat = PopMatrix();
94 if ( !pMat )
95 PushIllegalParameter();
96 else
97 {
98 FormulaConstTokenRef xNew;
99 ScTokenMatrixMap::const_iterator aMapIter;
100 // DoubleError handled by JumpMatrix
101 pMat->SetErrorInterpreter( nullptr);
102 SCSIZE nCols, nRows;
103 pMat->GetDimensions( nCols, nRows );
104 if ( nCols == 0 || nRows == 0 )
105 {
106 PushIllegalArgument();
107 return;
108 }
109 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
110 xNew = (*aMapIter).second;
111 else
112 {
113 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
114 pCur->GetOpCode(), nCols, nRows));
115 for ( SCSIZE nC=0; nC < nCols; ++nC )
116 {
117 for ( SCSIZE nR=0; nR < nRows; ++nR )
118 {
119 double fVal;
120 bool bTrue;
121 bool bIsValue = pMat->IsValue(nC, nR);
122 if (bIsValue)
123 {
124 fVal = pMat->GetDouble(nC, nR);
125 bIsValue = std::isfinite(fVal);
126 bTrue = bIsValue && (fVal != 0.0);
127 if (bTrue)
128 fVal = 1.0;
129 }
130 else
131 {
132 // Treat empty and empty path as 0, but string
133 // as error. ScMatrix::IsValueOrEmpty() returns
134 // true for any empty, empty path, empty cell,
135 // empty result.
136 bIsValue = pMat->IsValueOrEmpty(nC, nR);
137 bTrue = false;
138 fVal = (bIsValue ? 0.0 : CreateDoubleError( FormulaError::NoValue));
139 }
140 if ( bTrue )
141 { // TRUE
142 if( nJumpCount >= 2 )
143 { // THEN path
144 pJumpMat->SetJump( nC, nR, fVal,
145 pJump[ 1 ],
146 pJump[ nJumpCount ]);
147 }
148 else
149 { // no parameter given for THEN
150 pJumpMat->SetJump( nC, nR, fVal,
151 pJump[ nJumpCount ],
152 pJump[ nJumpCount ]);
153 }
154 }
155 else
156 { // FALSE
157 if( nJumpCount == 3 && bIsValue )
158 { // ELSE path
159 pJumpMat->SetJump( nC, nR, fVal,
160 pJump[ 2 ],
161 pJump[ nJumpCount ]);
162 }
163 else
164 { // no parameter given for ELSE,
165 // or DoubleError
166 pJumpMat->SetJump( nC, nR, fVal,
167 pJump[ nJumpCount ],
168 pJump[ nJumpCount ]);
169 }
170 }
171 }
172 }
173 xNew = new ScJumpMatrixToken( pJumpMat );
174 GetTokenMatrixMap().emplace(pCur, xNew);
175 }
176 if (!xNew)
177 {
178 PushIllegalArgument();
179 return;
180 }
181 PushTokenRef( xNew);
182 // set endpoint of path for main code line
183 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
184 }
185 }
186 break;
187 default:
188 {
189 if ( GetBool() )
190 { // TRUE
191 if( nJumpCount >= 2 )
192 { // THEN path
193 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
194 }
195 else
196 { // no parameter given for THEN
197 nFuncFmtType = SvNumFormatType::LOGICAL;
198 PushInt(1);
199 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
200 }
201 }
202 else
203 { // FALSE
204 if( nJumpCount == 3 )
205 { // ELSE path
206 aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
207 }
208 else
209 { // no parameter given for ELSE
210 nFuncFmtType = SvNumFormatType::LOGICAL;
211 PushInt(0);
212 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
213 }
214 }
215 }
216 }
217 }
218
219 /** Store a matrix value in another matrix in the context of that other matrix
220 is the result matrix of a jump matrix. All arguments must be valid and are
221 not checked. */
lcl_storeJumpMatResult(const ScMatrix * pMat,ScJumpMatrix * pJumpMat,SCSIZE nC,SCSIZE nR)222 static void lcl_storeJumpMatResult(
223 const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
224 {
225 if ( pMat->IsValue( nC, nR ) )
226 {
227 double fVal = pMat->GetDouble( nC, nR );
228 pJumpMat->PutResultDouble( fVal, nC, nR );
229 }
230 else if ( pMat->IsEmpty( nC, nR ) )
231 {
232 pJumpMat->PutResultEmpty( nC, nR );
233 }
234 else
235 {
236 pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
237 }
238 }
239
ScIfError(bool bNAonly)240 void ScInterpreter::ScIfError( bool bNAonly )
241 {
242 const short* pJump = pCur->GetJump();
243 short nJumpCount = pJump[ 0 ];
244 if (!sp || nJumpCount != 2)
245 {
246 // Reset nGlobalError here to not propagate the old error, if any.
247 nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable);
248 PushError( nGlobalError);
249 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
250 return;
251 }
252
253 FormulaConstTokenRef xToken( pStack[ sp - 1 ] );
254 bool bError = false;
255 FormulaError nOldGlobalError = nGlobalError;
256 nGlobalError = FormulaError::NONE;
257
258 MatrixJumpConditionToMatrix();
259 switch (GetStackType())
260 {
261 default:
262 Pop();
263 // Act on implicitly propagated error, if any.
264 if (nOldGlobalError != FormulaError::NONE)
265 nGlobalError = nOldGlobalError;
266 if (nGlobalError != FormulaError::NONE)
267 bError = true;
268 break;
269 case svError:
270 PopError();
271 bError = true;
272 break;
273 case svDoubleRef:
274 case svSingleRef:
275 {
276 ScAddress aAdr;
277 if (!PopDoubleRefOrSingleRef( aAdr))
278 bError = true;
279 else
280 {
281
282 ScRefCellValue aCell(mrDoc, aAdr);
283 nGlobalError = GetCellErrCode(aCell);
284 if (nGlobalError != FormulaError::NONE)
285 bError = true;
286 }
287 }
288 break;
289 case svExternalSingleRef:
290 case svExternalDoubleRef:
291 {
292 double fVal;
293 svl::SharedString aStr;
294 // Handles also existing jump matrix case and sets error on
295 // elements.
296 GetDoubleOrStringFromMatrix( fVal, aStr);
297 if (nGlobalError != FormulaError::NONE)
298 bError = true;
299 }
300 break;
301 case svMatrix:
302 {
303 const ScMatrixRef pMat = PopMatrix();
304 if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable)))
305 {
306 bError = true;
307 break; // switch
308 }
309 // If the matrix has no queried error at all we can simply use
310 // it as result and don't need to bother with jump matrix.
311 SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
312 nErrorRow = ::std::numeric_limits<SCSIZE>::max();
313 SCSIZE nCols, nRows;
314 pMat->GetDimensions( nCols, nRows );
315 if (nCols == 0 || nRows == 0)
316 {
317 bError = true;
318 break; // switch
319 }
320 for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
321 {
322 for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
323 {
324 FormulaError nErr = pMat->GetError( nC, nR );
325 if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
326 {
327 bError = true;
328 nErrorCol = nC;
329 nErrorRow = nR;
330 }
331 }
332 }
333 if (!bError)
334 break; // switch, we're done and have the result
335
336 FormulaConstTokenRef xNew;
337 ScTokenMatrixMap::const_iterator aMapIter;
338 if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
339 {
340 xNew = (*aMapIter).second;
341 }
342 else
343 {
344 const ScMatrix* pMatPtr = pMat.get();
345 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
346 pCur->GetOpCode(), nCols, nRows));
347 // Init all jumps to no error to save single calls. Error
348 // is the exceptional condition.
349 const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult);
350 pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
351 // Up to first error position simply store results, no need
352 // to evaluate error conditions again.
353 SCSIZE nC = 0, nR = 0;
354 for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
355 {
356 for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
357 {
358 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
359 }
360 if (nC != nErrorCol && nR != nErrorRow)
361 ++nC;
362 }
363 // Now the mixed cases.
364 for ( ; nC < nCols; ++nC)
365 {
366 for ( ; nR < nRows; ++nR)
367 {
368 FormulaError nErr = pMat->GetError( nC, nR );
369 if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable))
370 { // TRUE, THEN path
371 pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
372 }
373 else
374 { // FALSE, EMPTY path, store result instead
375 lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR);
376 }
377 }
378 nR = 0;
379 }
380 xNew = new ScJumpMatrixToken( pJumpMat );
381 GetTokenMatrixMap().emplace( pCur, xNew );
382 }
383 nGlobalError = nOldGlobalError;
384 PushTokenRef( xNew );
385 // set endpoint of path for main code line
386 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
387 return;
388 }
389 break;
390 }
391
392 if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable))
393 {
394 // error, calculate 2nd argument
395 nGlobalError = FormulaError::NONE;
396 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
397 }
398 else
399 {
400 // no error, push 1st argument and continue
401 nGlobalError = nOldGlobalError;
402 PushTokenRef( xToken);
403 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
404 }
405 }
406
ScChooseJump()407 void ScInterpreter::ScChooseJump()
408 {
409 // We have to set a jump, if there was none chosen because of an error set
410 // it to endpoint.
411 bool bHaveJump = false;
412 const short* pJump = pCur->GetJump();
413 short nJumpCount = pJump[ 0 ];
414 MatrixJumpConditionToMatrix();
415 switch ( GetStackType() )
416 {
417 case svMatrix:
418 {
419 ScMatrixRef pMat = PopMatrix();
420 if ( !pMat )
421 PushIllegalParameter();
422 else
423 {
424 FormulaConstTokenRef xNew;
425 ScTokenMatrixMap::const_iterator aMapIter;
426 // DoubleError handled by JumpMatrix
427 pMat->SetErrorInterpreter( nullptr);
428 SCSIZE nCols, nRows;
429 pMat->GetDimensions( nCols, nRows );
430 if ( nCols == 0 || nRows == 0 )
431 PushIllegalParameter();
432 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
433 pCur)) != pTokenMatrixMap->end()))
434 xNew = (*aMapIter).second;
435 else
436 {
437 std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>(
438 pCur->GetOpCode(), nCols, nRows));
439 for ( SCSIZE nC=0; nC < nCols; ++nC )
440 {
441 for ( SCSIZE nR=0; nR < nRows; ++nR )
442 {
443 double fVal;
444 bool bIsValue = pMat->IsValue(nC, nR);
445 if ( bIsValue )
446 {
447 fVal = pMat->GetDouble(nC, nR);
448 bIsValue = std::isfinite( fVal );
449 if ( bIsValue )
450 {
451 fVal = ::rtl::math::approxFloor( fVal);
452 if ( (fVal < 1) || (fVal >= nJumpCount))
453 {
454 bIsValue = false;
455 fVal = CreateDoubleError(
456 FormulaError::IllegalArgument);
457 }
458 }
459 }
460 else
461 {
462 fVal = CreateDoubleError( FormulaError::NoValue);
463 }
464 if ( bIsValue )
465 {
466 pJumpMat->SetJump( nC, nR, fVal,
467 pJump[ static_cast<short>(fVal) ],
468 pJump[ nJumpCount ]);
469 }
470 else
471 {
472 pJumpMat->SetJump( nC, nR, fVal,
473 pJump[ nJumpCount ],
474 pJump[ nJumpCount ]);
475 }
476 }
477 }
478 xNew = new ScJumpMatrixToken( pJumpMat );
479 GetTokenMatrixMap().emplace(pCur, xNew);
480 }
481 if (xNew)
482 {
483 PushTokenRef( xNew);
484 // set endpoint of path for main code line
485 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
486 bHaveJump = true;
487 }
488 }
489 }
490 break;
491 default:
492 {
493 sal_Int16 nJumpIndex = GetInt16();
494 if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
495 {
496 aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] );
497 bHaveJump = true;
498 }
499 else
500 PushIllegalArgument();
501 }
502 }
503 if (!bHaveJump)
504 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
505 }
506
lcl_AdjustJumpMatrix(ScJumpMatrix * pJumpM,SCSIZE nParmCols,SCSIZE nParmRows)507 static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
508 {
509 SCSIZE nJumpCols, nJumpRows;
510 SCSIZE nResCols, nResRows;
511 SCSIZE nAdjustCols, nAdjustRows;
512 pJumpM->GetDimensions( nJumpCols, nJumpRows );
513 pJumpM->GetResMatDimensions( nResCols, nResRows );
514 if (!(( nJumpCols == 1 && nParmCols > nResCols ) ||
515 ( nJumpRows == 1 && nParmRows > nResRows )))
516 return;
517
518 if ( nJumpCols == 1 && nJumpRows == 1 )
519 {
520 nAdjustCols = std::max(nParmCols, nResCols);
521 nAdjustRows = std::max(nParmRows, nResRows);
522 }
523 else if ( nJumpCols == 1 )
524 {
525 nAdjustCols = nParmCols;
526 nAdjustRows = nResRows;
527 }
528 else
529 {
530 nAdjustCols = nResCols;
531 nAdjustRows = nParmRows;
532 }
533 pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
534 }
535
JumpMatrix(short nStackLevel)536 bool ScInterpreter::JumpMatrix( short nStackLevel )
537 {
538 pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix();
539 bool bHasResMat = pJumpMatrix->HasResultMatrix();
540 SCSIZE nC, nR;
541 if ( nStackLevel == 2 )
542 {
543 if ( aCode.HasStacked() )
544 aCode.Pop(); // pop what Jump() pushed
545 else
546 {
547 assert(!"pop goes the weasel");
548 }
549
550 if ( !bHasResMat )
551 {
552 Pop();
553 SetError( FormulaError::UnknownStackVariable );
554 }
555 else
556 {
557 pJumpMatrix->GetPos( nC, nR );
558 switch ( GetStackType() )
559 {
560 case svDouble:
561 {
562 double fVal = GetDouble();
563 if ( nGlobalError != FormulaError::NONE )
564 {
565 fVal = CreateDoubleError( nGlobalError );
566 nGlobalError = FormulaError::NONE;
567 }
568 pJumpMatrix->PutResultDouble( fVal, nC, nR );
569 }
570 break;
571 case svString:
572 {
573 svl::SharedString aStr = GetString();
574 if ( nGlobalError != FormulaError::NONE )
575 {
576 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
577 nC, nR);
578 nGlobalError = FormulaError::NONE;
579 }
580 else
581 pJumpMatrix->PutResultString(aStr, nC, nR);
582 }
583 break;
584 case svSingleRef:
585 {
586 FormulaConstTokenRef xRef = pStack[sp-1];
587 ScAddress aAdr;
588 PopSingleRef( aAdr );
589 if ( nGlobalError != FormulaError::NONE )
590 {
591 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
592 nC, nR);
593 nGlobalError = FormulaError::NONE;
594 }
595 else
596 {
597 ScRefCellValue aCell(mrDoc, aAdr);
598 if (aCell.hasEmptyValue())
599 pJumpMatrix->PutResultEmpty( nC, nR );
600 else if (aCell.hasNumeric())
601 {
602 double fVal = GetCellValue(aAdr, aCell);
603 if ( nGlobalError != FormulaError::NONE )
604 {
605 fVal = CreateDoubleError(
606 nGlobalError);
607 nGlobalError = FormulaError::NONE;
608 }
609 pJumpMatrix->PutResultDouble( fVal, nC, nR );
610 }
611 else
612 {
613 svl::SharedString aStr;
614 GetCellString(aStr, aCell);
615 if ( nGlobalError != FormulaError::NONE )
616 {
617 pJumpMatrix->PutResultDouble( CreateDoubleError(
618 nGlobalError), nC, nR);
619 nGlobalError = FormulaError::NONE;
620 }
621 else
622 pJumpMatrix->PutResultString(aStr, nC, nR);
623 }
624 }
625
626 formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
627 if (eReturnType == ParamClass::Reference)
628 {
629 /* TODO: What about error handling and do we actually
630 * need the result matrix above at all in this case? */
631 ScComplexRefData aRef;
632 aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef());
633 pJumpMatrix->GetRefList().push_back( aRef);
634 }
635 }
636 break;
637 case svDoubleRef:
638 { // upper left plus offset within matrix
639 FormulaConstTokenRef xRef = pStack[sp-1];
640 double fVal;
641 ScRange aRange;
642 PopDoubleRef( aRange );
643 if ( nGlobalError != FormulaError::NONE )
644 {
645 fVal = CreateDoubleError( nGlobalError );
646 nGlobalError = FormulaError::NONE;
647 pJumpMatrix->PutResultDouble( fVal, nC, nR );
648 }
649 else
650 {
651 // Do not modify the original range because we use it
652 // to adjust the size of the result matrix if necessary.
653 ScAddress aAdr( aRange.aStart);
654 sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC;
655 sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR;
656 if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) &&
657 aRange.aEnd.Col() != aRange.aStart.Col())
658 || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) &&
659 aRange.aEnd.Row() != aRange.aStart.Row()))
660 {
661 fVal = CreateDoubleError( FormulaError::NotAvailable );
662 pJumpMatrix->PutResultDouble( fVal, nC, nR );
663 }
664 else
665 {
666 // Replicate column and/or row of a vector if it is
667 // one. Note that this could be a range reference
668 // that in fact consists of only one cell, e.g. A1:A1
669 if (aRange.aEnd.Col() == aRange.aStart.Col())
670 nCol = aRange.aStart.Col();
671 if (aRange.aEnd.Row() == aRange.aStart.Row())
672 nRow = aRange.aStart.Row();
673 aAdr.SetCol( static_cast<SCCOL>(nCol) );
674 aAdr.SetRow( static_cast<SCROW>(nRow) );
675 ScRefCellValue aCell(mrDoc, aAdr);
676 if (aCell.hasEmptyValue())
677 pJumpMatrix->PutResultEmpty( nC, nR );
678 else if (aCell.hasNumeric())
679 {
680 double fCellVal = GetCellValue(aAdr, aCell);
681 if ( nGlobalError != FormulaError::NONE )
682 {
683 fCellVal = CreateDoubleError(
684 nGlobalError);
685 nGlobalError = FormulaError::NONE;
686 }
687 pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
688 }
689 else
690 {
691 svl::SharedString aStr;
692 GetCellString(aStr, aCell);
693 if ( nGlobalError != FormulaError::NONE )
694 {
695 pJumpMatrix->PutResultDouble( CreateDoubleError(
696 nGlobalError), nC, nR);
697 nGlobalError = FormulaError::NONE;
698 }
699 else
700 pJumpMatrix->PutResultString(aStr, nC, nR);
701 }
702 }
703 SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
704 SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
705 lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
706 }
707
708 formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16);
709 if (eReturnType == ParamClass::Reference)
710 {
711 /* TODO: What about error handling and do we actually
712 * need the result matrix above at all in this case? */
713 pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef()));
714 }
715 }
716 break;
717 case svExternalSingleRef:
718 {
719 ScExternalRefCache::TokenRef pToken;
720 PopExternalSingleRef(pToken);
721 if (nGlobalError != FormulaError::NONE)
722 {
723 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR );
724 nGlobalError = FormulaError::NONE;
725 }
726 else
727 {
728 switch (pToken->GetType())
729 {
730 case svDouble:
731 pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR );
732 break;
733 case svString:
734 pJumpMatrix->PutResultString( pToken->GetString(), nC, nR );
735 break;
736 case svEmptyCell:
737 pJumpMatrix->PutResultEmpty( nC, nR );
738 break;
739 default:
740 // svError was already handled (set by
741 // PopExternalSingleRef()) with nGlobalError
742 // above.
743 assert(!"unhandled svExternalSingleRef case");
744 pJumpMatrix->PutResultDouble( CreateDoubleError(
745 FormulaError::UnknownStackVariable), nC, nR );
746 }
747 }
748 }
749 break;
750 case svExternalDoubleRef:
751 case svMatrix:
752 { // match matrix offsets
753 double fVal;
754 ScMatrixRef pMat = GetMatrix();
755 if ( nGlobalError != FormulaError::NONE )
756 {
757 fVal = CreateDoubleError( nGlobalError );
758 nGlobalError = FormulaError::NONE;
759 pJumpMatrix->PutResultDouble( fVal, nC, nR );
760 }
761 else if ( !pMat )
762 {
763 fVal = CreateDoubleError( FormulaError::UnknownVariable );
764 pJumpMatrix->PutResultDouble( fVal, nC, nR );
765 }
766 else
767 {
768 SCSIZE nCols, nRows;
769 pMat->GetDimensions( nCols, nRows );
770 if ((nCols <= nC && nCols != 1) ||
771 (nRows <= nR && nRows != 1))
772 {
773 fVal = CreateDoubleError( FormulaError::NotAvailable );
774 pJumpMatrix->PutResultDouble( fVal, nC, nR );
775 }
776 else
777 {
778 // GetMatrix() does SetErrorInterpreter() at the
779 // matrix, do not propagate an error from
780 // matrix->GetValue() as global error.
781 pMat->SetErrorInterpreter(nullptr);
782 lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
783 }
784 lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
785 }
786 }
787 break;
788 case svError:
789 {
790 PopError();
791 double fVal = CreateDoubleError( nGlobalError);
792 nGlobalError = FormulaError::NONE;
793 pJumpMatrix->PutResultDouble( fVal, nC, nR );
794 }
795 break;
796 default:
797 {
798 Pop();
799 double fVal = CreateDoubleError( FormulaError::IllegalArgument);
800 pJumpMatrix->PutResultDouble( fVal, nC, nR );
801 }
802 }
803 }
804 }
805 bool bCont = pJumpMatrix->Next( nC, nR );
806 if ( bCont )
807 {
808 double fBool;
809 short nStart, nNext, nStop;
810 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
811 while ( bCont && nStart == nNext )
812 { // push all results that have no jump path
813 if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) )
814 {
815 // a false without path results in an empty path value
816 if ( fBool == 0.0 )
817 pJumpMatrix->PutResultEmptyPath( nC, nR );
818 else
819 pJumpMatrix->PutResultDouble( fBool, nC, nR );
820 }
821 bCont = pJumpMatrix->Next( nC, nR );
822 if ( bCont )
823 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
824 }
825 if ( bCont && nStart != nNext )
826 {
827 const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters();
828 for ( auto const & i : rParams )
829 {
830 // This is not the current state of the interpreter, so
831 // push without error, and elements' errors are coded into
832 // double.
833 PushWithoutError(*i);
834 }
835 aCode.Jump( nStart, nNext, nStop );
836 }
837 }
838 if ( !bCont )
839 { // We're done with it, throw away jump matrix, keep result.
840 // For an intermediate result of Reference use the array of references
841 // if there are more than one reference and the current ForceArray
842 // context is ReferenceOrRefArray.
843 // Else (also for a final result of Reference) use the matrix.
844 // Treat the result of a jump command as final and use the matrix (see
845 // tdf#115493 for why).
846 if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray &&
847 pJumpMatrix->GetRefList().size() > 1 &&
848 ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16) == ParamClass::Reference &&
849 !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) &&
850 aCode.PeekNextOperator())
851 {
852 FormulaTokenRef xRef = new ScRefListToken(true);
853 *(xRef->GetRefList()) = pJumpMatrix->GetRefList();
854 pJumpMatrix = nullptr;
855 Pop();
856 PushTokenRef( xRef);
857 if (pTokenMatrixMap)
858 {
859 pTokenMatrixMap->erase( pCur);
860 // There's no result matrix to remember in this case.
861 }
862 }
863 else
864 {
865 ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
866 pJumpMatrix = nullptr;
867 Pop();
868 PushMatrix( pResMat );
869 // Remove jump matrix from map and remember result matrix in case it
870 // could be reused in another path of the same condition.
871 if (pTokenMatrixMap)
872 {
873 pTokenMatrixMap->erase( pCur);
874 pTokenMatrixMap->emplace(pCur, pStack[sp-1]);
875 }
876 }
877 return true;
878 }
879 return false;
880 }
881
Compare(ScQueryOp eOp)882 double ScInterpreter::Compare( ScQueryOp eOp )
883 {
884 sc::Compare aComp;
885 aComp.meOp = eOp;
886 aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
887 for( short i = 1; i >= 0; i-- )
888 {
889 sc::Compare::Cell& rCell = aComp.maCells[i];
890
891 switch ( GetRawStackType() )
892 {
893 case svEmptyCell:
894 Pop();
895 rCell.mbEmpty = true;
896 break;
897 case svMissing:
898 case svDouble:
899 rCell.mfValue = GetDouble();
900 rCell.mbValue = true;
901 break;
902 case svString:
903 rCell.maStr = GetString();
904 rCell.mbValue = false;
905 break;
906 case svDoubleRef :
907 case svSingleRef :
908 {
909 ScAddress aAdr;
910 if ( !PopDoubleRefOrSingleRef( aAdr ) )
911 break;
912 ScRefCellValue aCell(mrDoc, aAdr);
913 if (aCell.hasEmptyValue())
914 rCell.mbEmpty = true;
915 else if (aCell.hasString())
916 {
917 svl::SharedString aStr;
918 GetCellString(aStr, aCell);
919 rCell.maStr = aStr;
920 rCell.mbValue = false;
921 }
922 else
923 {
924 rCell.mfValue = GetCellValue(aAdr, aCell);
925 rCell.mbValue = true;
926 }
927 }
928 break;
929 case svExternalSingleRef:
930 {
931 ScMatrixRef pMat = GetMatrix();
932 if (!pMat)
933 {
934 SetError( FormulaError::IllegalParameter);
935 break;
936 }
937
938 SCSIZE nC, nR;
939 pMat->GetDimensions(nC, nR);
940 if (!nC || !nR)
941 {
942 SetError( FormulaError::IllegalParameter);
943 break;
944 }
945 if (pMat->IsEmpty(0, 0))
946 rCell.mbEmpty = true;
947 else if (pMat->IsStringOrEmpty(0, 0))
948 {
949 rCell.maStr = pMat->GetString(0, 0);
950 rCell.mbValue = false;
951 }
952 else
953 {
954 rCell.mfValue = pMat->GetDouble(0, 0);
955 rCell.mbValue = true;
956 }
957 }
958 break;
959 case svExternalDoubleRef:
960 // TODO: Find out how to handle this...
961 // Xcl generates a position dependent intersection using
962 // col/row, as it seems to do for all range references, not
963 // only in compare context. We'd need a general implementation
964 // for that behavior similar to svDoubleRef in scalar and array
965 // mode. Which also means we'd have to change all places where
966 // it currently is handled along with svMatrix.
967 default:
968 PopError();
969 SetError( FormulaError::IllegalParameter);
970 break;
971 }
972 }
973 if( nGlobalError != FormulaError::NONE )
974 return 0;
975 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
976 return sc::CompareFunc(aComp);
977 }
978
CompareMat(ScQueryOp eOp,sc::CompareOptions * pOptions)979 sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions )
980 {
981 sc::Compare aComp;
982 aComp.meOp = eOp;
983 aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase();
984 sc::RangeMatrix aMat[2];
985 ScAddress aAdr;
986 for( short i = 1; i >= 0; i-- )
987 {
988 sc::Compare::Cell& rCell = aComp.maCells[i];
989
990 switch (GetRawStackType())
991 {
992 case svEmptyCell:
993 Pop();
994 rCell.mbEmpty = true;
995 break;
996 case svMissing:
997 case svDouble:
998 rCell.mfValue = GetDouble();
999 rCell.mbValue = true;
1000 break;
1001 case svString:
1002 rCell.maStr = GetString();
1003 rCell.mbValue = false;
1004 break;
1005 case svSingleRef:
1006 {
1007 PopSingleRef( aAdr );
1008 ScRefCellValue aCell(mrDoc, aAdr);
1009 if (aCell.hasEmptyValue())
1010 rCell.mbEmpty = true;
1011 else if (aCell.hasString())
1012 {
1013 svl::SharedString aStr;
1014 GetCellString(aStr, aCell);
1015 rCell.maStr = aStr;
1016 rCell.mbValue = false;
1017 }
1018 else
1019 {
1020 rCell.mfValue = GetCellValue(aAdr, aCell);
1021 rCell.mbValue = true;
1022 }
1023 }
1024 break;
1025 case svExternalSingleRef:
1026 case svExternalDoubleRef:
1027 case svDoubleRef:
1028 case svMatrix:
1029 aMat[i] = GetRangeMatrix();
1030 if (!aMat[i].mpMat)
1031 SetError( FormulaError::IllegalParameter);
1032 else
1033 aMat[i].mpMat->SetErrorInterpreter(nullptr);
1034 // errors are transported as DoubleError inside matrix
1035 break;
1036 default:
1037 PopError();
1038 SetError( FormulaError::IllegalParameter);
1039 break;
1040 }
1041 }
1042
1043 sc::RangeMatrix aRes;
1044
1045 if (nGlobalError != FormulaError::NONE)
1046 {
1047 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1048 return aRes;
1049 }
1050
1051 if (aMat[0].mpMat && aMat[1].mpMat)
1052 {
1053 SCSIZE nC0, nC1;
1054 SCSIZE nR0, nR1;
1055 aMat[0].mpMat->GetDimensions(nC0, nR0);
1056 aMat[1].mpMat->GetDimensions(nC1, nR1);
1057 SCSIZE nC = std::max( nC0, nC1 );
1058 SCSIZE nR = std::max( nR0, nR1 );
1059 aRes.mpMat = GetNewMat( nC, nR, /*bEmpty*/true );
1060 if (!aRes.mpMat)
1061 return aRes;
1062 for ( SCSIZE j=0; j<nC; j++ )
1063 {
1064 for ( SCSIZE k=0; k<nR; k++ )
1065 {
1066 SCSIZE nCol = j, nRow = k;
1067 if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
1068 aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
1069 {
1070 for ( short i=1; i>=0; i-- )
1071 {
1072 sc::Compare::Cell& rCell = aComp.maCells[i];
1073
1074 if (aMat[i].mpMat->IsStringOrEmpty(j, k))
1075 {
1076 rCell.mbValue = false;
1077 rCell.maStr = aMat[i].mpMat->GetString(j, k);
1078 rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
1079 }
1080 else
1081 {
1082 rCell.mbValue = true;
1083 rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
1084 rCell.mbEmpty = false;
1085 }
1086 }
1087 aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k);
1088 }
1089 else
1090 aRes.mpMat->PutError( FormulaError::NoValue, j, k);
1091 }
1092 }
1093
1094 switch (eOp)
1095 {
1096 case SC_EQUAL:
1097 aRes.mpMat->CompareEqual();
1098 break;
1099 case SC_LESS:
1100 aRes.mpMat->CompareLess();
1101 break;
1102 case SC_GREATER:
1103 aRes.mpMat->CompareGreater();
1104 break;
1105 case SC_LESS_EQUAL:
1106 aRes.mpMat->CompareLessEqual();
1107 break;
1108 case SC_GREATER_EQUAL:
1109 aRes.mpMat->CompareGreaterEqual();
1110 break;
1111 case SC_NOT_EQUAL:
1112 aRes.mpMat->CompareNotEqual();
1113 break;
1114 default:
1115 SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp));
1116 aRes.mpMat.reset();
1117 return aRes;
1118 }
1119 }
1120 else if (aMat[0].mpMat || aMat[1].mpMat)
1121 {
1122 size_t i = ( aMat[0].mpMat ? 0 : 1);
1123
1124 aRes.mnCol1 = aMat[i].mnCol1;
1125 aRes.mnRow1 = aMat[i].mnRow1;
1126 aRes.mnTab1 = aMat[i].mnTab1;
1127 aRes.mnCol2 = aMat[i].mnCol2;
1128 aRes.mnRow2 = aMat[i].mnRow2;
1129 aRes.mnTab2 = aMat[i].mnTab2;
1130
1131 ScMatrix& rMat = *aMat[i].mpMat;
1132 aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
1133 if (!aRes.mpMat)
1134 return aRes;
1135 }
1136
1137 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
1138 return aRes;
1139 }
1140
QueryMat(const ScMatrixRef & pMat,sc::CompareOptions & rOptions)1141 ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions )
1142 {
1143 SvNumFormatType nSaveCurFmtType = nCurFmtType;
1144 SvNumFormatType nSaveFuncFmtType = nFuncFmtType;
1145 PushMatrix( pMat);
1146 const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
1147 if (rItem.meType == ScQueryEntry::ByString)
1148 PushString(rItem.maString.getString());
1149 else
1150 PushDouble(rItem.mfVal);
1151 ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
1152 nCurFmtType = nSaveCurFmtType;
1153 nFuncFmtType = nSaveFuncFmtType;
1154 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
1155 {
1156 SetError( FormulaError::IllegalParameter);
1157 return pResultMatrix;
1158 }
1159
1160 return pResultMatrix;
1161 }
1162
ScEqual()1163 void ScInterpreter::ScEqual()
1164 {
1165 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1166 {
1167 sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
1168 if (!aMat.mpMat)
1169 {
1170 PushIllegalParameter();
1171 return;
1172 }
1173
1174 PushMatrix(aMat);
1175 }
1176 else
1177 PushInt( int(Compare( SC_EQUAL) == 0) );
1178 }
1179
ScNotEqual()1180 void ScInterpreter::ScNotEqual()
1181 {
1182 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1183 {
1184 sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
1185 if (!aMat.mpMat)
1186 {
1187 PushIllegalParameter();
1188 return;
1189 }
1190
1191 PushMatrix(aMat);
1192 }
1193 else
1194 PushInt( int(Compare( SC_NOT_EQUAL) != 0) );
1195 }
1196
ScLess()1197 void ScInterpreter::ScLess()
1198 {
1199 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1200 {
1201 sc::RangeMatrix aMat = CompareMat(SC_LESS);
1202 if (!aMat.mpMat)
1203 {
1204 PushIllegalParameter();
1205 return;
1206 }
1207
1208 PushMatrix(aMat);
1209 }
1210 else
1211 PushInt( int(Compare( SC_LESS) < 0) );
1212 }
1213
ScGreater()1214 void ScInterpreter::ScGreater()
1215 {
1216 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1217 {
1218 sc::RangeMatrix aMat = CompareMat(SC_GREATER);
1219 if (!aMat.mpMat)
1220 {
1221 PushIllegalParameter();
1222 return;
1223 }
1224
1225 PushMatrix(aMat);
1226 }
1227 else
1228 PushInt( int(Compare( SC_GREATER) > 0) );
1229 }
1230
ScLessEqual()1231 void ScInterpreter::ScLessEqual()
1232 {
1233 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1234 {
1235 sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
1236 if (!aMat.mpMat)
1237 {
1238 PushIllegalParameter();
1239 return;
1240 }
1241
1242 PushMatrix(aMat);
1243 }
1244 else
1245 PushInt( int(Compare( SC_LESS_EQUAL) <= 0) );
1246 }
1247
ScGreaterEqual()1248 void ScInterpreter::ScGreaterEqual()
1249 {
1250 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1251 {
1252 sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
1253 if (!aMat.mpMat)
1254 {
1255 PushIllegalParameter();
1256 return;
1257 }
1258
1259 PushMatrix(aMat);
1260 }
1261 else
1262 PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) );
1263 }
1264
ScAnd()1265 void ScInterpreter::ScAnd()
1266 {
1267 nFuncFmtType = SvNumFormatType::LOGICAL;
1268 short nParamCount = GetByte();
1269 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1270 return;
1271
1272 bool bHaveValue = false;
1273 bool bRes = true;
1274 size_t nRefInList = 0;
1275 while( nParamCount-- > 0)
1276 {
1277 if ( nGlobalError == FormulaError::NONE )
1278 {
1279 switch ( GetStackType() )
1280 {
1281 case svDouble :
1282 bHaveValue = true;
1283 bRes &= ( PopDouble() != 0.0 );
1284 break;
1285 case svString :
1286 Pop();
1287 SetError( FormulaError::NoValue );
1288 break;
1289 case svSingleRef :
1290 {
1291 ScAddress aAdr;
1292 PopSingleRef( aAdr );
1293 if ( nGlobalError == FormulaError::NONE )
1294 {
1295 ScRefCellValue aCell(mrDoc, aAdr);
1296 if (aCell.hasNumeric())
1297 {
1298 bHaveValue = true;
1299 bRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
1300 }
1301 // else: Xcl raises no error here
1302 }
1303 }
1304 break;
1305 case svDoubleRef:
1306 case svRefList:
1307 {
1308 ScRange aRange;
1309 PopDoubleRef( aRange, nParamCount, nRefInList);
1310 if ( nGlobalError == FormulaError::NONE )
1311 {
1312 double fVal;
1313 FormulaError nErr = FormulaError::NONE;
1314 ScValueIterator aValIter( mrDoc, aRange );
1315 if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE )
1316 {
1317 bHaveValue = true;
1318 do
1319 {
1320 bRes &= ( fVal != 0.0 );
1321 } while ( (nErr == FormulaError::NONE) &&
1322 aValIter.GetNext( fVal, nErr ) );
1323 }
1324 SetError( nErr );
1325 }
1326 }
1327 break;
1328 case svExternalSingleRef:
1329 case svExternalDoubleRef:
1330 case svMatrix:
1331 {
1332 ScMatrixRef pMat = GetMatrix();
1333 if ( pMat )
1334 {
1335 bHaveValue = true;
1336 double fVal = pMat->And();
1337 FormulaError nErr = GetDoubleErrorValue( fVal );
1338 if ( nErr != FormulaError::NONE )
1339 {
1340 SetError( nErr );
1341 bRes = false;
1342 }
1343 else
1344 bRes &= (fVal != 0.0);
1345 }
1346 // else: GetMatrix did set FormulaError::IllegalParameter
1347 }
1348 break;
1349 default:
1350 PopError();
1351 SetError( FormulaError::IllegalParameter);
1352 }
1353 }
1354 else
1355 Pop();
1356 }
1357 if ( bHaveValue )
1358 PushInt( int(bRes) );
1359 else
1360 PushNoValue();
1361 }
1362
ScOr()1363 void ScInterpreter::ScOr()
1364 {
1365 nFuncFmtType = SvNumFormatType::LOGICAL;
1366 short nParamCount = GetByte();
1367 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1368 return;
1369
1370 bool bHaveValue = false;
1371 bool bRes = false;
1372 size_t nRefInList = 0;
1373 while( nParamCount-- > 0)
1374 {
1375 if ( nGlobalError == FormulaError::NONE )
1376 {
1377 switch ( GetStackType() )
1378 {
1379 case svDouble :
1380 bHaveValue = true;
1381 bRes |= ( PopDouble() != 0.0 );
1382 break;
1383 case svString :
1384 Pop();
1385 SetError( FormulaError::NoValue );
1386 break;
1387 case svSingleRef :
1388 {
1389 ScAddress aAdr;
1390 PopSingleRef( aAdr );
1391 if ( nGlobalError == FormulaError::NONE )
1392 {
1393 ScRefCellValue aCell(mrDoc, aAdr);
1394 if (aCell.hasNumeric())
1395 {
1396 bHaveValue = true;
1397 bRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
1398 }
1399 // else: Xcl raises no error here
1400 }
1401 }
1402 break;
1403 case svDoubleRef:
1404 case svRefList:
1405 {
1406 ScRange aRange;
1407 PopDoubleRef( aRange, nParamCount, nRefInList);
1408 if ( nGlobalError == FormulaError::NONE )
1409 {
1410 double fVal;
1411 FormulaError nErr = FormulaError::NONE;
1412 ScValueIterator aValIter( mrDoc, aRange );
1413 if ( aValIter.GetFirst( fVal, nErr ) )
1414 {
1415 bHaveValue = true;
1416 do
1417 {
1418 bRes |= ( fVal != 0.0 );
1419 } while ( (nErr == FormulaError::NONE) &&
1420 aValIter.GetNext( fVal, nErr ) );
1421 }
1422 SetError( nErr );
1423 }
1424 }
1425 break;
1426 case svExternalSingleRef:
1427 case svExternalDoubleRef:
1428 case svMatrix:
1429 {
1430 bHaveValue = true;
1431 ScMatrixRef pMat = GetMatrix();
1432 if ( pMat )
1433 {
1434 bHaveValue = true;
1435 double fVal = pMat->Or();
1436 FormulaError nErr = GetDoubleErrorValue( fVal );
1437 if ( nErr != FormulaError::NONE )
1438 {
1439 SetError( nErr );
1440 bRes = false;
1441 }
1442 else
1443 bRes |= (fVal != 0.0);
1444 }
1445 // else: GetMatrix did set FormulaError::IllegalParameter
1446 }
1447 break;
1448 default:
1449 PopError();
1450 SetError( FormulaError::IllegalParameter);
1451 }
1452 }
1453 else
1454 Pop();
1455 }
1456 if ( bHaveValue )
1457 PushInt( int(bRes) );
1458 else
1459 PushNoValue();
1460 }
1461
ScXor()1462 void ScInterpreter::ScXor()
1463 {
1464
1465 nFuncFmtType = SvNumFormatType::LOGICAL;
1466 short nParamCount = GetByte();
1467 if ( !MustHaveParamCountMin( nParamCount, 1 ) )
1468 return;
1469
1470 bool bHaveValue = false;
1471 bool bRes = false;
1472 size_t nRefInList = 0;
1473 while( nParamCount-- > 0)
1474 {
1475 if ( nGlobalError == FormulaError::NONE )
1476 {
1477 switch ( GetStackType() )
1478 {
1479 case svDouble :
1480 bHaveValue = true;
1481 bRes ^= ( PopDouble() != 0.0 );
1482 break;
1483 case svString :
1484 Pop();
1485 SetError( FormulaError::NoValue );
1486 break;
1487 case svSingleRef :
1488 {
1489 ScAddress aAdr;
1490 PopSingleRef( aAdr );
1491 if ( nGlobalError == FormulaError::NONE )
1492 {
1493 ScRefCellValue aCell(mrDoc, aAdr);
1494 if (aCell.hasNumeric())
1495 {
1496 bHaveValue = true;
1497 bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
1498 }
1499 /* TODO: set error? Excel doesn't have XOR, but
1500 * doesn't set an error in this case for AND and
1501 * OR. */
1502 }
1503 }
1504 break;
1505 case svDoubleRef:
1506 case svRefList:
1507 {
1508 ScRange aRange;
1509 PopDoubleRef( aRange, nParamCount, nRefInList);
1510 if ( nGlobalError == FormulaError::NONE )
1511 {
1512 double fVal;
1513 FormulaError nErr = FormulaError::NONE;
1514 ScValueIterator aValIter( mrDoc, aRange );
1515 if ( aValIter.GetFirst( fVal, nErr ) )
1516 {
1517 bHaveValue = true;
1518 do
1519 {
1520 bRes ^= ( fVal != 0.0 );
1521 } while ( (nErr == FormulaError::NONE) &&
1522 aValIter.GetNext( fVal, nErr ) );
1523 }
1524 SetError( nErr );
1525 }
1526 }
1527 break;
1528 case svExternalSingleRef:
1529 case svExternalDoubleRef:
1530 case svMatrix:
1531 {
1532 bHaveValue = true;
1533 ScMatrixRef pMat = GetMatrix();
1534 if ( pMat )
1535 {
1536 bHaveValue = true;
1537 double fVal = pMat->Xor();
1538 FormulaError nErr = GetDoubleErrorValue( fVal );
1539 if ( nErr != FormulaError::NONE )
1540 {
1541 SetError( nErr );
1542 bRes = false;
1543 }
1544 else
1545 bRes ^= ( fVal != 0.0 );
1546 }
1547 // else: GetMatrix did set FormulaError::IllegalParameter
1548 }
1549 break;
1550 default:
1551 PopError();
1552 SetError( FormulaError::IllegalParameter);
1553 }
1554 }
1555 else
1556 Pop();
1557 }
1558 if ( bHaveValue )
1559 PushInt( int(bRes) );
1560 else
1561 PushNoValue();
1562 }
1563
ScNeg()1564 void ScInterpreter::ScNeg()
1565 {
1566 // Simple negation doesn't change current format type to number, keep
1567 // current type.
1568 nFuncFmtType = nCurFmtType;
1569 switch ( GetStackType() )
1570 {
1571 case svMatrix :
1572 {
1573 ScMatrixRef pMat = GetMatrix();
1574 if ( !pMat )
1575 PushIllegalParameter();
1576 else
1577 {
1578 SCSIZE nC, nR;
1579 pMat->GetDimensions( nC, nR );
1580 ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true );
1581 if ( !pResMat )
1582 PushIllegalArgument();
1583 else
1584 {
1585 pMat->NegOp( *pResMat);
1586 PushMatrix( pResMat );
1587 }
1588 }
1589 }
1590 break;
1591 default:
1592 PushDouble( -GetDouble() );
1593 }
1594 }
1595
ScPercentSign()1596 void ScInterpreter::ScPercentSign()
1597 {
1598 nFuncFmtType = SvNumFormatType::PERCENT;
1599 const FormulaToken* pSaveCur = pCur;
1600 sal_uInt8 nSavePar = cPar;
1601 PushInt( 100 );
1602 cPar = 2;
1603 FormulaByteToken aDivOp( ocDiv, cPar );
1604 pCur = &aDivOp;
1605 ScDiv();
1606 pCur = pSaveCur;
1607 cPar = nSavePar;
1608 }
1609
ScNot()1610 void ScInterpreter::ScNot()
1611 {
1612 nFuncFmtType = SvNumFormatType::LOGICAL;
1613 switch ( GetStackType() )
1614 {
1615 case svMatrix :
1616 {
1617 ScMatrixRef pMat = GetMatrix();
1618 if ( !pMat )
1619 PushIllegalParameter();
1620 else
1621 {
1622 SCSIZE nC, nR;
1623 pMat->GetDimensions( nC, nR );
1624 ScMatrixRef pResMat = GetNewMat( nC, nR, /*bEmpty*/true);
1625 if ( !pResMat )
1626 PushIllegalArgument();
1627 else
1628 {
1629 pMat->NotOp( *pResMat);
1630 PushMatrix( pResMat );
1631 }
1632 }
1633 }
1634 break;
1635 default:
1636 PushInt( int(GetDouble() == 0.0) );
1637 }
1638 }
1639
ScBitAnd()1640 void ScInterpreter::ScBitAnd()
1641 {
1642
1643 if ( !MustHaveParamCount( GetByte(), 2 ) )
1644 return;
1645
1646 double num1 = ::rtl::math::approxFloor( GetDouble());
1647 double num2 = ::rtl::math::approxFloor( GetDouble());
1648 if ( (num1 >= n2power48) || (num1 < 0) ||
1649 (num2 >= n2power48) || (num2 < 0))
1650 PushIllegalArgument();
1651 else
1652 PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2));
1653 }
1654
ScBitOr()1655 void ScInterpreter::ScBitOr()
1656 {
1657
1658 if ( !MustHaveParamCount( GetByte(), 2 ) )
1659 return;
1660
1661 double num1 = ::rtl::math::approxFloor( GetDouble());
1662 double num2 = ::rtl::math::approxFloor( GetDouble());
1663 if ( (num1 >= n2power48) || (num1 < 0) ||
1664 (num2 >= n2power48) || (num2 < 0))
1665 PushIllegalArgument();
1666 else
1667 PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2));
1668 }
1669
ScBitXor()1670 void ScInterpreter::ScBitXor()
1671 {
1672
1673 if ( !MustHaveParamCount( GetByte(), 2 ) )
1674 return;
1675
1676 double num1 = ::rtl::math::approxFloor( GetDouble());
1677 double num2 = ::rtl::math::approxFloor( GetDouble());
1678 if ( (num1 >= n2power48) || (num1 < 0) ||
1679 (num2 >= n2power48) || (num2 < 0))
1680 PushIllegalArgument();
1681 else
1682 PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2));
1683 }
1684
ScBitLshift()1685 void ScInterpreter::ScBitLshift()
1686 {
1687
1688 if ( !MustHaveParamCount( GetByte(), 2 ) )
1689 return;
1690
1691 double fShift = ::rtl::math::approxFloor( GetDouble());
1692 double num = ::rtl::math::approxFloor( GetDouble());
1693 if ((num >= n2power48) || (num < 0))
1694 PushIllegalArgument();
1695 else
1696 {
1697 double fRes;
1698 if (fShift < 0)
1699 fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
1700 else if (fShift == 0)
1701 fRes = num;
1702 else
1703 fRes = num * pow( 2.0, fShift);
1704 PushDouble( fRes);
1705 }
1706 }
1707
ScBitRshift()1708 void ScInterpreter::ScBitRshift()
1709 {
1710
1711 if ( !MustHaveParamCount( GetByte(), 2 ) )
1712 return;
1713
1714 double fShift = ::rtl::math::approxFloor( GetDouble());
1715 double num = ::rtl::math::approxFloor( GetDouble());
1716 if ((num >= n2power48) || (num < 0))
1717 PushIllegalArgument();
1718 else
1719 {
1720 double fRes;
1721 if (fShift < 0)
1722 fRes = num * pow( 2.0, -fShift);
1723 else if (fShift == 0)
1724 fRes = num;
1725 else
1726 fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
1727 PushDouble( fRes);
1728 }
1729 }
1730
ScPi()1731 void ScInterpreter::ScPi()
1732 {
1733 PushDouble(F_PI);
1734 }
1735
ScRandomImpl(const std::function<double (double fFirst,double fLast)> & RandomFunc,double fFirst,double fLast)1736 void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc,
1737 double fFirst, double fLast )
1738 {
1739 if (bMatrixFormula)
1740 {
1741 SCCOL nCols = 0;
1742 SCROW nRows = 0;
1743 if (pMyFormulaCell)
1744 pMyFormulaCell->GetMatColsRows( nCols, nRows);
1745
1746 if (nCols == 1 && nRows == 1)
1747 {
1748 // For compatibility with existing
1749 // com.sun.star.sheet.FunctionAccess.callFunction() calls that per
1750 // default are executed in array context unless
1751 // FA.setPropertyValue("IsArrayFunction",False) was set, return a
1752 // scalar double instead of a 1x1 matrix object. tdf#128218
1753 PushDouble( RandomFunc( fFirst, fLast));
1754 return;
1755 }
1756
1757 // ScViewFunc::EnterMatrix() might be asking for
1758 // ScFormulaCell::GetResultDimensions(), which here are none so create
1759 // a 1x1 matrix at least which exactly is the case when EnterMatrix()
1760 // asks for a not selected range.
1761 if (nCols == 0)
1762 nCols = 1;
1763 if (nRows == 0)
1764 nRows = 1;
1765 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows), /*bEmpty*/true );
1766 if (!pResMat)
1767 PushError( FormulaError::MatrixSize);
1768 else
1769 {
1770 for (SCCOL i=0; i < nCols; ++i)
1771 {
1772 for (SCROW j=0; j < nRows; ++j)
1773 {
1774 pResMat->PutDouble( RandomFunc( fFirst, fLast),
1775 static_cast<SCSIZE>(i), static_cast<SCSIZE>(j));
1776 }
1777 }
1778 PushMatrix( pResMat);
1779 }
1780 }
1781 else
1782 {
1783 PushDouble( RandomFunc( fFirst, fLast));
1784 }
1785 }
1786
ScRandom()1787 void ScInterpreter::ScRandom()
1788 {
1789 auto RandomFunc = []( double, double )
1790 {
1791 return comphelper::rng::uniform_real_distribution();
1792 };
1793 ScRandomImpl( RandomFunc, 0.0, 0.0);
1794 }
1795
ScRandbetween()1796 void ScInterpreter::ScRandbetween()
1797 {
1798 if (!MustHaveParamCount( GetByte(), 2))
1799 return;
1800
1801 // Same like scaddins/source/analysis/analysis.cxx
1802 // AnalysisAddIn::getRandbetween()
1803 double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1804 double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up);
1805 if (nGlobalError != FormulaError::NONE || fMin > fMax)
1806 {
1807 PushIllegalArgument();
1808 return;
1809 }
1810 fMax = std::nextafter( fMax+1, -DBL_MAX);
1811 auto RandomFunc = []( double fFirst, double fLast )
1812 {
1813 return floor( comphelper::rng::uniform_real_distribution( fFirst, fLast));
1814 };
1815 ScRandomImpl( RandomFunc, fMin, fMax);
1816 }
1817
ScTrue()1818 void ScInterpreter::ScTrue()
1819 {
1820 nFuncFmtType = SvNumFormatType::LOGICAL;
1821 PushInt(1);
1822 }
1823
ScFalse()1824 void ScInterpreter::ScFalse()
1825 {
1826 nFuncFmtType = SvNumFormatType::LOGICAL;
1827 PushInt(0);
1828 }
1829
ScDeg()1830 void ScInterpreter::ScDeg()
1831 {
1832 PushDouble(basegfx::rad2deg(GetDouble()));
1833 }
1834
ScRad()1835 void ScInterpreter::ScRad()
1836 {
1837 PushDouble(basegfx::deg2rad(GetDouble()));
1838 }
1839
ScSin()1840 void ScInterpreter::ScSin()
1841 {
1842 PushDouble(::rtl::math::sin(GetDouble()));
1843 }
1844
ScCos()1845 void ScInterpreter::ScCos()
1846 {
1847 PushDouble(::rtl::math::cos(GetDouble()));
1848 }
1849
ScTan()1850 void ScInterpreter::ScTan()
1851 {
1852 PushDouble(::rtl::math::tan(GetDouble()));
1853 }
1854
ScCot()1855 void ScInterpreter::ScCot()
1856 {
1857 PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1858 }
1859
ScArcSin()1860 void ScInterpreter::ScArcSin()
1861 {
1862 PushDouble(asin(GetDouble()));
1863 }
1864
ScArcCos()1865 void ScInterpreter::ScArcCos()
1866 {
1867 PushDouble(acos(GetDouble()));
1868 }
1869
ScArcTan()1870 void ScInterpreter::ScArcTan()
1871 {
1872 PushDouble(atan(GetDouble()));
1873 }
1874
ScArcCot()1875 void ScInterpreter::ScArcCot()
1876 {
1877 PushDouble((F_PI2) - atan(GetDouble()));
1878 }
1879
ScSinHyp()1880 void ScInterpreter::ScSinHyp()
1881 {
1882 PushDouble(sinh(GetDouble()));
1883 }
1884
ScCosHyp()1885 void ScInterpreter::ScCosHyp()
1886 {
1887 PushDouble(cosh(GetDouble()));
1888 }
1889
ScTanHyp()1890 void ScInterpreter::ScTanHyp()
1891 {
1892 PushDouble(tanh(GetDouble()));
1893 }
1894
ScCotHyp()1895 void ScInterpreter::ScCotHyp()
1896 {
1897 PushDouble(1.0 / tanh(GetDouble()));
1898 }
1899
ScArcSinHyp()1900 void ScInterpreter::ScArcSinHyp()
1901 {
1902 PushDouble( ::rtl::math::asinh( GetDouble()));
1903 }
1904
ScArcCosHyp()1905 void ScInterpreter::ScArcCosHyp()
1906 {
1907 double fVal = GetDouble();
1908 if (fVal < 1.0)
1909 PushIllegalArgument();
1910 else
1911 PushDouble( ::rtl::math::acosh( fVal));
1912 }
1913
ScArcTanHyp()1914 void ScInterpreter::ScArcTanHyp()
1915 {
1916 double fVal = GetDouble();
1917 if (fabs(fVal) >= 1.0)
1918 PushIllegalArgument();
1919 else
1920 PushDouble( ::rtl::math::atanh( fVal));
1921 }
1922
ScArcCotHyp()1923 void ScInterpreter::ScArcCotHyp()
1924 {
1925 double nVal = GetDouble();
1926 if (fabs(nVal) <= 1.0)
1927 PushIllegalArgument();
1928 else
1929 PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
1930 }
1931
ScCosecant()1932 void ScInterpreter::ScCosecant()
1933 {
1934 PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
1935 }
1936
ScSecant()1937 void ScInterpreter::ScSecant()
1938 {
1939 PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
1940 }
1941
ScCosecantHyp()1942 void ScInterpreter::ScCosecantHyp()
1943 {
1944 PushDouble(1.0 / sinh(GetDouble()));
1945 }
1946
ScSecantHyp()1947 void ScInterpreter::ScSecantHyp()
1948 {
1949 PushDouble(1.0 / cosh(GetDouble()));
1950 }
1951
ScExp()1952 void ScInterpreter::ScExp()
1953 {
1954 PushDouble(exp(GetDouble()));
1955 }
1956
ScSqrt()1957 void ScInterpreter::ScSqrt()
1958 {
1959 double fVal = GetDouble();
1960 if (fVal >= 0.0)
1961 PushDouble(sqrt(fVal));
1962 else
1963 PushIllegalArgument();
1964 }
1965
ScIsEmpty()1966 void ScInterpreter::ScIsEmpty()
1967 {
1968 short nRes = 0;
1969 nFuncFmtType = SvNumFormatType::LOGICAL;
1970 switch ( GetRawStackType() )
1971 {
1972 case svEmptyCell:
1973 {
1974 FormulaConstTokenRef p = PopToken();
1975 if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
1976 nRes = 1;
1977 }
1978 break;
1979 case svDoubleRef :
1980 case svSingleRef :
1981 {
1982 ScAddress aAdr;
1983 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1984 break;
1985 // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that
1986 // may treat ="" in the referenced cell as blank for Excel
1987 // interoperability.
1988 ScRefCellValue aCell(mrDoc, aAdr);
1989 if (aCell.meType == CELLTYPE_NONE)
1990 nRes = 1;
1991 }
1992 break;
1993 case svExternalSingleRef:
1994 case svExternalDoubleRef:
1995 case svMatrix:
1996 {
1997 ScMatrixRef pMat = GetMatrix();
1998 if ( !pMat )
1999 ; // nothing
2000 else if ( !pJumpMatrix )
2001 nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0;
2002 else
2003 {
2004 SCSIZE nCols, nRows, nC, nR;
2005 pMat->GetDimensions( nCols, nRows);
2006 pJumpMatrix->GetPos( nC, nR);
2007 if ( nC < nCols && nR < nRows )
2008 nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0;
2009 // else: false, not empty (which is what Xcl does)
2010 }
2011 }
2012 break;
2013 default:
2014 Pop();
2015 }
2016 nGlobalError = FormulaError::NONE;
2017 PushInt( nRes );
2018 }
2019
IsString()2020 bool ScInterpreter::IsString()
2021 {
2022 nFuncFmtType = SvNumFormatType::LOGICAL;
2023 bool bRes = false;
2024 switch ( GetRawStackType() )
2025 {
2026 case svString:
2027 Pop();
2028 bRes = true;
2029 break;
2030 case svDoubleRef :
2031 case svSingleRef :
2032 {
2033 ScAddress aAdr;
2034 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2035 break;
2036
2037 ScRefCellValue aCell(mrDoc, aAdr);
2038 if (GetCellErrCode(aCell) == FormulaError::NONE)
2039 {
2040 switch (aCell.meType)
2041 {
2042 case CELLTYPE_STRING :
2043 case CELLTYPE_EDIT :
2044 bRes = true;
2045 break;
2046 case CELLTYPE_FORMULA :
2047 bRes = (!aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
2048 break;
2049 default:
2050 ; // nothing
2051 }
2052 }
2053 }
2054 break;
2055 case svExternalSingleRef:
2056 {
2057 ScExternalRefCache::TokenRef pToken;
2058 PopExternalSingleRef(pToken);
2059 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svString)
2060 bRes = true;
2061 }
2062 break;
2063 case svExternalDoubleRef:
2064 case svMatrix:
2065 {
2066 ScMatrixRef pMat = GetMatrix();
2067 if ( !pMat )
2068 ; // nothing
2069 else if ( !pJumpMatrix )
2070 bRes = pMat->IsStringOrEmpty(0, 0) && !pMat->IsEmpty(0, 0);
2071 else
2072 {
2073 SCSIZE nCols, nRows, nC, nR;
2074 pMat->GetDimensions( nCols, nRows);
2075 pJumpMatrix->GetPos( nC, nR);
2076 if ( nC < nCols && nR < nRows )
2077 bRes = pMat->IsStringOrEmpty( nC, nR) && !pMat->IsEmpty( nC, nR);
2078 }
2079 }
2080 break;
2081 default:
2082 Pop();
2083 }
2084 nGlobalError = FormulaError::NONE;
2085 return bRes;
2086 }
2087
ScIsString()2088 void ScInterpreter::ScIsString()
2089 {
2090 PushInt( int(IsString()) );
2091 }
2092
ScIsNonString()2093 void ScInterpreter::ScIsNonString()
2094 {
2095 PushInt( int(!IsString()) );
2096 }
2097
ScIsLogical()2098 void ScInterpreter::ScIsLogical()
2099 {
2100 bool bRes = false;
2101 switch ( GetStackType() )
2102 {
2103 case svDoubleRef :
2104 case svSingleRef :
2105 {
2106 ScAddress aAdr;
2107 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2108 break;
2109
2110 ScRefCellValue aCell(mrDoc, aAdr);
2111 if (GetCellErrCode(aCell) == FormulaError::NONE)
2112 {
2113 if (aCell.hasNumeric())
2114 {
2115 sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2116 bRes = (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL);
2117 }
2118 }
2119 }
2120 break;
2121 case svMatrix:
2122 {
2123 double fVal;
2124 svl::SharedString aStr;
2125 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
2126 bRes = (nMatValType == ScMatValType::Boolean);
2127 }
2128 break;
2129 default:
2130 PopError();
2131 if ( nGlobalError == FormulaError::NONE )
2132 bRes = ( nCurFmtType == SvNumFormatType::LOGICAL );
2133 }
2134 nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL;
2135 nGlobalError = FormulaError::NONE;
2136 PushInt( int(bRes) );
2137 }
2138
ScType()2139 void ScInterpreter::ScType()
2140 {
2141 short nType = 0;
2142 switch ( GetStackType() )
2143 {
2144 case svDoubleRef :
2145 case svSingleRef :
2146 {
2147 ScAddress aAdr;
2148 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2149 break;
2150
2151 ScRefCellValue aCell(mrDoc, aAdr);
2152 if (GetCellErrCode(aCell) == FormulaError::NONE)
2153 {
2154 switch (aCell.meType)
2155 {
2156 // NOTE: this is Xcl nonsense!
2157 case CELLTYPE_STRING :
2158 case CELLTYPE_EDIT :
2159 nType = 2;
2160 break;
2161 case CELLTYPE_VALUE :
2162 {
2163 sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell);
2164 if (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL)
2165 nType = 4;
2166 else
2167 nType = 1;
2168 }
2169 break;
2170 case CELLTYPE_NONE:
2171 // always 1, s. tdf#73078
2172 nType = 1;
2173 break;
2174 case CELLTYPE_FORMULA :
2175 nType = 8;
2176 break;
2177 default:
2178 PushIllegalArgument();
2179 }
2180 }
2181 else
2182 nType = 16;
2183 }
2184 break;
2185 case svString:
2186 PopError();
2187 if ( nGlobalError != FormulaError::NONE )
2188 {
2189 nType = 16;
2190 nGlobalError = FormulaError::NONE;
2191 }
2192 else
2193 nType = 2;
2194 break;
2195 case svMatrix:
2196 PopMatrix();
2197 if ( nGlobalError != FormulaError::NONE )
2198 {
2199 nType = 16;
2200 nGlobalError = FormulaError::NONE;
2201 }
2202 else
2203 nType = 64;
2204 // we could return the type of one element if in JumpMatrix or
2205 // ForceArray mode, but Xcl doesn't ...
2206 break;
2207 default:
2208 PopError();
2209 if ( nGlobalError != FormulaError::NONE )
2210 {
2211 nType = 16;
2212 nGlobalError = FormulaError::NONE;
2213 }
2214 else
2215 nType = 1;
2216 }
2217 PushInt( nType );
2218 }
2219
lcl_FormatHasNegColor(const SvNumberformat * pFormat)2220 static bool lcl_FormatHasNegColor( const SvNumberformat* pFormat )
2221 {
2222 return pFormat && pFormat->GetColor( 1 );
2223 }
2224
lcl_FormatHasOpenPar(const SvNumberformat * pFormat)2225 static bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
2226 {
2227 return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1);
2228 }
2229
2230 namespace {
2231
getFormatString(const SvNumberFormatter * pFormatter,sal_uLong nFormat,OUString & rFmtStr)2232 void getFormatString(const SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr)
2233 {
2234 rFmtStr = pFormatter->GetCalcCellReturn( nFormat);
2235 }
2236
2237 }
2238
ScCell()2239 void ScInterpreter::ScCell()
2240 { // ATTRIBUTE ; [REF]
2241 sal_uInt8 nParamCount = GetByte();
2242 if( !MustHaveParamCount( nParamCount, 1, 2 ) )
2243 return;
2244
2245 ScAddress aCellPos( aPos );
2246 if( nParamCount == 2 )
2247 {
2248 switch (GetStackType())
2249 {
2250 case svExternalSingleRef:
2251 case svExternalDoubleRef:
2252 {
2253 // Let's handle external reference separately...
2254 ScCellExternal();
2255 return;
2256 }
2257 case svDoubleRef:
2258 {
2259 // Exceptionally not an intersecting position but top left.
2260 // See ODF v1.3 part 4 OpenFormula 6.13.3 CELL
2261 ScRange aRange;
2262 PopDoubleRef( aRange);
2263 aCellPos = aRange.aStart;
2264 }
2265 break;
2266 case svSingleRef:
2267 PopSingleRef( aCellPos);
2268 break;
2269 default:
2270 PopError();
2271 SetError( FormulaError::NoRef);
2272 }
2273 }
2274 OUString aInfoType = GetString().getString();
2275 if (nGlobalError != FormulaError::NONE)
2276 PushIllegalParameter();
2277 else
2278 {
2279 ScRefCellValue aCell(mrDoc, aCellPos);
2280
2281 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell);
2282
2283 // *** ADDRESS INFO ***
2284 if( aInfoType == "COL" )
2285 { // column number (1-based)
2286 PushInt( aCellPos.Col() + 1 );
2287 }
2288 else if( aInfoType == "ROW" )
2289 { // row number (1-based)
2290 PushInt( aCellPos.Row() + 1 );
2291 }
2292 else if( aInfoType == "SHEET" )
2293 { // table number (1-based)
2294 PushInt( aCellPos.Tab() + 1 );
2295 }
2296 else if( aInfoType == "ADDRESS" )
2297 { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
2298 ScRefFlags nFlags = (aCellPos.Tab() == aPos.Tab()) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D;
2299 OUString aStr(aCellPos.Format(nFlags, &mrDoc, mrDoc.GetAddressConvention()));
2300 PushString(aStr);
2301 }
2302 else if( aInfoType == "FILENAME" )
2303 { // file name and table name: 'FILENAME'#$TABLE
2304 SCTAB nTab = aCellPos.Tab();
2305 OUString aFuncResult;
2306 if( nTab < mrDoc.GetTableCount() )
2307 {
2308 if( mrDoc.GetLinkMode( nTab ) == ScLinkMode::VALUE )
2309 mrDoc.GetName( nTab, aFuncResult );
2310 else
2311 {
2312 SfxObjectShell* pShell = mrDoc.GetDocumentShell();
2313 if( pShell && pShell->GetMedium() )
2314 {
2315 OUStringBuffer aBuf;
2316 aBuf.append('\'');
2317 const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
2318 aBuf.append(rURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous));
2319 aBuf.append("'#$");
2320 OUString aTabName;
2321 mrDoc.GetName( nTab, aTabName );
2322 aBuf.append(aTabName);
2323 aFuncResult = aBuf.makeStringAndClear();
2324 }
2325 }
2326 }
2327 PushString( aFuncResult );
2328 }
2329 else if( aInfoType == "COORD" )
2330 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2331 // Yes, passing tab as col is intentional!
2332 OUString aCellStr1 =
2333 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
2334 (ScRefFlags::COL_ABS|ScRefFlags::COL_VALID), nullptr, mrDoc.GetAddressConvention() );
2335 OUString aCellStr2 =
2336 aCellPos.Format((ScRefFlags::COL_ABS|ScRefFlags::COL_VALID|ScRefFlags::ROW_ABS|ScRefFlags::ROW_VALID),
2337 nullptr, mrDoc.GetAddressConvention());
2338 OUString aFuncResult = aCellStr1 + ":" + aCellStr2;
2339 PushString( aFuncResult );
2340 }
2341
2342 // *** CELL PROPERTIES ***
2343 else if( aInfoType == "CONTENTS" )
2344 { // contents of the cell, no formatting
2345 if (aCell.hasString())
2346 {
2347 svl::SharedString aStr;
2348 GetCellString(aStr, aCell);
2349 PushString( aStr );
2350 }
2351 else
2352 PushDouble(GetCellValue(aCellPos, aCell));
2353 }
2354 else if( aInfoType == "TYPE" )
2355 { // b = blank; l = string (label); v = otherwise (value)
2356 sal_Unicode c;
2357 if (aCell.hasString())
2358 c = 'l';
2359 else
2360 c = aCell.hasNumeric() ? 'v' : 'b';
2361 PushString( OUString(c) );
2362 }
2363 else if( aInfoType == "WIDTH" )
2364 { // column width (rounded off as count of zero characters in standard font and size)
2365 Printer* pPrinter = mrDoc.GetPrinter();
2366 MapMode aOldMode( pPrinter->GetMapMode() );
2367 vcl::Font aOldFont( pPrinter->GetFont() );
2368 vcl::Font aDefFont;
2369
2370 pPrinter->SetMapMode(MapMode(MapUnit::MapTwip));
2371 // font color doesn't matter here
2372 mrDoc.GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter );
2373 pPrinter->SetFont( aDefFont );
2374 tools::Long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
2375 pPrinter->SetFont( aOldFont );
2376 pPrinter->SetMapMode( aOldMode );
2377 int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
2378 PushInt( nZeroCount );
2379 }
2380 else if( aInfoType == "PREFIX" )
2381 { // ' = left; " = right; ^ = centered
2382 sal_Unicode c = 0;
2383 if (aCell.hasString())
2384 {
2385 const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY );
2386 switch( pJustAttr->GetValue() )
2387 {
2388 case SvxCellHorJustify::Standard:
2389 case SvxCellHorJustify::Left:
2390 case SvxCellHorJustify::Block: c = '\''; break;
2391 case SvxCellHorJustify::Center: c = '^'; break;
2392 case SvxCellHorJustify::Right: c = '"'; break;
2393 case SvxCellHorJustify::Repeat: c = '\\'; break;
2394 }
2395 }
2396 PushString( OUString(c) );
2397 }
2398 else if( aInfoType == "PROTECT" )
2399 { // 1 = cell locked
2400 const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION );
2401 PushInt( pProtAttr->GetProtection() ? 1 : 0 );
2402 }
2403
2404 // *** FORMATTING ***
2405 else if( aInfoType == "FORMAT" )
2406 { // specific format code for standard formats
2407 OUString aFuncResult;
2408 sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos );
2409 getFormatString(pFormatter, nFormat, aFuncResult);
2410 PushString( aFuncResult );
2411 }
2412 else if( aInfoType == "COLOR" )
2413 { // 1 = negative values are colored, otherwise 0
2414 const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2415 PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2416 }
2417 else if( aInfoType == "PARENTHESES" )
2418 { // 1 = format string contains a '(' character, otherwise 0
2419 const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) );
2420 PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2421 }
2422 else
2423 PushIllegalArgument();
2424 }
2425 }
2426
ScCellExternal()2427 void ScInterpreter::ScCellExternal()
2428 {
2429 sal_uInt16 nFileId;
2430 OUString aTabName;
2431 ScSingleRefData aRef;
2432 ScExternalRefCache::TokenRef pToken;
2433 ScExternalRefCache::CellFormat aFmt;
2434 PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
2435 if (nGlobalError != FormulaError::NONE)
2436 {
2437 PushError( nGlobalError);
2438 return;
2439 }
2440
2441 OUString aInfoType = GetString().getString();
2442 if (nGlobalError != FormulaError::NONE)
2443 {
2444 PushError( nGlobalError);
2445 return;
2446 }
2447
2448 SCCOL nCol;
2449 SCROW nRow;
2450 SCTAB nTab;
2451 aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2452 SingleRefToVars(aRef, nCol, nRow, nTab);
2453 if (nGlobalError != FormulaError::NONE)
2454 {
2455 PushIllegalParameter();
2456 return;
2457 }
2458 aRef.SetAbsTab(-1); // revert the value.
2459
2460 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell);
2461 ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager();
2462
2463 if ( aInfoType == "COL" )
2464 PushInt(nCol + 1);
2465 else if ( aInfoType == "ROW" )
2466 PushInt(nRow + 1);
2467 else if ( aInfoType == "SHEET" )
2468 {
2469 // For SHEET, No idea what number we should set, but let's always set
2470 // 1 if the external sheet exists, no matter what sheet. Excel does
2471 // the same.
2472 if (pRefMgr->getCacheTable(nFileId, aTabName, false))
2473 PushInt(1);
2474 else
2475 SetError(FormulaError::NoName);
2476 }
2477 else if ( aInfoType == "ADDRESS" )
2478 {
2479 // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2480 ScTokenArray aArray(mrDoc);
2481 aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned
2482 ScCompiler aComp(mrDoc, aPos, aArray, formula::FormulaGrammar::GRAM_ODFF_A1);
2483 OUString aStr;
2484 aComp.CreateStringFromTokenArray(aStr);
2485 PushString(aStr);
2486 }
2487 else if ( aInfoType == "FILENAME" )
2488 {
2489 // 'file URI'#$SheetName
2490
2491 const OUString* p = pRefMgr->getExternalFileName(nFileId);
2492 if (!p)
2493 {
2494 // In theory this should never happen...
2495 SetError(FormulaError::NoName);
2496 return;
2497 }
2498
2499 OUString aBuf = "'" + *p + "'#$" + aTabName;
2500 PushString(aBuf);
2501 }
2502 else if ( aInfoType == "CONTENTS" )
2503 {
2504 switch (pToken->GetType())
2505 {
2506 case svString:
2507 PushString(pToken->GetString());
2508 break;
2509 case svDouble:
2510 PushString(OUString::number(pToken->GetDouble()));
2511 break;
2512 case svError:
2513 PushString(ScGlobal::GetErrorString(pToken->GetError()));
2514 break;
2515 default:
2516 PushString(ScGlobal::GetEmptyOUString());
2517 }
2518 }
2519 else if ( aInfoType == "TYPE" )
2520 {
2521 sal_Unicode c = 'v';
2522 switch (pToken->GetType())
2523 {
2524 case svString:
2525 c = 'l';
2526 break;
2527 case svEmptyCell:
2528 c = 'b';
2529 break;
2530 default:
2531 ;
2532 }
2533 PushString(OUString(c));
2534 }
2535 else if ( aInfoType == "FORMAT" )
2536 {
2537 OUString aFmtStr;
2538 sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
2539 getFormatString(pFormatter, nFmt, aFmtStr);
2540 PushString(aFmtStr);
2541 }
2542 else if ( aInfoType == "COLOR" )
2543 {
2544 // 1 = negative values are colored, otherwise 0
2545 int nVal = 0;
2546 if (aFmt.mbIsSet)
2547 {
2548 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2549 nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
2550 }
2551 PushInt(nVal);
2552 }
2553 else if ( aInfoType == "PARENTHESES" )
2554 {
2555 // 1 = format string contains a '(' character, otherwise 0
2556 int nVal = 0;
2557 if (aFmt.mbIsSet)
2558 {
2559 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2560 nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
2561 }
2562 PushInt(nVal);
2563 }
2564 else
2565 PushIllegalParameter();
2566 }
2567
ScIsRef()2568 void ScInterpreter::ScIsRef()
2569 {
2570 nFuncFmtType = SvNumFormatType::LOGICAL;
2571 bool bRes = false;
2572 switch ( GetStackType() )
2573 {
2574 case svSingleRef :
2575 {
2576 ScAddress aAdr;
2577 PopSingleRef( aAdr );
2578 if ( nGlobalError == FormulaError::NONE )
2579 bRes = true;
2580 }
2581 break;
2582 case svDoubleRef :
2583 {
2584 ScRange aRange;
2585 PopDoubleRef( aRange );
2586 if ( nGlobalError == FormulaError::NONE )
2587 bRes = true;
2588 }
2589 break;
2590 case svRefList :
2591 {
2592 FormulaConstTokenRef x = PopToken();
2593 if ( nGlobalError == FormulaError::NONE )
2594 bRes = !x->GetRefList()->empty();
2595 }
2596 break;
2597 case svExternalSingleRef:
2598 {
2599 ScExternalRefCache::TokenRef pToken;
2600 PopExternalSingleRef(pToken);
2601 if (nGlobalError == FormulaError::NONE)
2602 bRes = true;
2603 }
2604 break;
2605 case svExternalDoubleRef:
2606 {
2607 ScExternalRefCache::TokenArrayRef pArray;
2608 PopExternalDoubleRef(pArray);
2609 if (nGlobalError == FormulaError::NONE)
2610 bRes = true;
2611 }
2612 break;
2613 default:
2614 Pop();
2615 }
2616 nGlobalError = FormulaError::NONE;
2617 PushInt( int(bRes) );
2618 }
2619
ScIsValue()2620 void ScInterpreter::ScIsValue()
2621 {
2622 nFuncFmtType = SvNumFormatType::LOGICAL;
2623 bool bRes = false;
2624 switch ( GetRawStackType() )
2625 {
2626 case svDouble:
2627 Pop();
2628 bRes = true;
2629 break;
2630 case svDoubleRef :
2631 case svSingleRef :
2632 {
2633 ScAddress aAdr;
2634 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2635 break;
2636
2637 ScRefCellValue aCell(mrDoc, aAdr);
2638 if (GetCellErrCode(aCell) == FormulaError::NONE)
2639 {
2640 switch (aCell.meType)
2641 {
2642 case CELLTYPE_VALUE :
2643 bRes = true;
2644 break;
2645 case CELLTYPE_FORMULA :
2646 bRes = (aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
2647 break;
2648 default:
2649 ; // nothing
2650 }
2651 }
2652 }
2653 break;
2654 case svExternalSingleRef:
2655 {
2656 ScExternalRefCache::TokenRef pToken;
2657 PopExternalSingleRef(pToken);
2658 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
2659 bRes = true;
2660 }
2661 break;
2662 case svExternalDoubleRef:
2663 case svMatrix:
2664 {
2665 ScMatrixRef pMat = GetMatrix();
2666 if ( !pMat )
2667 ; // nothing
2668 else if ( !pJumpMatrix )
2669 {
2670 if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE)
2671 bRes = pMat->IsValue( 0, 0);
2672 }
2673 else
2674 {
2675 SCSIZE nCols, nRows, nC, nR;
2676 pMat->GetDimensions( nCols, nRows);
2677 pJumpMatrix->GetPos( nC, nR);
2678 if ( nC < nCols && nR < nRows )
2679 if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE)
2680 bRes = pMat->IsValue( nC, nR);
2681 }
2682 }
2683 break;
2684 default:
2685 Pop();
2686 }
2687 nGlobalError = FormulaError::NONE;
2688 PushInt( int(bRes) );
2689 }
2690
ScIsFormula()2691 void ScInterpreter::ScIsFormula()
2692 {
2693 nFuncFmtType = SvNumFormatType::LOGICAL;
2694 bool bRes = false;
2695 switch ( GetStackType() )
2696 {
2697 case svDoubleRef :
2698 if (IsInArrayContext())
2699 {
2700 SCCOL nCol1, nCol2;
2701 SCROW nRow1, nRow2;
2702 SCTAB nTab1, nTab2;
2703 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2704 if (nGlobalError != FormulaError::NONE)
2705 {
2706 PushError( nGlobalError);
2707 return;
2708 }
2709 if (nTab1 != nTab2)
2710 {
2711 PushIllegalArgument();
2712 return;
2713 }
2714
2715 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1),
2716 static_cast<SCSIZE>(nRow2 - nRow1 + 1), true);
2717 if (!pResMat)
2718 {
2719 PushError( FormulaError::MatrixSize);
2720 return;
2721 }
2722
2723 /* TODO: we really should have a gap-aware cell iterator. */
2724 SCSIZE i=0, j=0;
2725 ScAddress aAdr( 0, 0, nTab1);
2726 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2727 {
2728 aAdr.SetCol(nCol);
2729 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2730 {
2731 aAdr.SetRow(nRow);
2732 ScRefCellValue aCell(mrDoc, aAdr);
2733 pResMat->PutBoolean( (aCell.meType == CELLTYPE_FORMULA), i,j);
2734 ++j;
2735 }
2736 ++i;
2737 j = 0;
2738 }
2739
2740 PushMatrix( pResMat);
2741 return;
2742 }
2743 [[fallthrough]];
2744 case svSingleRef :
2745 {
2746 ScAddress aAdr;
2747 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2748 break;
2749
2750 bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA);
2751 }
2752 break;
2753 default:
2754 Pop();
2755 }
2756 nGlobalError = FormulaError::NONE;
2757 PushInt( int(bRes) );
2758 }
2759
ScFormula()2760 void ScInterpreter::ScFormula()
2761 {
2762 OUString aFormula;
2763 switch ( GetStackType() )
2764 {
2765 case svDoubleRef :
2766 if (IsInArrayContext())
2767 {
2768 SCCOL nCol1, nCol2;
2769 SCROW nRow1, nRow2;
2770 SCTAB nTab1, nTab2;
2771 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
2772 if (nGlobalError != FormulaError::NONE)
2773 break;
2774
2775 if (nTab1 != nTab2)
2776 {
2777 SetError( FormulaError::IllegalArgument);
2778 break;
2779 }
2780
2781 ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true);
2782 if (!pResMat)
2783 break;
2784
2785 /* TODO: use a column iterator instead? */
2786 SCSIZE i=0, j=0;
2787 ScAddress aAdr(0,0,nTab1);
2788 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
2789 {
2790 aAdr.SetCol(nCol);
2791 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
2792 {
2793 aAdr.SetRow(nRow);
2794 ScRefCellValue aCell(mrDoc, aAdr);
2795 switch (aCell.meType)
2796 {
2797 case CELLTYPE_FORMULA :
2798 aCell.mpFormula->GetFormula(aFormula, formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
2799 pResMat->PutString( mrStrPool.intern( aFormula), i,j);
2800 break;
2801 default:
2802 pResMat->PutError( FormulaError::NotAvailable, i,j);
2803 }
2804 ++j;
2805 }
2806 ++i;
2807 j = 0;
2808 }
2809
2810 PushMatrix( pResMat);
2811 return;
2812 }
2813 [[fallthrough]];
2814 case svSingleRef :
2815 {
2816 ScAddress aAdr;
2817 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2818 break;
2819
2820 ScRefCellValue aCell(mrDoc, aAdr);
2821 switch (aCell.meType)
2822 {
2823 case CELLTYPE_FORMULA :
2824 aCell.mpFormula->GetFormula(aFormula, formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext);
2825 break;
2826 default:
2827 SetError( FormulaError::NotAvailable );
2828 }
2829 }
2830 break;
2831 default:
2832 Pop();
2833 SetError( FormulaError::NotAvailable );
2834 }
2835 PushString( aFormula );
2836 }
2837
ScIsNV()2838 void ScInterpreter::ScIsNV()
2839 {
2840 nFuncFmtType = SvNumFormatType::LOGICAL;
2841 bool bRes = false;
2842 switch ( GetStackType() )
2843 {
2844 case svDoubleRef :
2845 case svSingleRef :
2846 {
2847 ScAddress aAdr;
2848 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2849 if ( nGlobalError == FormulaError::NotAvailable )
2850 bRes = true;
2851 else if (bOk)
2852 {
2853 ScRefCellValue aCell(mrDoc, aAdr);
2854 FormulaError nErr = GetCellErrCode(aCell);
2855 bRes = (nErr == FormulaError::NotAvailable);
2856 }
2857 }
2858 break;
2859 case svExternalSingleRef:
2860 {
2861 ScExternalRefCache::TokenRef pToken;
2862 PopExternalSingleRef(pToken);
2863 if (nGlobalError == FormulaError::NotAvailable ||
2864 (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable))
2865 bRes = true;
2866 }
2867 break;
2868 case svExternalDoubleRef:
2869 case svMatrix:
2870 {
2871 ScMatrixRef pMat = GetMatrix();
2872 if ( !pMat )
2873 ; // nothing
2874 else if ( !pJumpMatrix )
2875 bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable);
2876 else
2877 {
2878 SCSIZE nCols, nRows, nC, nR;
2879 pMat->GetDimensions( nCols, nRows);
2880 pJumpMatrix->GetPos( nC, nR);
2881 if ( nC < nCols && nR < nRows )
2882 bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable);
2883 }
2884 }
2885 break;
2886 default:
2887 PopError();
2888 if ( nGlobalError == FormulaError::NotAvailable )
2889 bRes = true;
2890 }
2891 nGlobalError = FormulaError::NONE;
2892 PushInt( int(bRes) );
2893 }
2894
ScIsErr()2895 void ScInterpreter::ScIsErr()
2896 {
2897 nFuncFmtType = SvNumFormatType::LOGICAL;
2898 bool bRes = false;
2899 switch ( GetStackType() )
2900 {
2901 case svDoubleRef :
2902 case svSingleRef :
2903 {
2904 ScAddress aAdr;
2905 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2906 if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) )
2907 bRes = true;
2908 else
2909 {
2910 ScRefCellValue aCell(mrDoc, aAdr);
2911 FormulaError nErr = GetCellErrCode(aCell);
2912 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2913 }
2914 }
2915 break;
2916 case svExternalSingleRef:
2917 {
2918 ScExternalRefCache::TokenRef pToken;
2919 PopExternalSingleRef(pToken);
2920 if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken ||
2921 (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable))
2922 bRes = true;
2923 }
2924 break;
2925 case svExternalDoubleRef:
2926 case svMatrix:
2927 {
2928 ScMatrixRef pMat = GetMatrix();
2929 if ( nGlobalError != FormulaError::NONE || !pMat )
2930 bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat);
2931 else if ( !pJumpMatrix )
2932 {
2933 FormulaError nErr = pMat->GetErrorIfNotString( 0, 0);
2934 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2935 }
2936 else
2937 {
2938 SCSIZE nCols, nRows, nC, nR;
2939 pMat->GetDimensions( nCols, nRows);
2940 pJumpMatrix->GetPos( nC, nR);
2941 if ( nC < nCols && nR < nRows )
2942 {
2943 FormulaError nErr = pMat->GetErrorIfNotString( nC, nR);
2944 bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable);
2945 }
2946 }
2947 }
2948 break;
2949 default:
2950 PopError();
2951 if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable )
2952 bRes = true;
2953 }
2954 nGlobalError = FormulaError::NONE;
2955 PushInt( int(bRes) );
2956 }
2957
ScIsError()2958 void ScInterpreter::ScIsError()
2959 {
2960 nFuncFmtType = SvNumFormatType::LOGICAL;
2961 bool bRes = false;
2962 switch ( GetStackType() )
2963 {
2964 case svDoubleRef :
2965 case svSingleRef :
2966 {
2967 ScAddress aAdr;
2968 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2969 {
2970 bRes = true;
2971 break;
2972 }
2973 if ( nGlobalError != FormulaError::NONE )
2974 bRes = true;
2975 else
2976 {
2977 ScRefCellValue aCell(mrDoc, aAdr);
2978 bRes = (GetCellErrCode(aCell) != FormulaError::NONE);
2979 }
2980 }
2981 break;
2982 case svExternalSingleRef:
2983 {
2984 ScExternalRefCache::TokenRef pToken;
2985 PopExternalSingleRef(pToken);
2986 if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError)
2987 bRes = true;
2988 }
2989 break;
2990 case svExternalDoubleRef:
2991 case svMatrix:
2992 {
2993 ScMatrixRef pMat = GetMatrix();
2994 if ( nGlobalError != FormulaError::NONE || !pMat )
2995 bRes = true;
2996 else if ( !pJumpMatrix )
2997 bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE);
2998 else
2999 {
3000 SCSIZE nCols, nRows, nC, nR;
3001 pMat->GetDimensions( nCols, nRows);
3002 pJumpMatrix->GetPos( nC, nR);
3003 if ( nC < nCols && nR < nRows )
3004 bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE);
3005 }
3006 }
3007 break;
3008 default:
3009 PopError();
3010 if ( nGlobalError != FormulaError::NONE )
3011 bRes = true;
3012 }
3013 nGlobalError = FormulaError::NONE;
3014 PushInt( int(bRes) );
3015 }
3016
IsEven()3017 bool ScInterpreter::IsEven()
3018 {
3019 nFuncFmtType = SvNumFormatType::LOGICAL;
3020 bool bRes = false;
3021 double fVal = 0.0;
3022 switch ( GetStackType() )
3023 {
3024 case svDoubleRef :
3025 case svSingleRef :
3026 {
3027 ScAddress aAdr;
3028 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3029 break;
3030
3031 ScRefCellValue aCell(mrDoc, aAdr);
3032 FormulaError nErr = GetCellErrCode(aCell);
3033 if (nErr != FormulaError::NONE)
3034 SetError(nErr);
3035 else
3036 {
3037 switch (aCell.meType)
3038 {
3039 case CELLTYPE_VALUE :
3040 fVal = GetCellValue(aAdr, aCell);
3041 bRes = true;
3042 break;
3043 case CELLTYPE_FORMULA :
3044 if (aCell.mpFormula->IsValue())
3045 {
3046 fVal = GetCellValue(aAdr, aCell);
3047 bRes = true;
3048 }
3049 break;
3050 default:
3051 ; // nothing
3052 }
3053 }
3054 }
3055 break;
3056 case svDouble:
3057 {
3058 fVal = PopDouble();
3059 bRes = true;
3060 }
3061 break;
3062 case svExternalSingleRef:
3063 {
3064 ScExternalRefCache::TokenRef pToken;
3065 PopExternalSingleRef(pToken);
3066 if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble)
3067 {
3068 fVal = pToken->GetDouble();
3069 bRes = true;
3070 }
3071 }
3072 break;
3073 case svExternalDoubleRef:
3074 case svMatrix:
3075 {
3076 ScMatrixRef pMat = GetMatrix();
3077 if ( !pMat )
3078 ; // nothing
3079 else if ( !pJumpMatrix )
3080 {
3081 bRes = pMat->IsValue( 0, 0);
3082 if ( bRes )
3083 fVal = pMat->GetDouble( 0, 0);
3084 }
3085 else
3086 {
3087 SCSIZE nCols, nRows, nC, nR;
3088 pMat->GetDimensions( nCols, nRows);
3089 pJumpMatrix->GetPos( nC, nR);
3090 if ( nC < nCols && nR < nRows )
3091 {
3092 bRes = pMat->IsValue( nC, nR);
3093 if ( bRes )
3094 fVal = pMat->GetDouble( nC, nR);
3095 }
3096 else
3097 SetError( FormulaError::NoValue);
3098 }
3099 }
3100 break;
3101 default:
3102 ; // nothing
3103 }
3104 if ( !bRes )
3105 SetError( FormulaError::IllegalParameter);
3106 else
3107 bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
3108 return bRes;
3109 }
3110
ScIsEven()3111 void ScInterpreter::ScIsEven()
3112 {
3113 PushInt( int(IsEven()) );
3114 }
3115
ScIsOdd()3116 void ScInterpreter::ScIsOdd()
3117 {
3118 PushInt( int(!IsEven()) );
3119 }
3120
ScN()3121 void ScInterpreter::ScN()
3122 {
3123 FormulaError nErr = nGlobalError;
3124 nGlobalError = FormulaError::NONE;
3125 // Temporarily override the ConvertStringToValue() error for
3126 // GetCellValue() / GetCellValueOrZero()
3127 FormulaError nSErr = mnStringNoValueError;
3128 mnStringNoValueError = FormulaError::CellNoValue;
3129 double fVal = GetDouble();
3130 mnStringNoValueError = nSErr;
3131 if (nErr != FormulaError::NONE)
3132 nGlobalError = nErr; // preserve previous error if any
3133 else if (nGlobalError == FormulaError::CellNoValue)
3134 nGlobalError = FormulaError::NONE; // reset temporary detection error
3135 PushDouble(fVal);
3136 }
3137
ScTrim()3138 void ScInterpreter::ScTrim()
3139 {
3140 // Doesn't only trim but also removes duplicated blanks within!
3141 OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
3142 OUStringBuffer aStr;
3143 const sal_Unicode* p = aVal.getStr();
3144 const sal_Unicode* const pEnd = p + aVal.getLength();
3145 while ( p < pEnd )
3146 {
3147 if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine
3148 aStr.append(*p);
3149 p++;
3150 }
3151 PushString(aStr.makeStringAndClear());
3152 }
3153
ScUpper()3154 void ScInterpreter::ScUpper()
3155 {
3156 OUString aString = ScGlobal::getCharClassPtr()->uppercase(GetString().getString());
3157 PushString(aString);
3158 }
3159
ScProper()3160 void ScInterpreter::ScProper()
3161 {
3162 //2do: what to do with I18N-CJK ?!?
3163 OUStringBuffer aStr(GetString().getString());
3164 const sal_Int32 nLen = aStr.getLength();
3165 if ( nLen > 0 )
3166 {
3167 OUString aUpr(ScGlobal::getCharClassPtr()->uppercase(aStr.toString()));
3168 OUString aLwr(ScGlobal::getCharClassPtr()->lowercase(aStr.toString()));
3169 aStr[0] = aUpr[0];
3170 sal_Int32 nPos = 1;
3171 while( nPos < nLen )
3172 {
3173 OUString aTmpStr( aStr[nPos-1] );
3174 if ( !ScGlobal::getCharClassPtr()->isLetter( aTmpStr, 0 ) )
3175 aStr[nPos] = aUpr[nPos];
3176 else
3177 aStr[nPos] = aLwr[nPos];
3178 ++nPos;
3179 }
3180 }
3181 PushString(aStr.makeStringAndClear());
3182 }
3183
ScLower()3184 void ScInterpreter::ScLower()
3185 {
3186 OUString aString = ScGlobal::getCharClassPtr()->lowercase(GetString().getString());
3187 PushString(aString);
3188 }
3189
ScLen()3190 void ScInterpreter::ScLen()
3191 {
3192 OUString aStr = GetString().getString();
3193 sal_Int32 nIdx = 0;
3194 sal_Int32 nCnt = 0;
3195 while ( nIdx < aStr.getLength() )
3196 {
3197 aStr.iterateCodePoints( &nIdx );
3198 ++nCnt;
3199 }
3200 PushDouble( nCnt );
3201 }
3202
ScT()3203 void ScInterpreter::ScT()
3204 {
3205 switch ( GetStackType() )
3206 {
3207 case svDoubleRef :
3208 case svSingleRef :
3209 {
3210 ScAddress aAdr;
3211 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3212 {
3213 PushInt(0);
3214 return ;
3215 }
3216 bool bValue = false;
3217 ScRefCellValue aCell(mrDoc, aAdr);
3218 if (GetCellErrCode(aCell) == FormulaError::NONE)
3219 {
3220 switch (aCell.meType)
3221 {
3222 case CELLTYPE_VALUE :
3223 bValue = true;
3224 break;
3225 case CELLTYPE_FORMULA :
3226 bValue = aCell.mpFormula->IsValue();
3227 break;
3228 default:
3229 ; // nothing
3230 }
3231 }
3232 if ( bValue )
3233 PushString(EMPTY_OUSTRING);
3234 else
3235 {
3236 // like GetString()
3237 svl::SharedString aStr;
3238 GetCellString(aStr, aCell);
3239 PushString(aStr);
3240 }
3241 }
3242 break;
3243 case svMatrix:
3244 case svExternalSingleRef:
3245 case svExternalDoubleRef:
3246 {
3247 double fVal;
3248 svl::SharedString aStr;
3249 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
3250 if (ScMatrix::IsValueType( nMatValType))
3251 PushString(svl::SharedString::getEmptyString());
3252 else
3253 PushString( aStr);
3254 }
3255 break;
3256 case svDouble :
3257 {
3258 PopError();
3259 PushString( EMPTY_OUSTRING );
3260 }
3261 break;
3262 case svString :
3263 ; // leave on stack
3264 break;
3265 default :
3266 PushError( FormulaError::UnknownOpCode);
3267 }
3268 }
3269
ScValue()3270 void ScInterpreter::ScValue()
3271 {
3272 OUString aInputString;
3273 double fVal;
3274
3275 switch ( GetRawStackType() )
3276 {
3277 case svMissing:
3278 case svEmptyCell:
3279 Pop();
3280 PushInt(0);
3281 return;
3282 case svDouble:
3283 return; // leave on stack
3284
3285 case svSingleRef:
3286 case svDoubleRef:
3287 {
3288 ScAddress aAdr;
3289 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3290 {
3291 PushInt(0);
3292 return;
3293 }
3294 ScRefCellValue aCell(mrDoc, aAdr);
3295 if (aCell.hasString())
3296 {
3297 svl::SharedString aSS;
3298 GetCellString(aSS, aCell);
3299 aInputString = aSS.getString();
3300 }
3301 else if (aCell.hasNumeric())
3302 {
3303 PushDouble( GetCellValue(aAdr, aCell) );
3304 return;
3305 }
3306 else
3307 {
3308 PushDouble(0.0);
3309 return;
3310 }
3311 }
3312 break;
3313 case svMatrix:
3314 {
3315 svl::SharedString aSS;
3316 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
3317 aSS);
3318 aInputString = aSS.getString();
3319 switch (nType)
3320 {
3321 case ScMatValType::Empty:
3322 fVal = 0.0;
3323 [[fallthrough]];
3324 case ScMatValType::Value:
3325 case ScMatValType::Boolean:
3326 PushDouble( fVal);
3327 return;
3328 case ScMatValType::String:
3329 // evaluated below
3330 break;
3331 default:
3332 PushIllegalArgument();
3333 }
3334 }
3335 break;
3336 default:
3337 aInputString = GetString().getString();
3338 break;
3339 }
3340
3341 sal_uInt32 nFIndex = 0; // 0 for default locale
3342 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
3343 PushDouble(fVal);
3344 else
3345 PushIllegalArgument();
3346 }
3347
3348 // fdo#57180
ScNumberValue()3349 void ScInterpreter::ScNumberValue()
3350 {
3351
3352 sal_uInt8 nParamCount = GetByte();
3353 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
3354 return;
3355
3356 OUString aInputString;
3357 OUString aGroupSeparator;
3358 sal_Unicode cDecimalSeparator = 0;
3359
3360 if ( nParamCount == 3 )
3361 aGroupSeparator = GetString().getString();
3362
3363 if ( nParamCount >= 2 )
3364 {
3365 OUString aDecimalSeparator = GetString().getString();
3366 if ( aDecimalSeparator.getLength() == 1 )
3367 cDecimalSeparator = aDecimalSeparator[ 0 ];
3368 else
3369 {
3370 PushIllegalArgument(); //if given, separator length must be 1
3371 return;
3372 }
3373 }
3374
3375 if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
3376 {
3377 PushIllegalArgument(); //decimal separator cannot appear in group separator
3378 return;
3379 }
3380
3381 switch (GetStackType())
3382 {
3383 case svDouble:
3384 return; // leave on stack
3385 default:
3386 aInputString = GetString().getString();
3387 }
3388 if ( nGlobalError != FormulaError::NONE )
3389 {
3390 PushError( nGlobalError );
3391 return;
3392 }
3393 if ( aInputString.isEmpty() )
3394 {
3395 if ( maCalcConfig.mbEmptyStringAsZero )
3396 PushDouble( 0.0 );
3397 else
3398 PushNoValue();
3399 return;
3400 }
3401
3402 sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
3403 if ( nDecSep != 0 )
3404 {
3405 OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
3406 sal_Int32 nIndex = 0;
3407 while (nIndex < aGroupSeparator.getLength())
3408 {
3409 sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
3410 aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
3411 }
3412 if ( nDecSep >= 0 )
3413 aInputString = aTemporary + aInputString.subView( nDecSep );
3414 else
3415 aInputString = aTemporary;
3416 }
3417
3418 for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
3419 {
3420 sal_Unicode c = aInputString[ i ];
3421 if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
3422 aInputString = aInputString.replaceAt( i, 1, "" ); // remove spaces etc.
3423 }
3424 sal_Int32 nPercentCount = 0;
3425 for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
3426 {
3427 aInputString = aInputString.replaceAt( i, 1, "" ); // remove and count trailing '%'
3428 nPercentCount++;
3429 }
3430
3431 rtl_math_ConversionStatus eStatus;
3432 sal_Int32 nParseEnd;
3433 double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
3434 if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
3435 {
3436 if (nPercentCount)
3437 fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string
3438 PushDouble(fVal);
3439 return;
3440 }
3441 PushNoValue();
3442 }
3443
3444 //2do: this should be a proper unicode string method
lcl_ScInterpreter_IsPrintable(sal_Unicode c)3445 static bool lcl_ScInterpreter_IsPrintable( sal_Unicode c )
3446 {
3447 return 0x20 <= c && c != 0x7f;
3448 }
3449
ScClean()3450 void ScInterpreter::ScClean()
3451 {
3452 OUString aStr = GetString().getString();
3453 for ( sal_Int32 i = 0; i < aStr.getLength(); i++ )
3454 {
3455 if ( !lcl_ScInterpreter_IsPrintable( aStr[i] ) )
3456 aStr = aStr.replaceAt(i,1,"");
3457 }
3458 PushString(aStr);
3459 }
3460
ScCode()3461 void ScInterpreter::ScCode()
3462 {
3463 //2do: make it full range unicode?
3464 OUString aStr = GetString().getString();
3465 if (aStr.isEmpty())
3466 PushInt(0);
3467 else
3468 {
3469 //"classic" ByteString conversion flags
3470 const sal_uInt32 convertFlags =
3471 RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE |
3472 RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE |
3473 RTL_UNICODETOTEXT_FLAGS_FLUSH |
3474 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT |
3475 RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT |
3476 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE;
3477 PushInt( static_cast<unsigned char>(OUStringToOString(OUStringChar(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) );
3478 }
3479 }
3480
ScChar()3481 void ScInterpreter::ScChar()
3482 {
3483 //2do: make it full range unicode?
3484 double fVal = GetDouble();
3485 if (fVal < 0.0 || fVal >= 256.0)
3486 PushIllegalArgument();
3487 else
3488 {
3489 //"classic" ByteString conversion flags
3490 const sal_uInt32 convertFlags =
3491 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
3492 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
3493 RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
3494
3495 char cEncodedChar = static_cast<char>(fVal);
3496 OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags);
3497 PushString(aStr);
3498 }
3499 }
3500
3501 /* #i70213# fullwidth/halfwidth conversion provided by
3502 * Takashi Nakamoto <bluedwarf@ooo>
3503 * erAck: added Excel compatibility conversions as seen in issue's test case. */
3504
lcl_convertIntoHalfWidth(const OUString & rStr)3505 static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
3506 {
3507 // Make the initialization thread-safe. Since another function needs to be called, move it all to another
3508 // function and thread-safely initialize a static reference in this function.
3509 auto init = []() -> utl::TransliterationWrapper&
3510 {
3511 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3512 trans.loadModuleByImplName( "FULLWIDTH_HALFWIDTH_LIKE_ASC", LANGUAGE_SYSTEM );
3513 return trans;
3514 };
3515 static utl::TransliterationWrapper& aTrans( init());
3516 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3517 }
3518
lcl_convertIntoFullWidth(const OUString & rStr)3519 static OUString lcl_convertIntoFullWidth( const OUString & rStr )
3520 {
3521 auto init = []() -> utl::TransliterationWrapper&
3522 {
3523 static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE );
3524 trans.loadModuleByImplName( "HALFWIDTH_FULLWIDTH_LIKE_JIS", LANGUAGE_SYSTEM );
3525 return trans;
3526 };
3527 static utl::TransliterationWrapper& aTrans( init());
3528 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) );
3529 }
3530
3531 /* ODFF:
3532 * Summary: Converts half-width to full-width ASCII and katakana characters.
3533 * Semantics: Conversion is done for half-width ASCII and katakana characters,
3534 * other characters are simply copied from T to the result. This is the
3535 * complementary function to ASC.
3536 * For references regarding halfwidth and fullwidth characters see
3537 * http://www.unicode.org/reports/tr11/
3538 * http://www.unicode.org/charts/charindex2.html#H
3539 * http://www.unicode.org/charts/charindex2.html#F
3540 */
ScJis()3541 void ScInterpreter::ScJis()
3542 {
3543 if (MustHaveParamCount( GetByte(), 1))
3544 PushString( lcl_convertIntoFullWidth( GetString().getString()));
3545 }
3546
3547 /* ODFF:
3548 * Summary: Converts full-width to half-width ASCII and katakana characters.
3549 * Semantics: Conversion is done for full-width ASCII and katakana characters,
3550 * other characters are simply copied from T to the result. This is the
3551 * complementary function to JIS.
3552 */
ScAsc()3553 void ScInterpreter::ScAsc()
3554 {
3555 if (MustHaveParamCount( GetByte(), 1))
3556 PushString( lcl_convertIntoHalfWidth( GetString().getString()));
3557 }
3558
ScUnicode()3559 void ScInterpreter::ScUnicode()
3560 {
3561 if ( MustHaveParamCount( GetByte(), 1 ) )
3562 {
3563 OUString aStr = GetString().getString();
3564 if (aStr.isEmpty())
3565 PushIllegalParameter();
3566 else
3567 {
3568 PushDouble(aStr.iterateCodePoints(&o3tl::temporary(sal_Int32(0))));
3569 }
3570 }
3571 }
3572
ScUnichar()3573 void ScInterpreter::ScUnichar()
3574 {
3575 if ( MustHaveParamCount( GetByte(), 1 ) )
3576 {
3577 sal_uInt32 nCodePoint = GetUInt32();
3578 if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint))
3579 PushIllegalArgument();
3580 else
3581 {
3582 OUString aStr( &nCodePoint, 1 );
3583 PushString( aStr );
3584 }
3585 }
3586 }
3587
SwitchToArrayRefList(ScMatrixRef & xResMat,SCSIZE nMatRows,double fCurrent,const std::function<void (SCSIZE i,double fCurrent)> & MatOpFunc,bool bDoMatOp)3588 bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent,
3589 const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp )
3590 {
3591 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3592 if (!p || !p->IsArrayResult())
3593 return false;
3594
3595 if (!xResMat)
3596 {
3597 // Create and init all elements with current value.
3598 assert(nMatRows > 0);
3599 xResMat = GetNewMat( 1, nMatRows, true);
3600 xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1);
3601 }
3602 else if (bDoMatOp)
3603 {
3604 // Current value and values from vector are operands
3605 // for each vector position.
3606 for (SCSIZE i=0; i < nMatRows; ++i)
3607 {
3608 MatOpFunc( i, fCurrent);
3609 }
3610 }
3611 return true;
3612 }
3613
ScMin(bool bTextAsZero)3614 void ScInterpreter::ScMin( bool bTextAsZero )
3615 {
3616 short nParamCount = GetByte();
3617 if (!MustHaveParamCountMin( nParamCount, 1))
3618 return;
3619
3620 ScMatrixRef xResMat;
3621 double nMin = ::std::numeric_limits<double>::max();
3622 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin )
3623 {
3624 double fVecRes = xResMat->GetDouble(0,i);
3625 if (fVecRes > fCurMin)
3626 xResMat->PutDouble( fCurMin, 0,i);
3627 };
3628 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3629 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3630
3631 double nVal = 0.0;
3632 ScAddress aAdr;
3633 ScRange aRange;
3634 size_t nRefInList = 0;
3635 while (nParamCount-- > 0)
3636 {
3637 switch (GetStackType())
3638 {
3639 case svDouble :
3640 {
3641 nVal = GetDouble();
3642 if (nMin > nVal) nMin = nVal;
3643 nFuncFmtType = SvNumFormatType::NUMBER;
3644 }
3645 break;
3646 case svSingleRef :
3647 {
3648 PopSingleRef( aAdr );
3649 ScRefCellValue aCell(mrDoc, aAdr);
3650 if (aCell.hasNumeric())
3651 {
3652 nVal = GetCellValue(aAdr, aCell);
3653 CurFmtToFuncFmt();
3654 if (nMin > nVal) nMin = nVal;
3655 }
3656 else if (bTextAsZero && aCell.hasString())
3657 {
3658 if ( nMin > 0.0 )
3659 nMin = 0.0;
3660 }
3661 }
3662 break;
3663 case svRefList :
3664 {
3665 // bDoMatOp only for non-array value when switching to
3666 // ArrayRefList.
3667 if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc,
3668 nRefArrayPos == std::numeric_limits<size_t>::max()))
3669 {
3670 nRefArrayPos = nRefInList;
3671 }
3672 }
3673 [[fallthrough]];
3674 case svDoubleRef :
3675 {
3676 FormulaError nErr = FormulaError::NONE;
3677 PopDoubleRef( aRange, nParamCount, nRefInList);
3678 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
3679 aValIter.SetInterpreterContext( &mrContext );
3680 if (aValIter.GetFirst(nVal, nErr))
3681 {
3682 if (nMin > nVal)
3683 nMin = nVal;
3684 aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex );
3685 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3686 {
3687 if (nMin > nVal)
3688 nMin = nVal;
3689 }
3690 SetError(nErr);
3691 }
3692 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3693 {
3694 // Update vector element with current value.
3695 MatOpFunc( nRefArrayPos, nMin);
3696
3697 // Reset.
3698 nMin = std::numeric_limits<double>::max();
3699 nVal = 0.0;
3700 nRefArrayPos = std::numeric_limits<size_t>::max();
3701 }
3702 }
3703 break;
3704 case svMatrix :
3705 case svExternalSingleRef:
3706 case svExternalDoubleRef:
3707 {
3708 ScMatrixRef pMat = GetMatrix();
3709 if (pMat)
3710 {
3711 nFuncFmtType = SvNumFormatType::NUMBER;
3712 nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3713 if (nMin > nVal)
3714 nMin = nVal;
3715 }
3716 }
3717 break;
3718 case svString :
3719 {
3720 Pop();
3721 if ( bTextAsZero )
3722 {
3723 if ( nMin > 0.0 )
3724 nMin = 0.0;
3725 }
3726 else
3727 SetError(FormulaError::IllegalParameter);
3728 }
3729 break;
3730 default :
3731 Pop();
3732 SetError(FormulaError::IllegalParameter);
3733 }
3734 }
3735
3736 if (xResMat)
3737 {
3738 // Include value of last non-references-array type and calculate final result.
3739 if (nMin < std::numeric_limits<double>::max())
3740 {
3741 for (SCSIZE i=0; i < nMatRows; ++i)
3742 {
3743 MatOpFunc( i, nMin);
3744 }
3745 }
3746 else
3747 {
3748 /* TODO: the awkward "no value is minimum 0.0" is likely the case
3749 * if a value is numeric_limits::max. Still, that could be a valid
3750 * minimum value as well, but nVal and nMin had been reset after
3751 * the last svRefList... so we may lie here. */
3752 for (SCSIZE i=0; i < nMatRows; ++i)
3753 {
3754 double fVecRes = xResMat->GetDouble(0,i);
3755 if (fVecRes == std::numeric_limits<double>::max())
3756 xResMat->PutDouble( 0.0, 0,i);
3757 }
3758 }
3759 PushMatrix( xResMat);
3760 }
3761 else
3762 {
3763 if (!std::isfinite(nVal))
3764 PushError( GetDoubleErrorValue( nVal));
3765 else if ( nVal < nMin )
3766 PushDouble(0.0); // zero or only empty arguments
3767 else
3768 PushDouble(nMin);
3769 }
3770 }
3771
ScMax(bool bTextAsZero)3772 void ScInterpreter::ScMax( bool bTextAsZero )
3773 {
3774 short nParamCount = GetByte();
3775 if (!MustHaveParamCountMin( nParamCount, 1))
3776 return;
3777
3778 ScMatrixRef xResMat;
3779 double nMax = std::numeric_limits<double>::lowest();
3780 auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax )
3781 {
3782 double fVecRes = xResMat->GetDouble(0,i);
3783 if (fVecRes < fCurMax)
3784 xResMat->PutDouble( fCurMax, 0,i);
3785 };
3786 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3787 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
3788
3789 double nVal = 0.0;
3790 ScAddress aAdr;
3791 ScRange aRange;
3792 size_t nRefInList = 0;
3793 while (nParamCount-- > 0)
3794 {
3795 switch (GetStackType())
3796 {
3797 case svDouble :
3798 {
3799 nVal = GetDouble();
3800 if (nMax < nVal) nMax = nVal;
3801 nFuncFmtType = SvNumFormatType::NUMBER;
3802 }
3803 break;
3804 case svSingleRef :
3805 {
3806 PopSingleRef( aAdr );
3807 ScRefCellValue aCell(mrDoc, aAdr);
3808 if (aCell.hasNumeric())
3809 {
3810 nVal = GetCellValue(aAdr, aCell);
3811 CurFmtToFuncFmt();
3812 if (nMax < nVal) nMax = nVal;
3813 }
3814 else if (bTextAsZero && aCell.hasString())
3815 {
3816 if ( nMax < 0.0 )
3817 nMax = 0.0;
3818 }
3819 }
3820 break;
3821 case svRefList :
3822 {
3823 // bDoMatOp only for non-array value when switching to
3824 // ArrayRefList.
3825 if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc,
3826 nRefArrayPos == std::numeric_limits<size_t>::max()))
3827 {
3828 nRefArrayPos = nRefInList;
3829 }
3830 }
3831 [[fallthrough]];
3832 case svDoubleRef :
3833 {
3834 FormulaError nErr = FormulaError::NONE;
3835 PopDoubleRef( aRange, nParamCount, nRefInList);
3836 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
3837 aValIter.SetInterpreterContext( &mrContext );
3838 if (aValIter.GetFirst(nVal, nErr))
3839 {
3840 if (nMax < nVal)
3841 nMax = nVal;
3842 aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex );
3843 while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr))
3844 {
3845 if (nMax < nVal)
3846 nMax = nVal;
3847 }
3848 SetError(nErr);
3849 }
3850 if (nRefArrayPos != std::numeric_limits<size_t>::max())
3851 {
3852 // Update vector element with current value.
3853 MatOpFunc( nRefArrayPos, nMax);
3854
3855 // Reset.
3856 nMax = std::numeric_limits<double>::lowest();
3857 nVal = 0.0;
3858 nRefArrayPos = std::numeric_limits<size_t>::max();
3859 }
3860 }
3861 break;
3862 case svMatrix :
3863 case svExternalSingleRef:
3864 case svExternalDoubleRef:
3865 {
3866 ScMatrixRef pMat = GetMatrix();
3867 if (pMat)
3868 {
3869 nFuncFmtType = SvNumFormatType::NUMBER;
3870 nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal));
3871 if (nMax < nVal)
3872 nMax = nVal;
3873 }
3874 }
3875 break;
3876 case svString :
3877 {
3878 Pop();
3879 if ( bTextAsZero )
3880 {
3881 if ( nMax < 0.0 )
3882 nMax = 0.0;
3883 }
3884 else
3885 SetError(FormulaError::IllegalParameter);
3886 }
3887 break;
3888 default :
3889 Pop();
3890 SetError(FormulaError::IllegalParameter);
3891 }
3892 }
3893
3894 if (xResMat)
3895 {
3896 // Include value of last non-references-array type and calculate final result.
3897 if (nMax > std::numeric_limits<double>::lowest())
3898 {
3899 for (SCSIZE i=0; i < nMatRows; ++i)
3900 {
3901 MatOpFunc( i, nMax);
3902 }
3903 }
3904 else
3905 {
3906 /* TODO: the awkward "no value is maximum 0.0" is likely the case
3907 * if a value is numeric_limits::lowest. Still, that could be a
3908 * valid maximum value as well, but nVal and nMax had been reset
3909 * after the last svRefList... so we may lie here. */
3910 for (SCSIZE i=0; i < nMatRows; ++i)
3911 {
3912 double fVecRes = xResMat->GetDouble(0,i);
3913 if (fVecRes == -std::numeric_limits<double>::max())
3914 xResMat->PutDouble( 0.0, 0,i);
3915 }
3916 }
3917 PushMatrix( xResMat);
3918 }
3919 else
3920 {
3921 if (!std::isfinite(nVal))
3922 PushError( GetDoubleErrorValue( nVal));
3923 else if ( nVal > nMax )
3924 PushDouble(0.0); // zero or only empty arguments
3925 else
3926 PushDouble(nMax);
3927 }
3928 }
3929
GetStVarParams(bool bTextAsZero,double (* VarResult)(double fVal,size_t nValCount))3930 void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) )
3931 {
3932 short nParamCount = GetByte();
3933 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount);
3934
3935 struct ArrayRefListValue
3936 {
3937 std::vector<double> mvValues;
3938 KahanSum mfSum;
3939 ArrayRefListValue() = default;
3940 double get() const { return mfSum.get(); }
3941 };
3942 std::vector<ArrayRefListValue> vArrayValues;
3943
3944 std::vector<double> values;
3945 KahanSum fSum = 0.0;
3946 double fVal = 0.0;
3947 ScAddress aAdr;
3948 ScRange aRange;
3949 size_t nRefInList = 0;
3950 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
3951 {
3952 switch (GetStackType())
3953 {
3954 case svDouble :
3955 {
3956 fVal = GetDouble();
3957 if (nGlobalError == FormulaError::NONE)
3958 {
3959 values.push_back(fVal);
3960 fSum += fVal;
3961 }
3962 }
3963 break;
3964 case svSingleRef :
3965 {
3966 PopSingleRef( aAdr );
3967 ScRefCellValue aCell(mrDoc, aAdr);
3968 if (aCell.hasNumeric())
3969 {
3970 fVal = GetCellValue(aAdr, aCell);
3971 if (nGlobalError == FormulaError::NONE)
3972 {
3973 values.push_back(fVal);
3974 fSum += fVal;
3975 }
3976 }
3977 else if (bTextAsZero && aCell.hasString())
3978 {
3979 values.push_back(0.0);
3980 }
3981 }
3982 break;
3983 case svRefList :
3984 {
3985 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
3986 if (p && p->IsArrayResult())
3987 {
3988 size_t nRefArrayPos = nRefInList;
3989 if (vArrayValues.empty())
3990 {
3991 // Create and init all elements with current value.
3992 assert(nMatRows > 0);
3993 vArrayValues.resize(nMatRows);
3994 for (ArrayRefListValue & it : vArrayValues)
3995 {
3996 it.mvValues = values;
3997 it.mfSum = fSum;
3998 }
3999 }
4000 else
4001 {
4002 // Current value and values from vector are operands
4003 // for each vector position.
4004 for (ArrayRefListValue & it : vArrayValues)
4005 {
4006 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4007 it.mfSum += fSum;
4008 }
4009 }
4010 ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos];
4011 FormulaError nErr = FormulaError::NONE;
4012 PopDoubleRef( aRange, nParamCount, nRefInList);
4013 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
4014 if (aValIter.GetFirst(fVal, nErr))
4015 {
4016 do
4017 {
4018 rArrayValue.mvValues.push_back(fVal);
4019 rArrayValue.mfSum += fVal;
4020 }
4021 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4022 }
4023 if ( nErr != FormulaError::NONE )
4024 {
4025 rArrayValue.mfSum = CreateDoubleError( nErr);
4026 }
4027 // Reset.
4028 std::vector<double>().swap(values);
4029 fSum = 0.0;
4030 break;
4031 }
4032 }
4033 [[fallthrough]];
4034 case svDoubleRef :
4035 {
4036 FormulaError nErr = FormulaError::NONE;
4037 PopDoubleRef( aRange, nParamCount, nRefInList);
4038 ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero );
4039 if (aValIter.GetFirst(fVal, nErr))
4040 {
4041 do
4042 {
4043 values.push_back(fVal);
4044 fSum += fVal;
4045 }
4046 while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr));
4047 }
4048 if ( nErr != FormulaError::NONE )
4049 {
4050 SetError(nErr);
4051 }
4052 }
4053 break;
4054 case svExternalSingleRef :
4055 case svExternalDoubleRef :
4056 case svMatrix :
4057 {
4058 ScMatrixRef pMat = GetMatrix();
4059 if (pMat)
4060 {
4061 const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal);
4062 SCSIZE nC, nR;
4063 pMat->GetDimensions(nC, nR);
4064 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
4065 {
4066 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
4067 {
4068 if (!pMat->IsStringOrEmpty(nMatCol,nMatRow))
4069 {
4070 fVal= pMat->GetDouble(nMatCol,nMatRow);
4071 if (nGlobalError == FormulaError::NONE)
4072 {
4073 values.push_back(fVal);
4074 fSum += fVal;
4075 }
4076 else if (bIgnoreErrVal)
4077 nGlobalError = FormulaError::NONE;
4078 }
4079 else if ( bTextAsZero )
4080 {
4081 values.push_back(0.0);
4082 }
4083 }
4084 }
4085 }
4086 }
4087 break;
4088 case svString :
4089 {
4090 Pop();
4091 if ( bTextAsZero )
4092 {
4093 values.push_back(0.0);
4094 }
4095 else
4096 SetError(FormulaError::IllegalParameter);
4097 }
4098 break;
4099 default :
4100 Pop();
4101 SetError(FormulaError::IllegalParameter);
4102 }
4103 }
4104
4105 if (!vArrayValues.empty())
4106 {
4107 // Include value of last non-references-array type and calculate final result.
4108 if (!values.empty())
4109 {
4110 for (auto & it : vArrayValues)
4111 {
4112 it.mvValues.insert( it.mvValues.end(), values.begin(), values.end());
4113 it.mfSum += fSum;
4114 }
4115 }
4116 ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true);
4117 for (SCSIZE r=0; r < nMatRows; ++r)
4118 {
4119 ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size();
4120 if (!n)
4121 xResMat->PutError( FormulaError::DivisionByZero, 0, r);
4122 else
4123 {
4124 ArrayRefListValue& rArrayValue = vArrayValues[r];
4125 double vSum = 0.0;
4126 const double vMean = rArrayValue.get() / n;
4127 for (::std::vector<double>::size_type i = 0; i < n; i++)
4128 vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) *
4129 ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean);
4130 xResMat->PutDouble( VarResult( vSum, n), 0, r);
4131 }
4132 }
4133 PushMatrix( xResMat);
4134 }
4135 else
4136 {
4137 ::std::vector<double>::size_type n = values.size();
4138 if (!n)
4139 SetError( FormulaError::DivisionByZero);
4140 double vSum = 0.0;
4141 if (nGlobalError == FormulaError::NONE)
4142 {
4143 const double vMean = fSum.get() / n;
4144 for (::std::vector<double>::size_type i = 0; i < n; i++)
4145 vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
4146 }
4147 PushDouble( VarResult( vSum, n));
4148 }
4149 }
4150
ScVar(bool bTextAsZero)4151 void ScInterpreter::ScVar( bool bTextAsZero )
4152 {
4153 auto VarResult = []( double fVal, size_t nValCount )
4154 {
4155 if (nValCount <= 1)
4156 return CreateDoubleError( FormulaError::DivisionByZero );
4157 else
4158 return fVal / (nValCount - 1);
4159 };
4160 GetStVarParams( bTextAsZero, VarResult );
4161 }
4162
ScVarP(bool bTextAsZero)4163 void ScInterpreter::ScVarP( bool bTextAsZero )
4164 {
4165 auto VarResult = []( double fVal, size_t nValCount )
4166 {
4167 return sc::div( fVal, nValCount);
4168 };
4169 GetStVarParams( bTextAsZero, VarResult );
4170
4171 }
4172
ScStDev(bool bTextAsZero)4173 void ScInterpreter::ScStDev( bool bTextAsZero )
4174 {
4175 auto VarResult = []( double fVal, size_t nValCount )
4176 {
4177 if (nValCount <= 1)
4178 return CreateDoubleError( FormulaError::DivisionByZero );
4179 else
4180 return sqrt( fVal / (nValCount - 1));
4181 };
4182 GetStVarParams( bTextAsZero, VarResult );
4183 }
4184
ScStDevP(bool bTextAsZero)4185 void ScInterpreter::ScStDevP( bool bTextAsZero )
4186 {
4187 auto VarResult = []( double fVal, size_t nValCount )
4188 {
4189 if (nValCount == 0)
4190 return CreateDoubleError( FormulaError::DivisionByZero );
4191 else
4192 return sqrt( fVal / nValCount);
4193 };
4194 GetStVarParams( bTextAsZero, VarResult );
4195
4196 /* this was: PushDouble( sqrt( div( nVal, nValCount)));
4197 *
4198 * Besides that the special NAN gets lost in the call through sqrt(),
4199 * unxlngi6.pro then looped back and forth somewhere between div() and
4200 * ::rtl::math::setNan(). Tests showed that
4201 *
4202 * sqrt( div( 1, 0));
4203 *
4204 * produced a loop, but
4205 *
4206 * double f1 = div( 1, 0);
4207 * sqrt( f1 );
4208 *
4209 * was fine. There seems to be some compiler optimization problem. It does
4210 * not occur when compiled with debug=t.
4211 */
4212 }
4213
ScColumns()4214 void ScInterpreter::ScColumns()
4215 {
4216 sal_uInt8 nParamCount = GetByte();
4217 sal_uLong nVal = 0;
4218 SCCOL nCol1;
4219 SCROW nRow1;
4220 SCTAB nTab1;
4221 SCCOL nCol2;
4222 SCROW nRow2;
4223 SCTAB nTab2;
4224 while (nParamCount-- > 0)
4225 {
4226 switch ( GetStackType() )
4227 {
4228 case svSingleRef:
4229 PopError();
4230 nVal++;
4231 break;
4232 case svDoubleRef:
4233 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4234 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4235 static_cast<sal_uLong>(nCol2 - nCol1 + 1);
4236 break;
4237 case svMatrix:
4238 {
4239 ScMatrixRef pMat = PopMatrix();
4240 if (pMat)
4241 {
4242 SCSIZE nC, nR;
4243 pMat->GetDimensions(nC, nR);
4244 nVal += nC;
4245 }
4246 }
4247 break;
4248 case svExternalSingleRef:
4249 PopError();
4250 nVal++;
4251 break;
4252 case svExternalDoubleRef:
4253 {
4254 sal_uInt16 nFileId;
4255 OUString aTabName;
4256 ScComplexRefData aRef;
4257 PopExternalDoubleRef( nFileId, aTabName, aRef);
4258 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4259 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4260 static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
4261 }
4262 break;
4263 default:
4264 PopError();
4265 SetError(FormulaError::IllegalParameter);
4266 }
4267 }
4268 PushDouble(static_cast<double>(nVal));
4269 }
4270
ScRows()4271 void ScInterpreter::ScRows()
4272 {
4273 sal_uInt8 nParamCount = GetByte();
4274 sal_uLong nVal = 0;
4275 SCCOL nCol1;
4276 SCROW nRow1;
4277 SCTAB nTab1;
4278 SCCOL nCol2;
4279 SCROW nRow2;
4280 SCTAB nTab2;
4281 while (nParamCount-- > 0)
4282 {
4283 switch ( GetStackType() )
4284 {
4285 case svSingleRef:
4286 PopError();
4287 nVal++;
4288 break;
4289 case svDoubleRef:
4290 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4291 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
4292 static_cast<sal_uLong>(nRow2 - nRow1 + 1);
4293 break;
4294 case svMatrix:
4295 {
4296 ScMatrixRef pMat = PopMatrix();
4297 if (pMat)
4298 {
4299 SCSIZE nC, nR;
4300 pMat->GetDimensions(nC, nR);
4301 nVal += nR;
4302 }
4303 }
4304 break;
4305 case svExternalSingleRef:
4306 PopError();
4307 nVal++;
4308 break;
4309 case svExternalDoubleRef:
4310 {
4311 sal_uInt16 nFileId;
4312 OUString aTabName;
4313 ScComplexRefData aRef;
4314 PopExternalDoubleRef( nFileId, aTabName, aRef);
4315 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4316 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
4317 static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
4318 }
4319 break;
4320 default:
4321 PopError();
4322 SetError(FormulaError::IllegalParameter);
4323 }
4324 }
4325 PushDouble(static_cast<double>(nVal));
4326 }
4327
ScSheets()4328 void ScInterpreter::ScSheets()
4329 {
4330 sal_uInt8 nParamCount = GetByte();
4331 sal_uLong nVal;
4332 if ( nParamCount == 0 )
4333 nVal = mrDoc.GetTableCount();
4334 else
4335 {
4336 nVal = 0;
4337 SCCOL nCol1;
4338 SCROW nRow1;
4339 SCTAB nTab1;
4340 SCCOL nCol2;
4341 SCROW nRow2;
4342 SCTAB nTab2;
4343 while (nGlobalError == FormulaError::NONE && nParamCount-- > 0)
4344 {
4345 switch ( GetStackType() )
4346 {
4347 case svSingleRef:
4348 case svExternalSingleRef:
4349 PopError();
4350 nVal++;
4351 break;
4352 case svDoubleRef:
4353 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4354 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
4355 break;
4356 case svExternalDoubleRef:
4357 {
4358 sal_uInt16 nFileId;
4359 OUString aTabName;
4360 ScComplexRefData aRef;
4361 PopExternalDoubleRef( nFileId, aTabName, aRef);
4362 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4363 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
4364 }
4365 break;
4366 default:
4367 PopError();
4368 SetError( FormulaError::IllegalParameter );
4369 }
4370 }
4371 }
4372 PushDouble( static_cast<double>(nVal) );
4373 }
4374
ScColumn()4375 void ScInterpreter::ScColumn()
4376 {
4377 sal_uInt8 nParamCount = GetByte();
4378 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4379 return;
4380
4381 double nVal = 0.0;
4382 if (nParamCount == 0)
4383 {
4384 nVal = aPos.Col() + 1;
4385 if (bMatrixFormula)
4386 {
4387 SCCOL nCols = 0;
4388 SCROW nRows = 0;
4389 if (pMyFormulaCell)
4390 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4391 if (nCols == 0)
4392 {
4393 // Happens if called via ScViewFunc::EnterMatrix()
4394 // ScFormulaCell::GetResultDimensions() as of course a
4395 // matrix result is not available yet.
4396 nCols = 1;
4397 }
4398 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1, /*bEmpty*/true );
4399 if (pResMat)
4400 {
4401 for (SCCOL i=0; i < nCols; ++i)
4402 pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
4403 PushMatrix( pResMat);
4404 return;
4405 }
4406 }
4407 }
4408 else
4409 {
4410 switch ( GetStackType() )
4411 {
4412 case svSingleRef :
4413 {
4414 SCCOL nCol1(0);
4415 SCROW nRow1(0);
4416 SCTAB nTab1(0);
4417 PopSingleRef( nCol1, nRow1, nTab1 );
4418 nVal = static_cast<double>(nCol1 + 1);
4419 }
4420 break;
4421 case svExternalSingleRef :
4422 {
4423 sal_uInt16 nFileId;
4424 OUString aTabName;
4425 ScSingleRefData aRef;
4426 PopExternalSingleRef( nFileId, aTabName, aRef );
4427 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4428 nVal = static_cast<double>( aAbsRef.Col() + 1 );
4429 }
4430 break;
4431
4432 case svDoubleRef :
4433 case svExternalDoubleRef :
4434 {
4435 SCCOL nCol1;
4436 SCCOL nCol2;
4437 if ( GetStackType() == svDoubleRef )
4438 {
4439 SCROW nRow1;
4440 SCTAB nTab1;
4441 SCROW nRow2;
4442 SCTAB nTab2;
4443 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4444 }
4445 else
4446 {
4447 sal_uInt16 nFileId;
4448 OUString aTabName;
4449 ScComplexRefData aRef;
4450 PopExternalDoubleRef( nFileId, aTabName, aRef );
4451 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4452 nCol1 = aAbs.aStart.Col();
4453 nCol2 = aAbs.aEnd.Col();
4454 }
4455 if (nCol2 > nCol1)
4456 {
4457 ScMatrixRef pResMat = GetNewMat(
4458 static_cast<SCSIZE>(nCol2-nCol1+1), 1, /*bEmpty*/true);
4459 if (pResMat)
4460 {
4461 for (SCCOL i = nCol1; i <= nCol2; i++)
4462 pResMat->PutDouble(static_cast<double>(i+1),
4463 static_cast<SCSIZE>(i-nCol1), 0);
4464 PushMatrix(pResMat);
4465 return;
4466 }
4467 }
4468 else
4469 nVal = static_cast<double>(nCol1 + 1);
4470 }
4471 break;
4472 default:
4473 SetError( FormulaError::IllegalParameter );
4474 }
4475 }
4476 PushDouble( nVal );
4477 }
4478
ScRow()4479 void ScInterpreter::ScRow()
4480 {
4481 sal_uInt8 nParamCount = GetByte();
4482 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4483 return;
4484
4485 double nVal = 0.0;
4486 if (nParamCount == 0)
4487 {
4488 nVal = aPos.Row() + 1;
4489 if (bMatrixFormula)
4490 {
4491 SCCOL nCols = 0;
4492 SCROW nRows = 0;
4493 if (pMyFormulaCell)
4494 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4495 if (nRows == 0)
4496 {
4497 // Happens if called via ScViewFunc::EnterMatrix()
4498 // ScFormulaCell::GetResultDimensions() as of course a
4499 // matrix result is not available yet.
4500 nRows = 1;
4501 }
4502 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows), /*bEmpty*/true);
4503 if (pResMat)
4504 {
4505 for (SCROW i=0; i < nRows; i++)
4506 pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
4507 PushMatrix( pResMat);
4508 return;
4509 }
4510 }
4511 }
4512 else
4513 {
4514 switch ( GetStackType() )
4515 {
4516 case svSingleRef :
4517 {
4518 SCCOL nCol1(0);
4519 SCROW nRow1(0);
4520 SCTAB nTab1(0);
4521 PopSingleRef( nCol1, nRow1, nTab1 );
4522 nVal = static_cast<double>(nRow1 + 1);
4523 }
4524 break;
4525 case svExternalSingleRef :
4526 {
4527 sal_uInt16 nFileId;
4528 OUString aTabName;
4529 ScSingleRefData aRef;
4530 PopExternalSingleRef( nFileId, aTabName, aRef );
4531 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
4532 nVal = static_cast<double>( aAbsRef.Row() + 1 );
4533 }
4534 break;
4535 case svDoubleRef :
4536 case svExternalDoubleRef :
4537 {
4538 SCROW nRow1;
4539 SCROW nRow2;
4540 if ( GetStackType() == svDoubleRef )
4541 {
4542 SCCOL nCol1;
4543 SCTAB nTab1;
4544 SCCOL nCol2;
4545 SCTAB nTab2;
4546 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4547 }
4548 else
4549 {
4550 sal_uInt16 nFileId;
4551 OUString aTabName;
4552 ScComplexRefData aRef;
4553 PopExternalDoubleRef( nFileId, aTabName, aRef );
4554 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
4555 nRow1 = aAbs.aStart.Row();
4556 nRow2 = aAbs.aEnd.Row();
4557 }
4558 if (nRow2 > nRow1)
4559 {
4560 ScMatrixRef pResMat = GetNewMat( 1,
4561 static_cast<SCSIZE>(nRow2-nRow1+1), /*bEmpty*/true);
4562 if (pResMat)
4563 {
4564 for (SCROW i = nRow1; i <= nRow2; i++)
4565 pResMat->PutDouble(static_cast<double>(i+1), 0,
4566 static_cast<SCSIZE>(i-nRow1));
4567 PushMatrix(pResMat);
4568 return;
4569 }
4570 }
4571 else
4572 nVal = static_cast<double>(nRow1 + 1);
4573 }
4574 break;
4575 default:
4576 SetError( FormulaError::IllegalParameter );
4577 }
4578 }
4579 PushDouble( nVal );
4580 }
4581
ScSheet()4582 void ScInterpreter::ScSheet()
4583 {
4584 sal_uInt8 nParamCount = GetByte();
4585 if ( !MustHaveParamCount( nParamCount, 0, 1 ) )
4586 return;
4587
4588 SCTAB nVal = 0;
4589 if ( nParamCount == 0 )
4590 nVal = aPos.Tab() + 1;
4591 else
4592 {
4593 switch ( GetStackType() )
4594 {
4595 case svString :
4596 {
4597 svl::SharedString aStr = PopString();
4598 if ( mrDoc.GetTable(aStr.getString(), nVal))
4599 ++nVal;
4600 else
4601 SetError( FormulaError::IllegalArgument );
4602 }
4603 break;
4604 case svSingleRef :
4605 {
4606 SCCOL nCol1(0);
4607 SCROW nRow1(0);
4608 SCTAB nTab1(0);
4609 PopSingleRef(nCol1, nRow1, nTab1);
4610 nVal = nTab1 + 1;
4611 }
4612 break;
4613 case svDoubleRef :
4614 {
4615 SCCOL nCol1;
4616 SCROW nRow1;
4617 SCTAB nTab1;
4618 SCCOL nCol2;
4619 SCROW nRow2;
4620 SCTAB nTab2;
4621 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4622 nVal = nTab1 + 1;
4623 }
4624 break;
4625 default:
4626 SetError( FormulaError::IllegalParameter );
4627 }
4628 if ( nGlobalError != FormulaError::NONE )
4629 nVal = 0;
4630 }
4631 PushDouble( static_cast<double>(nVal) );
4632 }
4633
4634 namespace {
4635
4636 class VectorMatrixAccessor
4637 {
4638 public:
VectorMatrixAccessor(const ScMatrix & rMat,bool bColVec)4639 VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
4640 mrMat(rMat), mbColVec(bColVec) {}
4641
IsEmpty(SCSIZE i) const4642 bool IsEmpty(SCSIZE i) const
4643 {
4644 return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
4645 }
4646
IsEmptyPath(SCSIZE i) const4647 bool IsEmptyPath(SCSIZE i) const
4648 {
4649 return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
4650 }
4651
IsValue(SCSIZE i) const4652 bool IsValue(SCSIZE i) const
4653 {
4654 return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
4655 }
4656
IsStringOrEmpty(SCSIZE i) const4657 bool IsStringOrEmpty(SCSIZE i) const
4658 {
4659 return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0);
4660 }
4661
GetDouble(SCSIZE i) const4662 double GetDouble(SCSIZE i) const
4663 {
4664 return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
4665 }
4666
GetString(SCSIZE i) const4667 OUString GetString(SCSIZE i) const
4668 {
4669 return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
4670 }
4671
GetElementCount() const4672 SCSIZE GetElementCount() const
4673 {
4674 SCSIZE nC, nR;
4675 mrMat.GetDimensions(nC, nR);
4676 return mbColVec ? nR : nC;
4677 }
4678
4679 private:
4680 const ScMatrix& mrMat;
4681 bool mbColVec;
4682 };
4683
4684 /** returns -1 when the matrix value is smaller than the query value, 0 when
4685 they are equal, and 1 when the matrix value is larger than the query
4686 value. */
lcl_CompareMatrix2Query(SCSIZE i,const VectorMatrixAccessor & rMat,const ScQueryEntry & rEntry)4687 sal_Int32 lcl_CompareMatrix2Query(
4688 SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry)
4689 {
4690 if (rMat.IsEmpty(i))
4691 {
4692 /* TODO: in case we introduced query for real empty this would have to
4693 * be changed! */
4694 return -1; // empty always less than anything else
4695 }
4696
4697 /* FIXME: what is an empty path (result of IF(false;true_path) in
4698 * comparisons? */
4699
4700 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
4701 if (rMat.IsValue(i))
4702 {
4703 const double nVal1 = rMat.GetDouble(i);
4704 if (!std::isfinite(nVal1))
4705 {
4706 // XXX Querying for error values is not required, otherwise we'd
4707 // need to check here.
4708 return 1; // error always greater than numeric or string
4709 }
4710
4711 if (bByString)
4712 return -1; // numeric always less than string
4713
4714 const double nVal2 = rEntry.GetQueryItem().mfVal;
4715 // XXX Querying for error values is not required, otherwise we'd need
4716 // to check here and move that check before the bByString check.
4717 if (nVal1 == nVal2)
4718 return 0;
4719
4720 return nVal1 < nVal2 ? -1 : 1;
4721 }
4722
4723 if (!bByString)
4724 return 1; // string always greater than numeric
4725
4726 OUString aStr1 = rMat.GetString(i);
4727 OUString aStr2 = rEntry.GetQueryItem().maString.getString();
4728
4729 return ScGlobal::GetCollator()->compareString(aStr1, aStr2); // case-insensitive
4730 }
4731
4732 /** returns the last item with the identical value as the original item
4733 value. */
lcl_GetLastMatch(SCSIZE & rIndex,const VectorMatrixAccessor & rMat,SCSIZE nMatCount)4734 void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
4735 SCSIZE nMatCount)
4736 {
4737 if (rMat.IsValue(rIndex))
4738 {
4739 double nVal = rMat.GetDouble(rIndex);
4740 while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
4741 nVal == rMat.GetDouble(rIndex+1))
4742 ++rIndex;
4743 }
4744 // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant!
4745 else if (rMat.IsEmptyPath(rIndex))
4746 {
4747 while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
4748 ++rIndex;
4749 }
4750 else if (rMat.IsEmpty(rIndex))
4751 {
4752 while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4753 ++rIndex;
4754 }
4755 else if (rMat.IsStringOrEmpty(rIndex))
4756 {
4757 OUString aStr( rMat.GetString(rIndex));
4758 while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) &&
4759 aStr == rMat.GetString(rIndex+1))
4760 ++rIndex;
4761 }
4762 else
4763 {
4764 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4765 }
4766 }
4767
4768 }
4769
ScMatch()4770 void ScInterpreter::ScMatch()
4771 {
4772
4773 sal_uInt8 nParamCount = GetByte();
4774 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
4775 return;
4776
4777 double fTyp;
4778 if (nParamCount == 3)
4779 fTyp = GetDouble();
4780 else
4781 fTyp = 1.0;
4782 SCCOL nCol1 = 0;
4783 SCROW nRow1 = 0;
4784 SCTAB nTab1 = 0;
4785 SCCOL nCol2 = 0;
4786 SCROW nRow2 = 0;
4787 ScMatrixRef pMatSrc = nullptr;
4788
4789 switch (GetStackType())
4790 {
4791 case svSingleRef:
4792 PopSingleRef( nCol1, nRow1, nTab1);
4793 nCol2 = nCol1;
4794 nRow2 = nRow1;
4795 break;
4796 case svDoubleRef:
4797 {
4798 SCTAB nTab2 = 0;
4799 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4800 if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
4801 {
4802 PushIllegalParameter();
4803 return;
4804 }
4805 }
4806 break;
4807 case svMatrix:
4808 case svExternalDoubleRef:
4809 {
4810 if (GetStackType() == svMatrix)
4811 pMatSrc = PopMatrix();
4812 else
4813 PopExternalDoubleRef(pMatSrc);
4814
4815 if (!pMatSrc)
4816 {
4817 PushIllegalParameter();
4818 return;
4819 }
4820 }
4821 break;
4822 default:
4823 PushIllegalParameter();
4824 return;
4825 }
4826
4827 if (nGlobalError == FormulaError::NONE)
4828 {
4829 double fVal;
4830 ScQueryParam rParam;
4831 rParam.nCol1 = nCol1;
4832 rParam.nRow1 = nRow1;
4833 rParam.nCol2 = nCol2;
4834 rParam.nTab = nTab1;
4835
4836 ScQueryEntry& rEntry = rParam.GetEntry(0);
4837 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4838 rEntry.bDoQuery = true;
4839 if (fTyp < 0.0)
4840 rEntry.eOp = SC_GREATER_EQUAL;
4841 else if (fTyp > 0.0)
4842 rEntry.eOp = SC_LESS_EQUAL;
4843 switch ( GetStackType() )
4844 {
4845 case svDouble:
4846 {
4847 fVal = GetDouble();
4848 rItem.mfVal = fVal;
4849 rItem.meType = ScQueryEntry::ByValue;
4850 }
4851 break;
4852 case svString:
4853 {
4854 rItem.meType = ScQueryEntry::ByString;
4855 rItem.maString = GetString();
4856 }
4857 break;
4858 case svDoubleRef :
4859 case svSingleRef :
4860 {
4861 ScAddress aAdr;
4862 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4863 {
4864 PushInt(0);
4865 return ;
4866 }
4867 ScRefCellValue aCell(mrDoc, aAdr);
4868 if (aCell.hasNumeric())
4869 {
4870 fVal = GetCellValue(aAdr, aCell);
4871 rItem.meType = ScQueryEntry::ByValue;
4872 rItem.mfVal = fVal;
4873 }
4874 else
4875 {
4876 GetCellString(rItem.maString, aCell);
4877 rItem.meType = ScQueryEntry::ByString;
4878 }
4879 }
4880 break;
4881 case svExternalSingleRef:
4882 {
4883 ScExternalRefCache::TokenRef pToken;
4884 PopExternalSingleRef(pToken);
4885 if (nGlobalError != FormulaError::NONE)
4886 {
4887 PushError( nGlobalError);
4888 return;
4889 }
4890 if (pToken->GetType() == svDouble)
4891 {
4892 rItem.meType = ScQueryEntry::ByValue;
4893 rItem.mfVal = pToken->GetDouble();
4894 }
4895 else
4896 {
4897 rItem.meType = ScQueryEntry::ByString;
4898 rItem.maString = pToken->GetString();
4899 }
4900 }
4901 break;
4902 case svExternalDoubleRef:
4903 case svMatrix :
4904 {
4905 svl::SharedString aStr;
4906 ScMatValType nType = GetDoubleOrStringFromMatrix(
4907 rItem.mfVal, aStr);
4908 rItem.maString = aStr;
4909 rItem.meType = ScMatrix::IsNonValueType(nType) ?
4910 ScQueryEntry::ByString : ScQueryEntry::ByValue;
4911 }
4912 break;
4913 default:
4914 {
4915 PushIllegalParameter();
4916 return;
4917 }
4918 }
4919 if (rItem.meType == ScQueryEntry::ByString)
4920 {
4921 bool bIsVBAMode = mrDoc.IsInVBAMode();
4922
4923 if ( bIsVBAMode )
4924 rParam.eSearchType = utl::SearchParam::SearchType::Wildcard;
4925 else
4926 rParam.eSearchType = DetectSearchType(rEntry.GetQueryItem().maString.getString(), mrDoc);
4927 }
4928
4929 if (pMatSrc) // The source data is matrix array.
4930 {
4931 SCSIZE nC, nR;
4932 pMatSrc->GetDimensions( nC, nR);
4933 if (nC > 1 && nR > 1)
4934 {
4935 // The source matrix must be a vector.
4936 PushIllegalParameter();
4937 return;
4938 }
4939
4940 // Do not propagate errors from matrix while searching.
4941 pMatSrc->SetErrorInterpreter( nullptr);
4942
4943 SCSIZE nMatCount = (nC == 1) ? nR : nC;
4944 VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1);
4945
4946 // simple serial search for equality mode (source data doesn't
4947 // need to be sorted).
4948
4949 if (rEntry.eOp == SC_EQUAL)
4950 {
4951 for (SCSIZE i = 0; i < nMatCount; ++i)
4952 {
4953 if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0)
4954 {
4955 PushDouble(i+1); // found !
4956 return;
4957 }
4958 }
4959 PushNA(); // not found
4960 return;
4961 }
4962
4963 // binary search for non-equality mode (the source data is
4964 // assumed to be sorted).
4965
4966 bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
4967 SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
4968 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
4969 {
4970 SCSIZE nMid = nFirst + nLen/2;
4971 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry);
4972 if (nCmp == 0)
4973 {
4974 // exact match. find the last item with the same value.
4975 lcl_GetLastMatch( nMid, aMatAcc, nMatCount);
4976 PushDouble( nMid+1);
4977 return;
4978 }
4979
4980 if (nLen == 1) // first and last items are next to each other.
4981 {
4982 if (nCmp < 0)
4983 nHitIndex = bAscOrder ? nLast : nFirst;
4984 else
4985 nHitIndex = bAscOrder ? nFirst : nLast;
4986 break;
4987 }
4988
4989 if (nCmp < 0)
4990 {
4991 if (bAscOrder)
4992 nFirst = nMid;
4993 else
4994 nLast = nMid;
4995 }
4996 else
4997 {
4998 if (bAscOrder)
4999 nLast = nMid;
5000 else
5001 nFirst = nMid;
5002 }
5003 }
5004
5005 if (nHitIndex == nMatCount-1) // last item
5006 {
5007 sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry);
5008 if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
5009 {
5010 // either the last item is an exact match or the real
5011 // hit is beyond the last item.
5012 PushDouble( nHitIndex+1);
5013 return;
5014 }
5015 }
5016
5017 if (nHitIndex > 0) // valid hit must be 2nd item or higher
5018 {
5019 PushDouble( nHitIndex); // non-exact match
5020 return;
5021 }
5022
5023 PushNA();
5024 return;
5025 }
5026
5027 SCCOLROW nDelta = 0;
5028 if (nCol1 == nCol2)
5029 { // search row in column
5030 rParam.nRow2 = nRow2;
5031 rEntry.nField = nCol1;
5032 ScAddress aResultPos( nCol1, nRow1, nTab1);
5033 if (!LookupQueryWithCache( aResultPos, rParam))
5034 {
5035 PushNA();
5036 return;
5037 }
5038 nDelta = aResultPos.Row() - nRow1;
5039 }
5040 else
5041 { // search column in row
5042 SCCOL nC;
5043 rParam.bByRow = false;
5044 rParam.nRow2 = nRow1;
5045 rEntry.nField = nCol1;
5046 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5047 // Advance Entry.nField in Iterator if column changed
5048 aCellIter.SetAdvanceQueryParamEntryField( true );
5049 if (fTyp == 0.0)
5050 { // EQUAL
5051 if ( aCellIter.GetFirst() )
5052 nC = aCellIter.GetCol();
5053 else
5054 {
5055 PushNA();
5056 return;
5057 }
5058 }
5059 else
5060 { // <= or >=
5061 SCROW nR;
5062 if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
5063 {
5064 PushNA();
5065 return;
5066 }
5067 }
5068 nDelta = nC - nCol1;
5069 }
5070 PushDouble(static_cast<double>(nDelta + 1));
5071 }
5072 else
5073 PushIllegalParameter();
5074 }
5075
5076 namespace {
5077
isCellContentEmpty(const ScRefCellValue & rCell)5078 bool isCellContentEmpty( const ScRefCellValue& rCell )
5079 {
5080 switch (rCell.meType)
5081 {
5082 case CELLTYPE_VALUE:
5083 case CELLTYPE_STRING:
5084 case CELLTYPE_EDIT:
5085 return false;
5086 case CELLTYPE_FORMULA:
5087 {
5088 // NOTE: Excel treats ="" in a referenced cell as blank in
5089 // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
5090 // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
5091 // the cell content.
5092 // ODFF allows both for COUNTBLANK().
5093 // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
5094 // COUNTBLANK(), we now do for Excel interoperability.
5095 /* TODO: introduce yet another compatibility option? */
5096 sc::FormulaResultValue aRes = rCell.mpFormula->GetResult();
5097 if (aRes.meType != sc::FormulaResultValue::String)
5098 return false;
5099 if (!aRes.maString.isEmpty())
5100 return false;
5101 }
5102 break;
5103 default:
5104 ;
5105 }
5106
5107 return true;
5108 }
5109
5110 }
5111
ScCountEmptyCells()5112 void ScInterpreter::ScCountEmptyCells()
5113 {
5114 if ( !MustHaveParamCount( GetByte(), 1 ) )
5115 return;
5116
5117 const SCSIZE nMatRows = GetRefListArrayMaxSize(1);
5118 // There's either one RefList and nothing else, or none.
5119 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5120 sal_uLong nMaxCount = 0, nCount = 0;
5121 switch (GetStackType())
5122 {
5123 case svSingleRef :
5124 {
5125 nMaxCount = 1;
5126 ScAddress aAdr;
5127 PopSingleRef( aAdr );
5128 ScRefCellValue aCell(mrDoc, aAdr);
5129 if (!isCellContentEmpty(aCell))
5130 nCount = 1;
5131 }
5132 break;
5133 case svRefList :
5134 case svDoubleRef :
5135 {
5136 ScRange aRange;
5137 short nParam = 1;
5138 SCSIZE nRefListArrayPos = 0;
5139 size_t nRefInList = 0;
5140 while (nParam-- > 0)
5141 {
5142 nRefListArrayPos = nRefInList;
5143 PopDoubleRef( aRange, nParam, nRefInList);
5144 nMaxCount +=
5145 static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
5146 static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
5147 static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
5148
5149 ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags);
5150 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
5151 {
5152 const ScRefCellValue& rCell = aIter.getRefCellValue();
5153 if (!isCellContentEmpty(rCell))
5154 ++nCount;
5155 }
5156 if (xResMat)
5157 {
5158 xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos);
5159 nMaxCount = nCount = 0;
5160 }
5161 }
5162 }
5163 break;
5164 case svMatrix:
5165 case svExternalSingleRef:
5166 case svExternalDoubleRef:
5167 {
5168 ScMatrixRef xMat = GetMatrix();
5169 if (!xMat)
5170 SetError( FormulaError::IllegalParameter);
5171 else
5172 {
5173 SCSIZE nC, nR;
5174 xMat->GetDimensions( nC, nR);
5175 nMaxCount = nC * nR;
5176 // Numbers (implicit), strings and error values, ignore empty
5177 // strings as those if not entered in an inline array are the
5178 // result of a formula, to be par with a reference to formula
5179 // cell as *visual* blank, see isCellContentEmpty() above.
5180 nCount = xMat->Count( true, true, true);
5181 }
5182 }
5183 break;
5184 default : SetError(FormulaError::IllegalParameter); break;
5185 }
5186 if (xResMat)
5187 PushMatrix( xResMat);
5188 else
5189 PushDouble(nMaxCount - nCount);
5190 }
5191
IterateParametersIf(ScIterFuncIf eFunc)5192 void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc )
5193 {
5194 sal_uInt8 nParamCount = GetByte();
5195 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5196 return;
5197
5198 SCCOL nCol3 = 0;
5199 SCROW nRow3 = 0;
5200 SCTAB nTab3 = 0;
5201
5202 ScMatrixRef pSumExtraMatrix;
5203 bool bSumExtraRange = (nParamCount == 3);
5204 if (bSumExtraRange)
5205 {
5206 // Save only the upperleft cell in case of cell range. The geometry
5207 // of the 3rd parameter is taken from the 1st parameter.
5208
5209 switch ( GetStackType() )
5210 {
5211 case svDoubleRef :
5212 {
5213 SCCOL nColJunk = 0;
5214 SCROW nRowJunk = 0;
5215 SCTAB nTabJunk = 0;
5216 PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
5217 if ( nTabJunk != nTab3 )
5218 {
5219 PushError( FormulaError::IllegalParameter);
5220 return;
5221 }
5222 }
5223 break;
5224 case svSingleRef :
5225 PopSingleRef( nCol3, nRow3, nTab3 );
5226 break;
5227 case svMatrix:
5228 pSumExtraMatrix = PopMatrix();
5229 // nCol3, nRow3, nTab3 remain 0
5230 break;
5231 case svExternalSingleRef:
5232 {
5233 pSumExtraMatrix = GetNewMat(1,1);
5234 ScExternalRefCache::TokenRef pToken;
5235 PopExternalSingleRef(pToken);
5236 if (nGlobalError != FormulaError::NONE)
5237 {
5238 PushError( nGlobalError);
5239 return;
5240 }
5241
5242 if (pToken->GetType() == svDouble)
5243 pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
5244 else
5245 pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);
5246 }
5247 break;
5248 case svExternalDoubleRef:
5249 PopExternalDoubleRef(pSumExtraMatrix);
5250 break;
5251 default:
5252 PushError( FormulaError::IllegalParameter);
5253 return;
5254 }
5255 }
5256
5257 svl::SharedString aString;
5258 double fVal = 0.0;
5259 bool bIsString = true;
5260 switch ( GetStackType() )
5261 {
5262 case svDoubleRef :
5263 case svSingleRef :
5264 {
5265 ScAddress aAdr;
5266 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5267 {
5268 PushError( nGlobalError);
5269 return;
5270 }
5271
5272 ScRefCellValue aCell(mrDoc, aAdr);
5273 switch (aCell.meType)
5274 {
5275 case CELLTYPE_VALUE :
5276 fVal = GetCellValue(aAdr, aCell);
5277 bIsString = false;
5278 break;
5279 case CELLTYPE_FORMULA :
5280 if (aCell.mpFormula->IsValue())
5281 {
5282 fVal = GetCellValue(aAdr, aCell);
5283 bIsString = false;
5284 }
5285 else
5286 GetCellString(aString, aCell);
5287 break;
5288 case CELLTYPE_STRING :
5289 case CELLTYPE_EDIT :
5290 GetCellString(aString, aCell);
5291 break;
5292 default:
5293 fVal = 0.0;
5294 bIsString = false;
5295 }
5296 }
5297 break;
5298 case svString:
5299 aString = GetString();
5300 break;
5301 case svMatrix :
5302 case svExternalDoubleRef:
5303 {
5304 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5305 bIsString = ScMatrix::IsRealStringType( nType);
5306 }
5307 break;
5308 case svExternalSingleRef:
5309 {
5310 ScExternalRefCache::TokenRef pToken;
5311 PopExternalSingleRef(pToken);
5312 if (nGlobalError == FormulaError::NONE)
5313 {
5314 if (pToken->GetType() == svDouble)
5315 {
5316 fVal = pToken->GetDouble();
5317 bIsString = false;
5318 }
5319 else
5320 aString = pToken->GetString();
5321 }
5322 }
5323 break;
5324 default:
5325 {
5326 fVal = GetDouble();
5327 bIsString = false;
5328 }
5329 }
5330
5331 KahanSum fSum = 0.0;
5332 double fRes = 0.0;
5333 double fCount = 0.0;
5334 short nParam = 1;
5335 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5336 // There's either one RefList and nothing else, or none.
5337 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5338 SCSIZE nRefListArrayPos = 0;
5339 size_t nRefInList = 0;
5340 while (nParam-- > 0)
5341 {
5342 SCCOL nCol1 = 0;
5343 SCROW nRow1 = 0;
5344 SCTAB nTab1 = 0;
5345 SCCOL nCol2 = 0;
5346 SCROW nRow2 = 0;
5347 SCTAB nTab2 = 0;
5348 ScMatrixRef pQueryMatrix;
5349 switch ( GetStackType() )
5350 {
5351 case svRefList :
5352 if (bSumExtraRange)
5353 {
5354 /* TODO: this could resolve if all refs are of the same size */
5355 SetError( FormulaError::IllegalParameter);
5356 }
5357 else
5358 {
5359 nRefListArrayPos = nRefInList;
5360 ScRange aRange;
5361 PopDoubleRef( aRange, nParam, nRefInList);
5362 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5363 }
5364 break;
5365 case svDoubleRef :
5366 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
5367 break;
5368 case svSingleRef :
5369 PopSingleRef( nCol1, nRow1, nTab1 );
5370 nCol2 = nCol1;
5371 nRow2 = nRow1;
5372 nTab2 = nTab1;
5373 break;
5374 case svMatrix:
5375 case svExternalSingleRef:
5376 case svExternalDoubleRef:
5377 {
5378 pQueryMatrix = GetMatrix();
5379 if (!pQueryMatrix)
5380 {
5381 PushError( FormulaError::IllegalParameter);
5382 return;
5383 }
5384 nCol1 = 0;
5385 nRow1 = 0;
5386 nTab1 = 0;
5387 SCSIZE nC, nR;
5388 pQueryMatrix->GetDimensions( nC, nR);
5389 nCol2 = static_cast<SCCOL>(nC - 1);
5390 nRow2 = static_cast<SCROW>(nR - 1);
5391 nTab2 = 0;
5392 }
5393 break;
5394 default:
5395 SetError( FormulaError::IllegalParameter);
5396 }
5397 if ( nTab1 != nTab2 )
5398 {
5399 SetError( FormulaError::IllegalParameter);
5400 }
5401
5402 if (bSumExtraRange)
5403 {
5404 // Take the range geometry of the 1st parameter and apply it to
5405 // the 3rd. If parts of the resulting range would point outside
5406 // the sheet, don't complain but silently ignore and simply cut
5407 // them away, this is what Xcl does :-/
5408
5409 // For the cut-away part we also don't need to determine the
5410 // criteria match, so shrink the source range accordingly,
5411 // instead of the result range.
5412 SCCOL nColDelta = nCol2 - nCol1;
5413 SCROW nRowDelta = nRow2 - nRow1;
5414 SCCOL nMaxCol;
5415 SCROW nMaxRow;
5416 if (pSumExtraMatrix)
5417 {
5418 SCSIZE nC, nR;
5419 pSumExtraMatrix->GetDimensions( nC, nR);
5420 nMaxCol = static_cast<SCCOL>(nC - 1);
5421 nMaxRow = static_cast<SCROW>(nR - 1);
5422 }
5423 else
5424 {
5425 nMaxCol = mrDoc.MaxCol();
5426 nMaxRow = mrDoc.MaxRow();
5427 }
5428 if (nCol3 + nColDelta > nMaxCol)
5429 {
5430 SCCOL nNewDelta = nMaxCol - nCol3;
5431 nCol2 = nCol1 + nNewDelta;
5432 }
5433
5434 if (nRow3 + nRowDelta > nMaxRow)
5435 {
5436 SCROW nNewDelta = nMaxRow - nRow3;
5437 nRow2 = nRow1 + nNewDelta;
5438 }
5439 }
5440 else
5441 {
5442 nCol3 = nCol1;
5443 nRow3 = nRow1;
5444 nTab3 = nTab1;
5445 }
5446
5447 if (nGlobalError == FormulaError::NONE)
5448 {
5449 ScQueryParam rParam;
5450 rParam.nRow1 = nRow1;
5451 rParam.nRow2 = nRow2;
5452
5453 ScQueryEntry& rEntry = rParam.GetEntry(0);
5454 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5455 rEntry.bDoQuery = true;
5456 if (!bIsString)
5457 {
5458 rItem.meType = ScQueryEntry::ByValue;
5459 rItem.mfVal = fVal;
5460 rEntry.eOp = SC_EQUAL;
5461 }
5462 else
5463 {
5464 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
5465 if (rItem.meType == ScQueryEntry::ByString)
5466 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5467 }
5468 ScAddress aAdr;
5469 aAdr.SetTab( nTab3 );
5470 rParam.nCol1 = nCol1;
5471 rParam.nCol2 = nCol2;
5472 rEntry.nField = nCol1;
5473 SCCOL nColDiff = nCol3 - nCol1;
5474 SCROW nRowDiff = nRow3 - nRow1;
5475 if (pQueryMatrix)
5476 {
5477 // Never case-sensitive.
5478 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5479 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5480 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5481 {
5482 SetError( FormulaError::IllegalParameter);
5483 }
5484
5485 if (pSumExtraMatrix)
5486 {
5487 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5488 {
5489 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5490 {
5491 if (pResultMatrix->IsValue( nCol, nRow) &&
5492 pResultMatrix->GetDouble( nCol, nRow))
5493 {
5494 SCSIZE nC = nCol + nColDiff;
5495 SCSIZE nR = nRow + nRowDiff;
5496 if (pSumExtraMatrix->IsValue( nC, nR))
5497 {
5498 fVal = pSumExtraMatrix->GetDouble( nC, nR);
5499 ++fCount;
5500 fSum += fVal;
5501 }
5502 }
5503 }
5504 }
5505 }
5506 else
5507 {
5508 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
5509 {
5510 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
5511 {
5512 if (pResultMatrix->GetDouble( nCol, nRow))
5513 {
5514 aAdr.SetCol( nCol + nColDiff);
5515 aAdr.SetRow( nRow + nRowDiff);
5516 ScRefCellValue aCell(mrDoc, aAdr);
5517 if (aCell.hasNumeric())
5518 {
5519 fVal = GetCellValue(aAdr, aCell);
5520 ++fCount;
5521 fSum += fVal;
5522 }
5523 }
5524 }
5525 }
5526 }
5527 }
5528 else
5529 {
5530 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false);
5531 // Increment Entry.nField in iterator when switching to next column.
5532 aCellIter.SetAdvanceQueryParamEntryField( true );
5533 if ( aCellIter.GetFirst() )
5534 {
5535 if (pSumExtraMatrix)
5536 {
5537 do
5538 {
5539 SCSIZE nC = aCellIter.GetCol() + nColDiff;
5540 SCSIZE nR = aCellIter.GetRow() + nRowDiff;
5541 if (pSumExtraMatrix->IsValue( nC, nR))
5542 {
5543 fVal = pSumExtraMatrix->GetDouble( nC, nR);
5544 ++fCount;
5545 fSum += fVal;
5546 }
5547 } while ( aCellIter.GetNext() );
5548 }
5549 else
5550 {
5551 do
5552 {
5553 aAdr.SetCol( aCellIter.GetCol() + nColDiff);
5554 aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
5555 ScRefCellValue aCell(mrDoc, aAdr);
5556 if (aCell.hasNumeric())
5557 {
5558 fVal = GetCellValue(aAdr, aCell);
5559 ++fCount;
5560 fSum += fVal;
5561 }
5562 } while ( aCellIter.GetNext() );
5563 }
5564 }
5565 }
5566 }
5567 else
5568 {
5569 PushError( FormulaError::IllegalParameter);
5570 return;
5571 }
5572
5573 switch( eFunc )
5574 {
5575 case ifSUMIF: fRes = fSum.get(); break;
5576 case ifAVERAGEIF: fRes = div( fSum.get(), fCount ); break;
5577 }
5578 if (xResMat)
5579 {
5580 if (nGlobalError == FormulaError::NONE)
5581 xResMat->PutDouble( fRes, 0, nRefListArrayPos);
5582 else
5583 {
5584 xResMat->PutError( nGlobalError, 0, nRefListArrayPos);
5585 nGlobalError = FormulaError::NONE;
5586 }
5587 fRes = fCount = 0.0;
5588 fSum = 0;
5589 }
5590 }
5591 if (xResMat)
5592 PushMatrix( xResMat);
5593 else
5594 PushDouble( fRes);
5595 }
5596
ScSumIf()5597 void ScInterpreter::ScSumIf()
5598 {
5599 IterateParametersIf( ifSUMIF);
5600 }
5601
ScAverageIf()5602 void ScInterpreter::ScAverageIf()
5603 {
5604 IterateParametersIf( ifAVERAGEIF);
5605 }
5606
ScCountIf()5607 void ScInterpreter::ScCountIf()
5608 {
5609 if ( !MustHaveParamCount( GetByte(), 2 ) )
5610 return;
5611
5612 svl::SharedString aString;
5613 double fVal = 0.0;
5614 bool bIsString = true;
5615 switch ( GetStackType() )
5616 {
5617 case svDoubleRef :
5618 case svSingleRef :
5619 {
5620 ScAddress aAdr;
5621 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5622 {
5623 PushInt(0);
5624 return ;
5625 }
5626 ScRefCellValue aCell(mrDoc, aAdr);
5627 switch (aCell.meType)
5628 {
5629 case CELLTYPE_VALUE :
5630 fVal = GetCellValue(aAdr, aCell);
5631 bIsString = false;
5632 break;
5633 case CELLTYPE_FORMULA :
5634 if (aCell.mpFormula->IsValue())
5635 {
5636 fVal = GetCellValue(aAdr, aCell);
5637 bIsString = false;
5638 }
5639 else
5640 GetCellString(aString, aCell);
5641 break;
5642 case CELLTYPE_STRING :
5643 case CELLTYPE_EDIT :
5644 GetCellString(aString, aCell);
5645 break;
5646 default:
5647 fVal = 0.0;
5648 bIsString = false;
5649 }
5650 }
5651 break;
5652 case svMatrix:
5653 case svExternalSingleRef:
5654 case svExternalDoubleRef:
5655 {
5656 ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString);
5657 bIsString = ScMatrix::IsRealStringType( nType);
5658 }
5659 break;
5660 case svString:
5661 aString = GetString();
5662 break;
5663 default:
5664 {
5665 fVal = GetDouble();
5666 bIsString = false;
5667 }
5668 }
5669 double fCount = 0.0;
5670 short nParam = 1;
5671 const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam);
5672 // There's either one RefList and nothing else, or none.
5673 ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows, /*bEmpty*/true ) : nullptr);
5674 SCSIZE nRefListArrayPos = 0;
5675 size_t nRefInList = 0;
5676 while (nParam-- > 0)
5677 {
5678 SCCOL nCol1 = 0;
5679 SCROW nRow1 = 0;
5680 SCTAB nTab1 = 0;
5681 SCCOL nCol2 = 0;
5682 SCROW nRow2 = 0;
5683 SCTAB nTab2 = 0;
5684 ScMatrixRef pQueryMatrix;
5685 switch ( GetStackType() )
5686 {
5687 case svRefList :
5688 nRefListArrayPos = nRefInList;
5689 [[fallthrough]];
5690 case svDoubleRef :
5691 {
5692 ScRange aRange;
5693 PopDoubleRef( aRange, nParam, nRefInList);
5694 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5695 }
5696 break;
5697 case svSingleRef :
5698 PopSingleRef( nCol1, nRow1, nTab1 );
5699 nCol2 = nCol1;
5700 nRow2 = nRow1;
5701 nTab2 = nTab1;
5702 break;
5703 case svMatrix:
5704 case svExternalSingleRef:
5705 case svExternalDoubleRef:
5706 {
5707 pQueryMatrix = GetMatrix();
5708 if (!pQueryMatrix)
5709 {
5710 PushIllegalParameter();
5711 return;
5712 }
5713 nCol1 = 0;
5714 nRow1 = 0;
5715 nTab1 = 0;
5716 SCSIZE nC, nR;
5717 pQueryMatrix->GetDimensions( nC, nR);
5718 nCol2 = static_cast<SCCOL>(nC - 1);
5719 nRow2 = static_cast<SCROW>(nR - 1);
5720 nTab2 = 0;
5721 }
5722 break;
5723 default:
5724 PopError(); // Propagate it further
5725 PushIllegalParameter();
5726 return ;
5727 }
5728 if ( nTab1 != nTab2 )
5729 {
5730 PushIllegalParameter();
5731 return;
5732 }
5733 if (nCol1 > nCol2)
5734 {
5735 PushIllegalParameter();
5736 return;
5737 }
5738 if (nGlobalError == FormulaError::NONE)
5739 {
5740 ScQueryParam rParam;
5741 rParam.nRow1 = nRow1;
5742 rParam.nRow2 = nRow2;
5743
5744 ScQueryEntry& rEntry = rParam.GetEntry(0);
5745 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5746 rEntry.bDoQuery = true;
5747 if (!bIsString)
5748 {
5749 rItem.meType = ScQueryEntry::ByValue;
5750 rItem.mfVal = fVal;
5751 rEntry.eOp = SC_EQUAL;
5752 }
5753 else
5754 {
5755 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
5756 if (rItem.meType == ScQueryEntry::ByString)
5757 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
5758 }
5759 rParam.nCol1 = nCol1;
5760 rParam.nCol2 = nCol2;
5761 rEntry.nField = nCol1;
5762 if (pQueryMatrix)
5763 {
5764 // Never case-sensitive.
5765 sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType);
5766 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5767 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
5768 {
5769 PushIllegalParameter();
5770 return;
5771 }
5772
5773 SCSIZE nSize = pResultMatrix->GetElementCount();
5774 for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
5775 {
5776 if (pResultMatrix->IsValue( nIndex) &&
5777 pResultMatrix->GetDouble( nIndex))
5778 ++fCount;
5779 }
5780 }
5781 else
5782 {
5783 ScCountIfCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam);
5784 fCount += aCellIter.GetCount();
5785 }
5786 }
5787 else
5788 {
5789 PushIllegalParameter();
5790 return;
5791 }
5792 if (xResMat)
5793 {
5794 xResMat->PutDouble( fCount, 0, nRefListArrayPos);
5795 fCount = 0.0;
5796 }
5797 }
5798 if (xResMat)
5799 PushMatrix( xResMat);
5800 else
5801 PushDouble(fCount);
5802 }
5803
IterateParametersIfs(double (* ResultFunc)(const sc::ParamIfsResult & rRes))5804 void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) )
5805 {
5806 sal_uInt8 nParamCount = GetByte();
5807 sal_uInt8 nQueryCount = nParamCount / 2;
5808
5809 std::vector<sal_uInt32>& vConditions = mrContext.maConditions;
5810 // vConditions is cached, although it is clear'ed after every cell is interpreted,
5811 // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because
5812 // with a single InterpretTail() call it results in evaluation of all the cells in the
5813 // matrix formula.
5814 vConditions.clear();
5815
5816 SCCOL nStartColDiff = 0;
5817 SCCOL nEndColDiff = 0;
5818 SCROW nStartRowDiff = 0;
5819 SCROW nEndRowDiff = 0;
5820 bool bRangeReduce = false;
5821 ScRange aMainRange;
5822
5823 // Range-reduce optimization
5824 if (nParamCount % 2) // Not COUNTIFS
5825 {
5826 bool bHasDoubleRefCriteriaRanges = true;
5827 // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs.
5828 for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 )
5829 {
5830 const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ];
5831 if (pCriteriaRangeToken->GetType() != svDoubleRef )
5832 {
5833 bHasDoubleRefCriteriaRanges = false;
5834 break;
5835 }
5836 }
5837
5838 // Probe the main range token, and try if we can shrink the range without altering results.
5839 const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ];
5840 if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges)
5841 {
5842 const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef();
5843 if (!pRefData->IsDeleted())
5844 {
5845 DoubleRefToRange( *pRefData, aMainRange);
5846
5847 if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab())
5848 {
5849 // Shrink the range to actual data content.
5850 ScRange aSubRange = aMainRange;
5851 mrDoc.GetDataAreaSubrange(aSubRange);
5852
5853 nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col();
5854 nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row();
5855
5856 nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col();
5857 nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row();
5858 bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff;
5859 }
5860 }
5861 }
5862 }
5863
5864 double fVal = 0.0;
5865 SCCOL nDimensionCols = 0;
5866 SCROW nDimensionRows = 0;
5867 const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount);
5868 std::vector<std::vector<sal_uInt32>> vRefArrayConditions;
5869
5870 while (nParamCount > 1 && nGlobalError == FormulaError::NONE)
5871 {
5872 // take criteria
5873 svl::SharedString aString;
5874 fVal = 0.0;
5875 bool bIsString = true;
5876 switch ( GetStackType() )
5877 {
5878 case svDoubleRef :
5879 case svSingleRef :
5880 {
5881 ScAddress aAdr;
5882 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5883 {
5884 PushError( nGlobalError);
5885 return;
5886 }
5887
5888 ScRefCellValue aCell(mrDoc, aAdr);
5889 switch (aCell.meType)
5890 {
5891 case CELLTYPE_VALUE :
5892 fVal = GetCellValue(aAdr, aCell);
5893 bIsString = false;
5894 break;
5895 case CELLTYPE_FORMULA :
5896 if (aCell.mpFormula->IsValue())
5897 {
5898 fVal = GetCellValue(aAdr, aCell);
5899 bIsString = false;
5900 }
5901 else
5902 GetCellString(aString, aCell);
5903 break;
5904 case CELLTYPE_STRING :
5905 case CELLTYPE_EDIT :
5906 GetCellString(aString, aCell);
5907 break;
5908 default:
5909 fVal = 0.0;
5910 bIsString = false;
5911 }
5912 }
5913 break;
5914 case svString:
5915 aString = GetString();
5916 break;
5917 case svMatrix :
5918 case svExternalDoubleRef:
5919 {
5920 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5921 bIsString = ScMatrix::IsRealStringType( nType);
5922 }
5923 break;
5924 case svExternalSingleRef:
5925 {
5926 ScExternalRefCache::TokenRef pToken;
5927 PopExternalSingleRef(pToken);
5928 if (nGlobalError == FormulaError::NONE)
5929 {
5930 if (pToken->GetType() == svDouble)
5931 {
5932 fVal = pToken->GetDouble();
5933 bIsString = false;
5934 }
5935 else
5936 aString = pToken->GetString();
5937 }
5938 }
5939 break;
5940 default:
5941 {
5942 fVal = GetDouble();
5943 bIsString = false;
5944 }
5945 }
5946
5947 if (nGlobalError != FormulaError::NONE)
5948 {
5949 PushError( nGlobalError);
5950 return; // and bail out, no need to evaluate other arguments
5951 }
5952
5953 // take range
5954 short nParam = nParamCount;
5955 size_t nRefInList = 0;
5956 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
5957 SCCOL nCol1 = 0;
5958 SCROW nRow1 = 0;
5959 SCTAB nTab1 = 0;
5960 SCCOL nCol2 = 0;
5961 SCROW nRow2 = 0;
5962 SCTAB nTab2 = 0;
5963 ScMatrixRef pQueryMatrix;
5964 while (nParam-- == nParamCount)
5965 {
5966 switch ( GetStackType() )
5967 {
5968 case svRefList :
5969 {
5970 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
5971 if (p && p->IsArrayResult())
5972 {
5973 if (nRefInList == 0)
5974 {
5975 if (vRefArrayConditions.empty())
5976 vRefArrayConditions.resize( nRefArrayRows);
5977 if (!vConditions.empty())
5978 {
5979 // Similar to other reference list array
5980 // handling, add/op the current value to
5981 // all array positions.
5982 for (auto & rVec : vRefArrayConditions)
5983 {
5984 if (rVec.empty())
5985 rVec = vConditions;
5986 else
5987 {
5988 assert(rVec.size() == vConditions.size()); // see dimensions below
5989 for (size_t i=0, n = rVec.size(); i < n; ++i)
5990 {
5991 rVec[i] += vConditions[i];
5992 }
5993 }
5994 }
5995 // Reset condition results.
5996 std::for_each( vConditions.begin(), vConditions.end(),
5997 [](sal_uInt32 & r){ r = 0.0; } );
5998 }
5999 }
6000 nRefArrayPos = nRefInList;
6001 }
6002 ScRange aRange;
6003 PopDoubleRef( aRange, nParam, nRefInList);
6004 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6005 }
6006 break;
6007 case svDoubleRef :
6008 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
6009 break;
6010 case svSingleRef :
6011 PopSingleRef( nCol1, nRow1, nTab1 );
6012 nCol2 = nCol1;
6013 nRow2 = nRow1;
6014 nTab2 = nTab1;
6015 break;
6016 case svMatrix:
6017 case svExternalSingleRef:
6018 case svExternalDoubleRef:
6019 {
6020 pQueryMatrix = GetMatrix();
6021 if (!pQueryMatrix)
6022 {
6023 PushError( FormulaError::IllegalParameter);
6024 return;
6025 }
6026 nCol1 = 0;
6027 nRow1 = 0;
6028 nTab1 = 0;
6029 SCSIZE nC, nR;
6030 pQueryMatrix->GetDimensions( nC, nR);
6031 nCol2 = static_cast<SCCOL>(nC - 1);
6032 nRow2 = static_cast<SCROW>(nR - 1);
6033 nTab2 = 0;
6034 }
6035 break;
6036 default:
6037 PushError( FormulaError::IllegalParameter);
6038 return;
6039 }
6040 if ( nTab1 != nTab2 )
6041 {
6042 PushError( FormulaError::IllegalArgument);
6043 return;
6044 }
6045
6046 if (bRangeReduce)
6047 {
6048 // All reference ranges must be of the same size as the main range.
6049 if( aMainRange.aEnd.Col() - aMainRange.aStart.Col() != nCol2 - nCol1
6050 || aMainRange.aEnd.Row() - aMainRange.aStart.Row() != nRow2 - nRow1)
6051 {
6052 PushError ( FormulaError::IllegalArgument);
6053 return;
6054 }
6055 nCol1 += nStartColDiff;
6056 nRow1 += nStartRowDiff;
6057
6058 nCol2 += nEndColDiff;
6059 nRow2 += nEndRowDiff;
6060 }
6061
6062 // All reference ranges must be of same dimension and size.
6063 if (!nDimensionCols)
6064 nDimensionCols = nCol2 - nCol1 + 1;
6065 if (!nDimensionRows)
6066 nDimensionRows = nRow2 - nRow1 + 1;
6067 if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1)))
6068 {
6069 PushError ( FormulaError::IllegalArgument);
6070 return;
6071 }
6072
6073 // recalculate matrix values
6074 if (nGlobalError != FormulaError::NONE)
6075 {
6076 PushError( nGlobalError);
6077 return;
6078 }
6079
6080 // initialize temporary result matrix
6081 if (vConditions.empty())
6082 vConditions.resize( nDimensionCols * nDimensionRows, 0);
6083
6084 ScQueryParam rParam;
6085 rParam.nRow1 = nRow1;
6086 rParam.nRow2 = nRow2;
6087
6088 ScQueryEntry& rEntry = rParam.GetEntry(0);
6089 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6090 rEntry.bDoQuery = true;
6091 if (!bIsString)
6092 {
6093 rItem.meType = ScQueryEntry::ByValue;
6094 rItem.mfVal = fVal;
6095 rEntry.eOp = SC_EQUAL;
6096 }
6097 else
6098 {
6099 rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter);
6100 if (rItem.meType == ScQueryEntry::ByString)
6101 rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
6102 }
6103 rParam.nCol1 = nCol1;
6104 rParam.nCol2 = nCol2;
6105 rEntry.nField = nCol1;
6106 SCCOL nColDiff = -nCol1;
6107 SCROW nRowDiff = -nRow1;
6108 if (pQueryMatrix)
6109 {
6110 // Never case-sensitive.
6111 sc::CompareOptions aOptions(mrDoc, rEntry, rParam.eSearchType);
6112 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
6113 if (nGlobalError != FormulaError::NONE || !pResultMatrix)
6114 {
6115 PushError( FormulaError::IllegalParameter);
6116 return;
6117 }
6118
6119 // result matrix is filled with boolean values.
6120 std::vector<double> aResValues;
6121 pResultMatrix->GetDoubleArray(aResValues);
6122 if (vConditions.size() != aResValues.size())
6123 {
6124 PushError( FormulaError::IllegalParameter);
6125 return;
6126 }
6127
6128 std::vector<double>::const_iterator itThisRes = aResValues.begin();
6129 for (auto& rCondition : vConditions)
6130 {
6131 rCondition += *itThisRes;
6132 ++itThisRes;
6133 }
6134 }
6135 else
6136 {
6137 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false);
6138 // Increment Entry.nField in iterator when switching to next column.
6139 aCellIter.SetAdvanceQueryParamEntryField( true );
6140 if ( aCellIter.GetFirst() )
6141 {
6142 do
6143 {
6144 size_t nC = aCellIter.GetCol() + nColDiff;
6145 size_t nR = aCellIter.GetRow() + nRowDiff;
6146 ++vConditions[nC * nDimensionRows + nR];
6147 } while ( aCellIter.GetNext() );
6148 }
6149 }
6150 if (nRefArrayPos != std::numeric_limits<size_t>::max())
6151 {
6152 // Apply condition result to reference list array result position.
6153 std::vector<sal_uInt32>& rVec = vRefArrayConditions[nRefArrayPos];
6154 if (rVec.empty())
6155 rVec = vConditions;
6156 else
6157 {
6158 assert(rVec.size() == vConditions.size()); // see dimensions above
6159 for (size_t i=0, n = rVec.size(); i < n; ++i)
6160 {
6161 rVec[i] += vConditions[i];
6162 }
6163 }
6164 // Reset conditions vector.
6165 // When leaving an svRefList this has to be emptied not set to
6166 // 0.0 because it's checked when entering an svRefList.
6167 if (nRefInList == 0)
6168 std::vector<sal_uInt32>().swap( vConditions);
6169 else
6170 std::for_each( vConditions.begin(), vConditions.end(), [](sal_uInt32 & r){ r = 0.0; } );
6171 }
6172 }
6173 nParamCount -= 2;
6174 }
6175
6176 if (!vRefArrayConditions.empty() && !vConditions.empty())
6177 {
6178 // Add/op the last current value to all array positions.
6179 for (auto & rVec : vRefArrayConditions)
6180 {
6181 if (rVec.empty())
6182 rVec = vConditions;
6183 else
6184 {
6185 assert(rVec.size() == vConditions.size()); // see dimensions above
6186 for (size_t i=0, n = rVec.size(); i < n; ++i)
6187 {
6188 rVec[i] += vConditions[i];
6189 }
6190 }
6191 }
6192 }
6193
6194 if (nGlobalError != FormulaError::NONE)
6195 {
6196 PushError( nGlobalError);
6197 return; // bail out
6198 }
6199
6200 sc::ParamIfsResult aRes;
6201 ScMatrixRef xResMat;
6202
6203 // main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS
6204 if (nParamCount == 1)
6205 {
6206 short nParam = nParamCount;
6207 size_t nRefInList = 0;
6208 size_t nRefArrayPos = std::numeric_limits<size_t>::max();
6209 bool bRefArrayMain = false;
6210 while (nParam-- == nParamCount)
6211 {
6212 SCCOL nMainCol1 = 0;
6213 SCROW nMainRow1 = 0;
6214 SCTAB nMainTab1 = 0;
6215 SCCOL nMainCol2 = 0;
6216 SCROW nMainRow2 = 0;
6217 SCTAB nMainTab2 = 0;
6218 ScMatrixRef pMainMatrix;
6219 switch ( GetStackType() )
6220 {
6221 case svRefList :
6222 {
6223 const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]);
6224 if (p && p->IsArrayResult())
6225 {
6226 if (vRefArrayConditions.empty())
6227 {
6228 // Replicate conditions if there wasn't a
6229 // reference list array for criteria
6230 // evaluation.
6231 vRefArrayConditions.resize( nRefArrayRows);
6232 for (auto & rVec : vRefArrayConditions)
6233 {
6234 rVec = vConditions;
6235 }
6236 }
6237
6238 bRefArrayMain = true;
6239 nRefArrayPos = nRefInList;
6240 }
6241 ScRange aRange;
6242 PopDoubleRef( aRange, nParam, nRefInList);
6243 aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2);
6244 }
6245 break;
6246 case svDoubleRef :
6247 PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 );
6248 break;
6249 case svSingleRef :
6250 PopSingleRef( nMainCol1, nMainRow1, nMainTab1 );
6251 nMainCol2 = nMainCol1;
6252 nMainRow2 = nMainRow1;
6253 nMainTab2 = nMainTab1;
6254 break;
6255 case svMatrix:
6256 case svExternalSingleRef:
6257 case svExternalDoubleRef:
6258 {
6259 pMainMatrix = GetMatrix();
6260 if (!pMainMatrix)
6261 {
6262 PushError( FormulaError::IllegalParameter);
6263 return;
6264 }
6265 nMainCol1 = 0;
6266 nMainRow1 = 0;
6267 nMainTab1 = 0;
6268 SCSIZE nC, nR;
6269 pMainMatrix->GetDimensions( nC, nR);
6270 nMainCol2 = static_cast<SCCOL>(nC - 1);
6271 nMainRow2 = static_cast<SCROW>(nR - 1);
6272 nMainTab2 = 0;
6273 }
6274 break;
6275 // Treat a scalar value as 1x1 matrix.
6276 case svDouble:
6277 pMainMatrix = GetNewMat(1,1);
6278 nMainCol1 = nMainCol2 = 0;
6279 nMainRow1 = nMainRow2 = 0;
6280 nMainTab1 = nMainTab2 = 0;
6281 pMainMatrix->PutDouble( GetDouble(), 0, 0);
6282 break;
6283 case svString:
6284 pMainMatrix = GetNewMat(1,1);
6285 nMainCol1 = nMainCol2 = 0;
6286 nMainRow1 = nMainRow2 = 0;
6287 nMainTab1 = nMainTab2 = 0;
6288 pMainMatrix->PutString( GetString(), 0, 0);
6289 break;
6290 default:
6291 PopError();
6292 PushError( FormulaError::IllegalParameter);
6293 return;
6294 }
6295 if ( nMainTab1 != nMainTab2 )
6296 {
6297 PushError( FormulaError::IllegalArgument);
6298 return;
6299 }
6300
6301 if (bRangeReduce)
6302 {
6303 nMainCol1 += nStartColDiff;
6304 nMainRow1 += nStartRowDiff;
6305
6306 nMainCol2 += nEndColDiff;
6307 nMainRow2 += nEndRowDiff;
6308 }
6309
6310 // All reference ranges must be of same dimension and size.
6311 if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1)))
6312 {
6313 PushError ( FormulaError::IllegalArgument);
6314 return;
6315 }
6316
6317 if (nGlobalError != FormulaError::NONE)
6318 {
6319 PushError( nGlobalError);
6320 return; // bail out
6321 }
6322
6323 // end-result calculation
6324
6325 // This gets weird... if conditions were calculated using a
6326 // reference list array but the main calculation range is not a
6327 // reference list array, then the conditions of the array are
6328 // applied to the main range each in turn to form the array result.
6329
6330 size_t nRefArrayMainPos = (bRefArrayMain ? nRefArrayPos :
6331 (vRefArrayConditions.empty() ? std::numeric_limits<size_t>::max() : 0));
6332 const bool bAppliedArray = (!bRefArrayMain && nRefArrayMainPos == 0);
6333
6334 if (nRefArrayMainPos == 0)
6335 xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true );
6336
6337 if (pMainMatrix)
6338 {
6339 std::vector<double> aMainValues;
6340 pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's.
6341
6342 do
6343 {
6344 if (nRefArrayMainPos < vRefArrayConditions.size())
6345 vConditions = vRefArrayConditions[nRefArrayMainPos];
6346
6347 if (vConditions.size() != aMainValues.size())
6348 {
6349 PushError( FormulaError::IllegalArgument);
6350 return;
6351 }
6352
6353 std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end();
6354 std::vector<double>::const_iterator itMain = aMainValues.begin();
6355 for (; itRes != itResEnd; ++itRes, ++itMain)
6356 {
6357 if (*itRes != nQueryCount)
6358 continue;
6359
6360 fVal = *itMain;
6361 if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN)
6362 continue;
6363
6364 ++aRes.mfCount;
6365 aRes.mfSum += fVal;
6366 if ( aRes.mfMin > fVal )
6367 aRes.mfMin = fVal;
6368 if ( aRes.mfMax < fVal )
6369 aRes.mfMax = fVal;
6370 }
6371 if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
6372 {
6373 xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
6374 aRes = sc::ParamIfsResult();
6375 }
6376 }
6377 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
6378 }
6379 else
6380 {
6381 ScAddress aAdr;
6382 aAdr.SetTab( nMainTab1 );
6383 do
6384 {
6385 if (nRefArrayMainPos < vRefArrayConditions.size())
6386 vConditions = vRefArrayConditions[nRefArrayMainPos];
6387
6388 std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin();
6389 for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol)
6390 {
6391 for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes)
6392 {
6393 if (*itRes == nQueryCount)
6394 {
6395 aAdr.SetCol( nCol + nMainCol1);
6396 aAdr.SetRow( nRow + nMainRow1);
6397 ScRefCellValue aCell(mrDoc, aAdr);
6398 if (aCell.hasNumeric())
6399 {
6400 fVal = GetCellValue(aAdr, aCell);
6401 ++aRes.mfCount;
6402 aRes.mfSum += fVal;
6403 if ( aRes.mfMin > fVal )
6404 aRes.mfMin = fVal;
6405 if ( aRes.mfMax < fVal )
6406 aRes.mfMax = fVal;
6407 }
6408 }
6409 }
6410 }
6411 if (nRefArrayMainPos != std::numeric_limits<size_t>::max())
6412 {
6413 xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos);
6414 aRes = sc::ParamIfsResult();
6415 }
6416 }
6417 while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows);
6418 }
6419 }
6420 }
6421 else
6422 {
6423 // COUNTIFS only.
6424 if (vRefArrayConditions.empty())
6425 {
6426 for (auto const & rCond : vConditions)
6427 {
6428 if (rCond == nQueryCount)
6429 ++aRes.mfCount;
6430 }
6431 }
6432 else
6433 {
6434 xResMat = GetNewMat( 1, nRefArrayRows, /*bEmpty*/true );
6435 for (size_t i=0, n = vRefArrayConditions.size(); i < n; ++i)
6436 {
6437 double fCount = 0.0;
6438 for (auto const & rCond : vRefArrayConditions[i])
6439 {
6440 if (rCond == nQueryCount)
6441 ++fCount;
6442 }
6443 xResMat->PutDouble( fCount, 0, i);
6444 }
6445 }
6446 }
6447
6448 if (xResMat)
6449 PushMatrix( xResMat);
6450 else
6451 PushDouble( ResultFunc( aRes));
6452 }
6453
ScSumIfs()6454 void ScInterpreter::ScSumIfs()
6455 {
6456 // ScMutationGuard aShouldFail(pDok, ScMutationGuardFlags::CORE);
6457 sal_uInt8 nParamCount = GetByte();
6458
6459 if (nParamCount < 3 || (nParamCount % 2 != 1))
6460 {
6461 PushError( FormulaError::ParameterExpected);
6462 return;
6463 }
6464
6465 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6466 {
6467 return rRes.mfSum.get();
6468 };
6469 IterateParametersIfs(ResultFunc);
6470 }
6471
ScAverageIfs()6472 void ScInterpreter::ScAverageIfs()
6473 {
6474 sal_uInt8 nParamCount = GetByte();
6475
6476 if (nParamCount < 3 || (nParamCount % 2 != 1))
6477 {
6478 PushError( FormulaError::ParameterExpected);
6479 return;
6480 }
6481
6482 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6483 {
6484 return sc::div( rRes.mfSum.get(), rRes.mfCount);
6485 };
6486 IterateParametersIfs(ResultFunc);
6487 }
6488
ScCountIfs()6489 void ScInterpreter::ScCountIfs()
6490 {
6491 sal_uInt8 nParamCount = GetByte();
6492
6493 if (nParamCount < 2 || (nParamCount % 2 != 0))
6494 {
6495 PushError( FormulaError::ParameterExpected);
6496 return;
6497 }
6498
6499 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6500 {
6501 return rRes.mfCount;
6502 };
6503 IterateParametersIfs(ResultFunc);
6504 }
6505
ScMinIfs_MS()6506 void ScInterpreter::ScMinIfs_MS()
6507 {
6508 sal_uInt8 nParamCount = GetByte();
6509
6510 if (nParamCount < 3 || (nParamCount % 2 != 1))
6511 {
6512 PushError( FormulaError::ParameterExpected);
6513 return;
6514 }
6515
6516 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6517 {
6518 return (rRes.mfMin < std::numeric_limits<double>::max()) ? rRes.mfMin : 0.0;
6519 };
6520 IterateParametersIfs(ResultFunc);
6521 }
6522
6523
ScMaxIfs_MS()6524 void ScInterpreter::ScMaxIfs_MS()
6525 {
6526 sal_uInt8 nParamCount = GetByte();
6527
6528 if (nParamCount < 3 || (nParamCount % 2 != 1))
6529 {
6530 PushError( FormulaError::ParameterExpected);
6531 return;
6532 }
6533
6534 auto ResultFunc = []( const sc::ParamIfsResult& rRes )
6535 {
6536 return (rRes.mfMax > std::numeric_limits<double>::lowest()) ? rRes.mfMax : 0.0;
6537 };
6538 IterateParametersIfs(ResultFunc);
6539 }
6540
ScLookup()6541 void ScInterpreter::ScLookup()
6542 {
6543 sal_uInt8 nParamCount = GetByte();
6544 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
6545 return ;
6546
6547 ScMatrixRef pDataMat = nullptr, pResMat = nullptr;
6548 SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0;
6549 SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0;
6550 SCTAB nTab1 = 0, nResTab = 0;
6551 SCSIZE nLenMajor = 0; // length of major direction
6552 bool bVertical = true; // whether to lookup vertically or horizontally
6553
6554 // The third parameter, result array, double, string and reference.
6555 double fResVal = 0.0;
6556 svl::SharedString aResStr;
6557 StackVar eResArrayType = svUnknown;
6558
6559 if (nParamCount == 3)
6560 {
6561 eResArrayType = GetStackType();
6562 switch (eResArrayType)
6563 {
6564 case svDoubleRef:
6565 {
6566 SCTAB nTabJunk;
6567 PopDoubleRef(nResCol1, nResRow1, nResTab,
6568 nResCol2, nResRow2, nTabJunk);
6569 if (nResTab != nTabJunk ||
6570 ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0))
6571 {
6572 // The result array must be a vector.
6573 PushIllegalParameter();
6574 return;
6575 }
6576 }
6577 break;
6578 case svSingleRef:
6579 PopSingleRef( nResCol1, nResRow1, nResTab);
6580 nResCol2 = nResCol1;
6581 nResRow2 = nResRow1;
6582 break;
6583 case svMatrix:
6584 case svExternalSingleRef:
6585 case svExternalDoubleRef:
6586 {
6587 pResMat = GetMatrix();
6588 if (!pResMat)
6589 {
6590 PushIllegalParameter();
6591 return;
6592 }
6593 SCSIZE nC, nR;
6594 pResMat->GetDimensions(nC, nR);
6595 if (nC != 1 && nR != 1)
6596 {
6597 // Result matrix must be a vector.
6598 PushIllegalParameter();
6599 return;
6600 }
6601 }
6602 break;
6603 case svDouble:
6604 fResVal = GetDouble();
6605 break;
6606 case svString:
6607 aResStr = GetString();
6608 break;
6609 default:
6610 PushIllegalParameter();
6611 return;
6612 }
6613 }
6614
6615 // For double, string and single reference.
6616 double fDataVal = 0.0;
6617 svl::SharedString aDataStr;
6618 ScAddress aDataAdr;
6619 bool bValueData = false;
6620
6621 // Get the data-result range and also determine whether this is vertical
6622 // lookup or horizontal lookup.
6623
6624 StackVar eDataArrayType = GetStackType();
6625 switch (eDataArrayType)
6626 {
6627 case svDoubleRef:
6628 {
6629 SCTAB nTabJunk;
6630 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk);
6631 if (nTab1 != nTabJunk)
6632 {
6633 PushIllegalParameter();
6634 return;
6635 }
6636 bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1);
6637 nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1;
6638 }
6639 break;
6640 case svMatrix:
6641 case svExternalSingleRef:
6642 case svExternalDoubleRef:
6643 {
6644 pDataMat = GetMatrix();
6645 if (!pDataMat)
6646 {
6647 PushIllegalParameter();
6648 return;
6649 }
6650
6651 SCSIZE nC, nR;
6652 pDataMat->GetDimensions(nC, nR);
6653 bVertical = (nR >= nC);
6654 nLenMajor = bVertical ? nR : nC;
6655 }
6656 break;
6657 case svDouble:
6658 {
6659 fDataVal = GetDouble();
6660 bValueData = true;
6661 }
6662 break;
6663 case svString:
6664 {
6665 aDataStr = GetString();
6666 }
6667 break;
6668 case svSingleRef:
6669 {
6670 PopSingleRef( aDataAdr );
6671 ScRefCellValue aCell(mrDoc, aDataAdr);
6672 if (aCell.hasEmptyValue())
6673 {
6674 // Empty cells aren't found anywhere, bail out early.
6675 SetError( FormulaError::NotAvailable);
6676 }
6677 else if (aCell.hasNumeric())
6678 {
6679 fDataVal = GetCellValue(aDataAdr, aCell);
6680 bValueData = true;
6681 }
6682 else
6683 GetCellString(aDataStr, aCell);
6684 }
6685 break;
6686 default:
6687 SetError( FormulaError::IllegalParameter);
6688 }
6689
6690 if (nGlobalError != FormulaError::NONE)
6691 {
6692 PushError( nGlobalError);
6693 return;
6694 }
6695
6696 // Get the lookup value.
6697
6698 ScQueryParam aParam;
6699 ScQueryEntry& rEntry = aParam.GetEntry(0);
6700 if ( !FillEntry(rEntry) )
6701 return;
6702
6703 if ( eDataArrayType == svDouble || eDataArrayType == svString ||
6704 eDataArrayType == svSingleRef )
6705 {
6706 // Delta position for a single value is always 0.
6707
6708 // Found if data <= query, but not if query is string and found data is
6709 // numeric or vice versa. This is how Excel does it but doesn't
6710 // document it.
6711
6712 bool bFound = false;
6713 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6714
6715 if ( bValueData )
6716 {
6717 if (rItem.meType == ScQueryEntry::ByString)
6718 bFound = false;
6719 else
6720 bFound = (fDataVal <= rItem.mfVal);
6721 }
6722 else
6723 {
6724 if (rItem.meType != ScQueryEntry::ByString)
6725 bFound = false;
6726 else
6727 bFound = (ScGlobal::GetCollator()->compareString(aDataStr.getString(), rItem.maString.getString()) <= 0);
6728 }
6729
6730 if (!bFound)
6731 {
6732 PushNA();
6733 return;
6734 }
6735
6736 if (pResMat)
6737 {
6738 if (pResMat->IsValue( 0, 0 ))
6739 PushDouble(pResMat->GetDouble( 0, 0 ));
6740 else
6741 PushString(pResMat->GetString(0, 0));
6742 }
6743 else if (nParamCount == 3)
6744 {
6745 switch (eResArrayType)
6746 {
6747 case svDouble:
6748 PushDouble( fResVal );
6749 break;
6750 case svString:
6751 PushString( aResStr );
6752 break;
6753 case svDoubleRef:
6754 case svSingleRef:
6755 PushCellResultToken( true, ScAddress( nResCol1, nResRow1, nResTab), nullptr, nullptr);
6756 break;
6757 default:
6758 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, single value data");
6759 PushIllegalParameter();
6760 }
6761 }
6762 else
6763 {
6764 switch (eDataArrayType)
6765 {
6766 case svDouble:
6767 PushDouble( fDataVal );
6768 break;
6769 case svString:
6770 PushString( aDataStr );
6771 break;
6772 case svSingleRef:
6773 PushCellResultToken( true, aDataAdr, nullptr, nullptr);
6774 break;
6775 default:
6776 assert(!"ScInterpreter::ScLookup: unhandled eDataArrayType, single value data");
6777 PushIllegalParameter();
6778 }
6779 }
6780 return;
6781 }
6782
6783 // Now, perform the search to compute the delta position (nDelta).
6784
6785 if (pDataMat)
6786 {
6787 // Data array is given as a matrix.
6788 rEntry.bDoQuery = true;
6789 rEntry.eOp = SC_LESS_EQUAL;
6790 bool bFound = false;
6791
6792 SCSIZE nC, nR;
6793 pDataMat->GetDimensions(nC, nR);
6794
6795 // Do not propagate errors from matrix while copying to vector.
6796 pDataMat->SetErrorInterpreter( nullptr);
6797
6798 // Excel has an undocumented behaviour in that it seems to internally
6799 // sort an interim array (i.e. error values specifically #DIV/0! are
6800 // sorted to the end) or ignore error values that makes these "get last
6801 // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A)
6802 // see tdf#117016
6803 // Instead of sorting a million entries of which mostly only a bunch of
6804 // rows are filled and moving error values to the end which most are
6805 // already anyway, assume the matrix to be sorted except error values
6806 // and omit the coded DoubleError values.
6807 // Do this only for a numeric matrix (that includes errors coded as
6808 // doubles), which covers the case in question.
6809 /* TODO: it's unclear whether this really matches Excel behaviour in
6810 * all constellations or if there are cases that include unsorted error
6811 * values and thus yield arbitrary binary search results or something
6812 * different or whether there are cases where error values are also
6813 * omitted from mixed numeric/string arrays or if it's not an interim
6814 * matrix but a cell range reference instead. */
6815 const bool bOmitErrorValues = (eDataArrayType == svMatrix && pDataMat->IsNumeric());
6816
6817 // In case of non-vector matrix, only search the first row or column.
6818 ScMatrixRef pDataMat2;
6819 std::vector<SCCOLROW> vIndex;
6820 if (bOmitErrorValues)
6821 {
6822 std::vector<double> vArray;
6823 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
6824 const SCSIZE nElements = aMatAcc.GetElementCount();
6825 for (SCSIZE i=0; i < nElements; ++i)
6826 {
6827 const double fVal = aMatAcc.GetDouble(i);
6828 if (std::isfinite(fVal))
6829 {
6830 vArray.push_back(fVal);
6831 vIndex.push_back(i);
6832 }
6833 }
6834 if (vArray.empty())
6835 {
6836 PushNA();
6837 return;
6838 }
6839 const size_t nElems = vArray.size();
6840 if (nElems == nElements)
6841 {
6842 // No error value omitted, use as is.
6843 pDataMat2 = pDataMat;
6844 std::vector<SCCOLROW>().swap( vIndex);
6845 }
6846 else
6847 {
6848 nLenMajor = nElems;
6849 if (bVertical)
6850 {
6851 ScMatrixRef pTempMat = GetNewMat( 1, nElems, /*bEmpty*/true );
6852 pTempMat->PutDoubleVector( vArray, 0, 0);
6853 pDataMat2 = pTempMat;
6854 }
6855 else
6856 {
6857 ScMatrixRef pTempMat = GetNewMat( nElems, 1, /*bEmpty*/true );
6858 for (size_t i=0; i < nElems; ++i)
6859 pTempMat->PutDouble( vArray[i], i, 0);
6860 pDataMat2 = pTempMat;
6861 }
6862 }
6863 }
6864 else
6865 {
6866 // Just use as is with the VectorMatrixAccessor.
6867 pDataMat2 = pDataMat;
6868 }
6869
6870 // Do not propagate errors from matrix while searching.
6871 pDataMat2->SetErrorInterpreter( nullptr);
6872
6873 VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical);
6874
6875 // binary search for non-equality mode (the source data is
6876 // assumed to be sorted in ascending order).
6877
6878 SCCOLROW nDelta = -1;
6879
6880 SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0;
6881 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
6882 {
6883 SCSIZE nMid = nFirst + nLen/2;
6884 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry);
6885 if (nCmp == 0)
6886 {
6887 // exact match. find the last item with the same value.
6888 lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor);
6889 nDelta = nMid;
6890 bFound = true;
6891 break;
6892 }
6893
6894 if (nLen == 1) // first and last items are next to each other.
6895 {
6896 nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1;
6897 // If already the 1st item is greater there's nothing found.
6898 bFound = (nDelta >= 0);
6899 break;
6900 }
6901
6902 if (nCmp < 0)
6903 nFirst = nMid;
6904 else
6905 nLast = nMid;
6906 }
6907
6908 if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item
6909 {
6910 sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry);
6911 if (nCmp <= 0)
6912 {
6913 // either the last item is an exact match or the real
6914 // hit is beyond the last item.
6915 nDelta += 1;
6916 bFound = true;
6917 }
6918 }
6919 else if (nDelta > 0) // valid hit must be 2nd item or higher
6920 {
6921 // non-exact match
6922 bFound = true;
6923 }
6924
6925 // With 0-9 < A-Z, if query is numeric and data found is string, or
6926 // vice versa, the (yet another undocumented) Excel behavior is to
6927 // return #N/A instead.
6928
6929 if (bFound)
6930 {
6931 if (!vIndex.empty())
6932 nDelta = vIndex[nDelta];
6933
6934 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
6935 SCCOLROW i = nDelta;
6936 SCSIZE n = aMatAcc.GetElementCount();
6937 if (o3tl::make_unsigned(i) >= n)
6938 i = static_cast<SCCOLROW>(n);
6939 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
6940 if (bByString == aMatAcc.IsValue(i))
6941 bFound = false;
6942 }
6943
6944 if (!bFound)
6945 {
6946 PushNA();
6947 return;
6948 }
6949
6950 // Now that we've found the delta, push the result back to the cell.
6951
6952 if (pResMat)
6953 {
6954 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
6955 // result array is matrix.
6956 if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount())
6957 {
6958 PushNA();
6959 return;
6960 }
6961 if (aResMatAcc.IsValue(nDelta))
6962 PushDouble(aResMatAcc.GetDouble(nDelta));
6963 else
6964 PushString(aResMatAcc.GetString(nDelta));
6965 }
6966 else if (nParamCount == 3)
6967 {
6968 /* TODO: the entire switch is a copy of the cell range search
6969 * result, factor out. */
6970 switch (eResArrayType)
6971 {
6972 case svDoubleRef:
6973 case svSingleRef:
6974 {
6975 // Use the result array vector. Note that the result array is assumed
6976 // to be a vector (i.e. 1-dimensional array).
6977
6978 ScAddress aAdr;
6979 aAdr.SetTab(nResTab);
6980 bool bResVertical = (nResRow2 - nResRow1) > 0;
6981 if (bResVertical)
6982 {
6983 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
6984 if (nTempRow > mrDoc.MaxRow())
6985 {
6986 PushDouble(0);
6987 return;
6988 }
6989 aAdr.SetCol(nResCol1);
6990 aAdr.SetRow(nTempRow);
6991 }
6992 else
6993 {
6994 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
6995 if (nTempCol > mrDoc.MaxCol())
6996 {
6997 PushDouble(0);
6998 return;
6999 }
7000 aAdr.SetCol(nTempCol);
7001 aAdr.SetRow(nResRow1);
7002 }
7003 PushCellResultToken( true, aAdr, nullptr, nullptr);
7004 }
7005 break;
7006 case svDouble:
7007 case svString:
7008 {
7009 if (nDelta != 0)
7010 PushNA();
7011 else
7012 {
7013 switch (eResArrayType)
7014 {
7015 case svDouble:
7016 PushDouble( fResVal );
7017 break;
7018 case svString:
7019 PushString( aResStr );
7020 break;
7021 default:
7022 ; // nothing
7023 }
7024 }
7025 }
7026 break;
7027 default:
7028 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, array search");
7029 PushIllegalParameter();
7030 }
7031 }
7032 else
7033 {
7034 // No result array. Use the data array to get the final value from.
7035 // Propagate errors from matrix again.
7036 pDataMat->SetErrorInterpreter( this);
7037 if (bVertical)
7038 {
7039 if (pDataMat->IsValue(nC-1, nDelta))
7040 PushDouble(pDataMat->GetDouble(nC-1, nDelta));
7041 else
7042 PushString(pDataMat->GetString(nC-1, nDelta));
7043 }
7044 else
7045 {
7046 if (pDataMat->IsValue(nDelta, nR-1))
7047 PushDouble(pDataMat->GetDouble(nDelta, nR-1));
7048 else
7049 PushString(pDataMat->GetString(nDelta, nR-1));
7050 }
7051 }
7052
7053 return;
7054 }
7055
7056 // Perform cell range search.
7057
7058 aParam.nCol1 = nCol1;
7059 aParam.nRow1 = nRow1;
7060 aParam.nCol2 = bVertical ? nCol1 : nCol2;
7061 aParam.nRow2 = bVertical ? nRow2 : nRow1;
7062 aParam.bByRow = bVertical;
7063
7064 rEntry.bDoQuery = true;
7065 rEntry.eOp = SC_LESS_EQUAL;
7066 rEntry.nField = nCol1;
7067 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7068 if (rItem.meType == ScQueryEntry::ByString)
7069 aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
7070
7071 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, aParam, false);
7072 SCCOL nC;
7073 SCROW nR;
7074 // Advance Entry.nField in iterator upon switching columns if
7075 // lookup in row.
7076 aCellIter.SetAdvanceQueryParamEntryField(!bVertical);
7077 if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) )
7078 {
7079 PushNA();
7080 return;
7081 }
7082
7083 SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1);
7084
7085 if (pResMat)
7086 {
7087 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
7088 // Use the matrix result array.
7089 if (aResMatAcc.IsValue(nDelta))
7090 PushDouble(aResMatAcc.GetDouble(nDelta));
7091 else
7092 PushString(aResMatAcc.GetString(nDelta));
7093 }
7094 else if (nParamCount == 3)
7095 {
7096 /* TODO: the entire switch is a copy of the array search result, factor
7097 * out. */
7098 switch (eResArrayType)
7099 {
7100 case svDoubleRef:
7101 case svSingleRef:
7102 {
7103 // Use the result array vector. Note that the result array is assumed
7104 // to be a vector (i.e. 1-dimensional array).
7105
7106 ScAddress aAdr;
7107 aAdr.SetTab(nResTab);
7108 bool bResVertical = (nResRow2 - nResRow1) > 0;
7109 if (bResVertical)
7110 {
7111 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
7112 if (nTempRow > mrDoc.MaxRow())
7113 {
7114 PushDouble(0);
7115 return;
7116 }
7117 aAdr.SetCol(nResCol1);
7118 aAdr.SetRow(nTempRow);
7119 }
7120 else
7121 {
7122 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
7123 if (nTempCol > mrDoc.MaxCol())
7124 {
7125 PushDouble(0);
7126 return;
7127 }
7128 aAdr.SetCol(nTempCol);
7129 aAdr.SetRow(nResRow1);
7130 }
7131 PushCellResultToken( true, aAdr, nullptr, nullptr);
7132 }
7133 break;
7134 case svDouble:
7135 case svString:
7136 {
7137 if (nDelta != 0)
7138 PushNA();
7139 else
7140 {
7141 switch (eResArrayType)
7142 {
7143 case svDouble:
7144 PushDouble( fResVal );
7145 break;
7146 case svString:
7147 PushString( aResStr );
7148 break;
7149 default:
7150 ; // nothing
7151 }
7152 }
7153 }
7154 break;
7155 default:
7156 assert(!"ScInterpreter::ScLookup: unhandled eResArrayType, range search");
7157 PushIllegalParameter();
7158 }
7159 }
7160 else
7161 {
7162 // Regardless of whether or not the result array exists, the last
7163 // array is always used as the "result" array.
7164
7165 ScAddress aAdr;
7166 aAdr.SetTab(nTab1);
7167 if (bVertical)
7168 {
7169 SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta);
7170 if (nTempRow > mrDoc.MaxRow())
7171 {
7172 PushDouble(0);
7173 return;
7174 }
7175 aAdr.SetCol(nCol2);
7176 aAdr.SetRow(nTempRow);
7177 }
7178 else
7179 {
7180 SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta);
7181 if (nTempCol > mrDoc.MaxCol())
7182 {
7183 PushDouble(0);
7184 return;
7185 }
7186 aAdr.SetCol(nTempCol);
7187 aAdr.SetRow(nRow2);
7188 }
7189 PushCellResultToken(true, aAdr, nullptr, nullptr);
7190 }
7191 }
7192
ScHLookup()7193 void ScInterpreter::ScHLookup()
7194 {
7195 CalculateLookup(true);
7196 }
7197
CalculateLookup(bool bHLookup)7198 void ScInterpreter::CalculateLookup(bool bHLookup)
7199 {
7200 sal_uInt8 nParamCount = GetByte();
7201 if (!MustHaveParamCount(nParamCount, 3, 4))
7202 return;
7203
7204 // Optional 4th argument to declare whether or not the range is sorted.
7205 bool bSorted = true;
7206 if (nParamCount == 4)
7207 bSorted = GetBool();
7208
7209 // Index of column to search.
7210 double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0;
7211
7212 ScMatrixRef pMat = nullptr;
7213 SCSIZE nC = 0, nR = 0;
7214 SCCOL nCol1 = 0;
7215 SCROW nRow1 = 0;
7216 SCTAB nTab1 = 0;
7217 SCCOL nCol2 = 0;
7218 SCROW nRow2 = 0;
7219 StackVar eType = GetStackType();
7220 if (eType == svDoubleRef)
7221 {
7222 SCTAB nTab2;
7223 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7224 if (nTab1 != nTab2)
7225 {
7226 PushIllegalParameter();
7227 return;
7228 }
7229 }
7230 else if (eType == svSingleRef)
7231 {
7232 PopSingleRef(nCol1, nRow1, nTab1);
7233 nCol2 = nCol1;
7234 nRow2 = nRow1;
7235 }
7236 else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef)
7237 {
7238 pMat = GetMatrix();
7239
7240 if (pMat)
7241 pMat->GetDimensions(nC, nR);
7242 else
7243 {
7244 PushIllegalParameter();
7245 return;
7246 }
7247 }
7248 else
7249 {
7250 PushIllegalParameter();
7251 return;
7252 }
7253
7254 if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) )
7255 {
7256 PushIllegalArgument();
7257 return;
7258 }
7259
7260 SCROW nZIndex = static_cast<SCROW>(fIndex);
7261 SCCOL nSpIndex = static_cast<SCCOL>(fIndex);
7262
7263 if (!pMat)
7264 {
7265 nZIndex += nRow1; // value row
7266 nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column
7267 }
7268
7269 if (nGlobalError != FormulaError::NONE)
7270 {
7271 PushIllegalParameter();
7272 return;
7273 }
7274
7275 ScQueryParam aParam;
7276 aParam.nCol1 = nCol1;
7277 aParam.nRow1 = nRow1;
7278 if ( bHLookup )
7279 {
7280 aParam.nCol2 = nCol2;
7281 aParam.nRow2 = nRow1; // search only in the first row
7282 aParam.bByRow = false;
7283 }
7284 else
7285 {
7286 aParam.nCol2 = nCol1; // search only in the first column
7287 aParam.nRow2 = nRow2;
7288 aParam.nTab = nTab1;
7289 }
7290
7291 ScQueryEntry& rEntry = aParam.GetEntry(0);
7292 rEntry.bDoQuery = true;
7293 if ( bSorted )
7294 rEntry.eOp = SC_LESS_EQUAL;
7295 if ( !FillEntry(rEntry) )
7296 return;
7297
7298 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7299 if (rItem.meType == ScQueryEntry::ByString)
7300 aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc);
7301 if (pMat)
7302 {
7303 SCSIZE nMatCount = bHLookup ? nC : nR;
7304 SCSIZE nDelta = SCSIZE_MAX;
7305 if (rItem.meType == ScQueryEntry::ByString)
7306 {
7307 //!!!!!!!
7308 //TODO: enable regex on matrix strings
7309 //!!!!!!!
7310 svl::SharedString aParamStr = rItem.maString;
7311 if ( bSorted )
7312 {
7313 CollatorWrapper* pCollator = ScGlobal::GetCollator();
7314 for (SCSIZE i = 0; i < nMatCount; i++)
7315 {
7316 if (bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i))
7317 {
7318 sal_Int32 nRes =
7319 pCollator->compareString(
7320 bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString());
7321 if (nRes <= 0)
7322 nDelta = i;
7323 else if (i>0) // #i2168# ignore first mismatch
7324 i = nMatCount+1;
7325 }
7326 else
7327 nDelta = i;
7328 }
7329 }
7330 else
7331 {
7332 if (bHLookup)
7333 {
7334 for (SCSIZE i = 0; i < nMatCount; i++)
7335 {
7336 if (pMat->IsStringOrEmpty(i, 0))
7337 {
7338 if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase())
7339 {
7340 nDelta = i;
7341 i = nMatCount + 1;
7342 }
7343 }
7344 }
7345 }
7346 else
7347 {
7348 nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0);
7349 }
7350 }
7351 }
7352 else
7353 {
7354 if ( bSorted )
7355 {
7356 // #i2168# ignore strings
7357 for (SCSIZE i = 0; i < nMatCount; i++)
7358 {
7359 if (!(bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i)))
7360 {
7361 if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal)
7362 nDelta = i;
7363 else
7364 i = nMatCount+1;
7365 }
7366 }
7367 }
7368 else
7369 {
7370 if (bHLookup)
7371 {
7372 for (SCSIZE i = 0; i < nMatCount; i++)
7373 {
7374 if (! pMat->IsStringOrEmpty(i, 0) )
7375 {
7376 if ( pMat->GetDouble(i,0) == rItem.mfVal)
7377 {
7378 nDelta = i;
7379 i = nMatCount + 1;
7380 }
7381 }
7382 }
7383 }
7384 else
7385 {
7386 nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0);
7387 }
7388 }
7389 }
7390 if ( nDelta != SCSIZE_MAX )
7391 {
7392 SCSIZE nX = static_cast<SCSIZE>(nSpIndex);
7393 SCSIZE nY = nDelta;
7394 if ( bHLookup )
7395 {
7396 nX = nDelta;
7397 nY = static_cast<SCSIZE>(nZIndex);
7398 }
7399 assert( nX < nC && nY < nR );
7400 if ( pMat->IsStringOrEmpty( nX, nY) )
7401 PushString(pMat->GetString( nX,nY).getString());
7402 else
7403 PushDouble(pMat->GetDouble( nX,nY));
7404 }
7405 else
7406 PushNA();
7407 }
7408 else
7409 {
7410 rEntry.nField = nCol1;
7411 bool bFound = false;
7412 SCCOL nCol = 0;
7413 SCROW nRow = 0;
7414 if ( bSorted )
7415 rEntry.eOp = SC_LESS_EQUAL;
7416 if ( bHLookup )
7417 {
7418 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, aParam, false);
7419 // advance Entry.nField in Iterator upon switching columns
7420 aCellIter.SetAdvanceQueryParamEntryField( true );
7421 if ( bSorted )
7422 {
7423 SCROW nRow1_temp;
7424 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp );
7425 }
7426 else if ( aCellIter.GetFirst() )
7427 {
7428 bFound = true;
7429 nCol = aCellIter.GetCol();
7430 }
7431 nRow = nZIndex;
7432 }
7433 else
7434 {
7435 ScAddress aResultPos( nCol1, nRow1, nTab1);
7436 bFound = LookupQueryWithCache( aResultPos, aParam);
7437 nRow = aResultPos.Row();
7438 nCol = nSpIndex;
7439 }
7440
7441 if ( bFound )
7442 {
7443 ScAddress aAdr( nCol, nRow, nTab1 );
7444 PushCellResultToken( true, aAdr, nullptr, nullptr);
7445 }
7446 else
7447 PushNA();
7448 }
7449 }
7450
FillEntry(ScQueryEntry & rEntry)7451 bool ScInterpreter::FillEntry(ScQueryEntry& rEntry)
7452 {
7453 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7454 switch ( GetStackType() )
7455 {
7456 case svDouble:
7457 {
7458 rItem.meType = ScQueryEntry::ByValue;
7459 rItem.mfVal = GetDouble();
7460 }
7461 break;
7462 case svString:
7463 {
7464 rItem.meType = ScQueryEntry::ByString;
7465 rItem.maString = GetString();
7466 }
7467 break;
7468 case svDoubleRef :
7469 case svSingleRef :
7470 {
7471 ScAddress aAdr;
7472 if ( !PopDoubleRefOrSingleRef( aAdr ) )
7473 {
7474 PushInt(0);
7475 return false;
7476 }
7477 ScRefCellValue aCell(mrDoc, aAdr);
7478 if (aCell.hasNumeric())
7479 {
7480 rItem.meType = ScQueryEntry::ByValue;
7481 rItem.mfVal = GetCellValue(aAdr, aCell);
7482 }
7483 else
7484 {
7485 GetCellString(rItem.maString, aCell);
7486 rItem.meType = ScQueryEntry::ByString;
7487 }
7488 }
7489 break;
7490 case svExternalDoubleRef:
7491 case svExternalSingleRef:
7492 case svMatrix:
7493 {
7494 svl::SharedString aStr;
7495 const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr);
7496 rItem.maString = aStr;
7497 rItem.meType = ScMatrix::IsNonValueType(nType) ?
7498 ScQueryEntry::ByString : ScQueryEntry::ByValue;
7499 }
7500 break;
7501 default:
7502 {
7503 PushIllegalParameter();
7504 return false;
7505 }
7506 } // switch ( GetStackType() )
7507 return true;
7508 }
7509
ScVLookup()7510 void ScInterpreter::ScVLookup()
7511 {
7512 CalculateLookup(false);
7513 }
7514
ScSubTotal()7515 void ScInterpreter::ScSubTotal()
7516 {
7517 sal_uInt8 nParamCount = GetByte();
7518 if ( !MustHaveParamCountMin( nParamCount, 2 ) )
7519 return;
7520
7521 // We must fish the 1st parameter deep from the stack! And push it on top.
7522 const FormulaToken* p = pStack[ sp - nParamCount ];
7523 PushWithoutError( *p );
7524 sal_Int32 nFunc = GetInt32();
7525 mnSubTotalFlags |= SubtotalFlags::IgnoreNestedStAg | SubtotalFlags::IgnoreFiltered;
7526 if (nFunc > 100)
7527 {
7528 // For opcodes 101 through 111, we need to skip hidden cells.
7529 // Other than that these opcodes are identical to 1 through 11.
7530 mnSubTotalFlags |= SubtotalFlags::IgnoreHidden;
7531 nFunc -= 100;
7532 }
7533
7534 if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 11 )
7535 PushIllegalArgument(); // simulate return on stack, not SetError(...)
7536 else
7537 {
7538 cPar = nParamCount - 1;
7539 switch( nFunc )
7540 {
7541 case SUBTOTAL_FUNC_AVE : ScAverage(); break;
7542 case SUBTOTAL_FUNC_CNT : ScCount(); break;
7543 case SUBTOTAL_FUNC_CNT2 : ScCount2(); break;
7544 case SUBTOTAL_FUNC_MAX : ScMax(); break;
7545 case SUBTOTAL_FUNC_MIN : ScMin(); break;
7546 case SUBTOTAL_FUNC_PROD : ScProduct(); break;
7547 case SUBTOTAL_FUNC_STD : ScStDev(); break;
7548 case SUBTOTAL_FUNC_STDP : ScStDevP(); break;
7549 case SUBTOTAL_FUNC_SUM : ScSum(); break;
7550 case SUBTOTAL_FUNC_VAR : ScVar(); break;
7551 case SUBTOTAL_FUNC_VARP : ScVarP(); break;
7552 default : PushIllegalArgument(); break;
7553 }
7554 }
7555 mnSubTotalFlags = SubtotalFlags::NONE;
7556 // Get rid of the 1st (fished) parameter.
7557 FormulaConstTokenRef xRef( PopToken());
7558 Pop();
7559 PushTokenRef( xRef);
7560 }
7561
ScAggregate()7562 void ScInterpreter::ScAggregate()
7563 {
7564 sal_uInt8 nParamCount = GetByte();
7565 if ( !MustHaveParamCountMin( nParamCount, 3 ) )
7566 return;
7567
7568 const FormulaError nErr = nGlobalError;
7569 nGlobalError = FormulaError::NONE;
7570
7571 // fish the 1st parameter from the stack and push it on top.
7572 const FormulaToken* p = pStack[ sp - nParamCount ];
7573 PushWithoutError( *p );
7574 sal_Int32 nFunc = GetInt32();
7575 // fish the 2nd parameter from the stack and push it on top.
7576 const FormulaToken* p2 = pStack[ sp - ( nParamCount - 1 ) ];
7577 PushWithoutError( *p2 );
7578 sal_Int32 nOption = GetInt32();
7579
7580 if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 19 )
7581 {
7582 nGlobalError = nErr;
7583 PushIllegalArgument();
7584 }
7585 else
7586 {
7587 switch ( nOption)
7588 {
7589 case 0 : // ignore nested SUBTOTAL and AGGREGATE functions
7590 mnSubTotalFlags = SubtotalFlags::IgnoreNestedStAg;
7591 break;
7592 case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions
7593 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreNestedStAg;
7594 break;
7595 case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions
7596 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
7597 break;
7598 case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions
7599 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg;
7600 break;
7601 case 4 : // ignore nothing
7602 mnSubTotalFlags = SubtotalFlags::NONE;
7603 break;
7604 case 5 : // ignore hidden rows
7605 mnSubTotalFlags = SubtotalFlags::IgnoreHidden ;
7606 break;
7607 case 6 : // ignore error values
7608 mnSubTotalFlags = SubtotalFlags::IgnoreErrVal ;
7609 break;
7610 case 7 : // ignore hidden rows and error values
7611 mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal ;
7612 break;
7613 default :
7614 nGlobalError = nErr;
7615 PushIllegalArgument();
7616 return;
7617 }
7618
7619 if ((mnSubTotalFlags & SubtotalFlags::IgnoreErrVal) == SubtotalFlags::NONE)
7620 nGlobalError = nErr;
7621
7622 cPar = nParamCount - 2;
7623 switch ( nFunc )
7624 {
7625 case AGGREGATE_FUNC_AVE : ScAverage(); break;
7626 case AGGREGATE_FUNC_CNT : ScCount(); break;
7627 case AGGREGATE_FUNC_CNT2 : ScCount2(); break;
7628 case AGGREGATE_FUNC_MAX : ScMax(); break;
7629 case AGGREGATE_FUNC_MIN : ScMin(); break;
7630 case AGGREGATE_FUNC_PROD : ScProduct(); break;
7631 case AGGREGATE_FUNC_STD : ScStDev(); break;
7632 case AGGREGATE_FUNC_STDP : ScStDevP(); break;
7633 case AGGREGATE_FUNC_SUM : ScSum(); break;
7634 case AGGREGATE_FUNC_VAR : ScVar(); break;
7635 case AGGREGATE_FUNC_VARP : ScVarP(); break;
7636 case AGGREGATE_FUNC_MEDIAN : ScMedian(); break;
7637 case AGGREGATE_FUNC_MODSNGL : ScModalValue(); break;
7638 case AGGREGATE_FUNC_LARGE : ScLarge(); break;
7639 case AGGREGATE_FUNC_SMALL : ScSmall(); break;
7640 case AGGREGATE_FUNC_PERCINC : ScPercentile( true ); break;
7641 case AGGREGATE_FUNC_QRTINC : ScQuartile( true ); break;
7642 case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break;
7643 case AGGREGATE_FUNC_QRTEXC : ScQuartile( false ); break;
7644 default:
7645 nGlobalError = nErr;
7646 PushIllegalArgument();
7647 break;
7648 }
7649 mnSubTotalFlags = SubtotalFlags::NONE;
7650 }
7651 FormulaConstTokenRef xRef( PopToken());
7652 // Get rid of the 1st and 2nd (fished) parameters.
7653 Pop();
7654 Pop();
7655 PushTokenRef( xRef);
7656 }
7657
GetDBParams(bool & rMissingField)7658 std::unique_ptr<ScDBQueryParamBase> ScInterpreter::GetDBParams( bool& rMissingField )
7659 {
7660 bool bAllowMissingField = false;
7661 if ( rMissingField )
7662 {
7663 bAllowMissingField = true;
7664 rMissingField = false;
7665 }
7666 if ( GetByte() == 3 )
7667 {
7668 // First, get the query criteria range.
7669 ::std::unique_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() );
7670 if (!pQueryRef)
7671 return nullptr;
7672
7673 bool bByVal = true;
7674 double nVal = 0.0;
7675 svl::SharedString aStr;
7676 ScRange aMissingRange;
7677 bool bRangeFake = false;
7678 switch (GetStackType())
7679 {
7680 case svDouble :
7681 nVal = ::rtl::math::approxFloor( GetDouble() );
7682 if ( bAllowMissingField && nVal == 0.0 )
7683 rMissingField = true; // fake missing parameter
7684 break;
7685 case svString :
7686 bByVal = false;
7687 aStr = GetString();
7688 break;
7689 case svSingleRef :
7690 {
7691 ScAddress aAdr;
7692 PopSingleRef( aAdr );
7693 ScRefCellValue aCell(mrDoc, aAdr);
7694 if (aCell.hasNumeric())
7695 nVal = GetCellValue(aAdr, aCell);
7696 else
7697 {
7698 bByVal = false;
7699 GetCellString(aStr, aCell);
7700 }
7701 }
7702 break;
7703 case svDoubleRef :
7704 if ( bAllowMissingField )
7705 { // fake missing parameter for old SO compatibility
7706 bRangeFake = true;
7707 PopDoubleRef( aMissingRange );
7708 }
7709 else
7710 {
7711 PopError();
7712 SetError( FormulaError::IllegalParameter );
7713 }
7714 break;
7715 case svMissing :
7716 PopError();
7717 if ( bAllowMissingField )
7718 rMissingField = true;
7719 else
7720 SetError( FormulaError::IllegalParameter );
7721 break;
7722 default:
7723 PopError();
7724 SetError( FormulaError::IllegalParameter );
7725 }
7726
7727 if (nGlobalError != FormulaError::NONE)
7728 return nullptr;
7729
7730 unique_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() );
7731
7732 if (nGlobalError != FormulaError::NONE || !pDBRef)
7733 return nullptr;
7734
7735 if ( bRangeFake )
7736 {
7737 // range parameter must match entire database range
7738 if (pDBRef->isRangeEqual(aMissingRange))
7739 rMissingField = true;
7740 else
7741 SetError( FormulaError::IllegalParameter );
7742 }
7743
7744 if (nGlobalError != FormulaError::NONE)
7745 return nullptr;
7746
7747 SCCOL nField = pDBRef->getFirstFieldColumn();
7748 if (rMissingField)
7749 ; // special case
7750 else if (bByVal)
7751 nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal));
7752 else
7753 {
7754 FormulaError nErr = FormulaError::NONE;
7755 nField = pDBRef->findFieldColumn(aStr.getString(), &nErr);
7756 SetError(nErr);
7757 }
7758
7759 if (!mrDoc.ValidCol(nField))
7760 return nullptr;
7761
7762 unique_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) );
7763
7764 if (pParam)
7765 {
7766 // An allowed missing field parameter sets the result field
7767 // to any of the query fields, just to be able to return
7768 // some cell from the iterator.
7769 if ( rMissingField )
7770 nField = static_cast<SCCOL>(pParam->GetEntry(0).nField);
7771 pParam->mnField = nField;
7772
7773 SCSIZE nCount = pParam->GetEntryCount();
7774 for ( SCSIZE i=0; i < nCount; i++ )
7775 {
7776 ScQueryEntry& rEntry = pParam->GetEntry(i);
7777 if (!rEntry.bDoQuery)
7778 break;
7779
7780 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
7781 sal_uInt32 nIndex = 0;
7782 OUString aQueryStr = rItem.maString.getString();
7783 bool bNumber = pFormatter->IsNumberFormat(
7784 aQueryStr, nIndex, rItem.mfVal);
7785 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
7786
7787 if (!bNumber && pParam->eSearchType == utl::SearchParam::SearchType::Normal)
7788 pParam->eSearchType = DetectSearchType(aQueryStr, mrDoc);
7789 }
7790 return pParam;
7791 }
7792 }
7793 return nullptr;
7794 }
7795
DBIterator(ScIterFunc eFunc)7796 void ScInterpreter::DBIterator( ScIterFunc eFunc )
7797 {
7798 double fRes = 0;
7799 KahanSum fErg = 0;
7800 sal_uLong nCount = 0;
7801 bool bMissingField = false;
7802 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
7803 if (pQueryParam)
7804 {
7805 if (!pQueryParam->IsValidFieldIndex())
7806 {
7807 SetError(FormulaError::NoValue);
7808 return;
7809 }
7810 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
7811 ScDBQueryDataIterator::Value aValue;
7812 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
7813 {
7814 switch( eFunc )
7815 {
7816 case ifPRODUCT: fRes = 1; break;
7817 case ifMAX: fRes = -MAXDOUBLE; break;
7818 case ifMIN: fRes = MAXDOUBLE; break;
7819 default: ; // nothing
7820 }
7821
7822 do
7823 {
7824 nCount++;
7825 switch( eFunc )
7826 {
7827 case ifAVERAGE:
7828 case ifSUM:
7829 fErg += aValue.mfValue;
7830 break;
7831 case ifSUMSQ:
7832 fErg += aValue.mfValue * aValue.mfValue;
7833 break;
7834 case ifPRODUCT:
7835 fRes *= aValue.mfValue;
7836 break;
7837 case ifMAX:
7838 if( aValue.mfValue > fRes ) fRes = aValue.mfValue;
7839 break;
7840 case ifMIN:
7841 if( aValue.mfValue < fRes ) fRes = aValue.mfValue;
7842 break;
7843 default: ; // nothing
7844 }
7845 }
7846 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
7847 }
7848 SetError(aValue.mnError);
7849 }
7850 else
7851 SetError( FormulaError::IllegalParameter);
7852 switch( eFunc )
7853 {
7854 case ifCOUNT: fRes = nCount; break;
7855 case ifSUM: fRes = fErg.get(); break;
7856 case ifSUMSQ: fRes = fErg.get(); break;
7857 case ifAVERAGE: fRes = div(fErg.get(), nCount); break;
7858 default: ; // nothing
7859 }
7860 PushDouble( fRes );
7861 }
7862
ScDBSum()7863 void ScInterpreter::ScDBSum()
7864 {
7865 DBIterator( ifSUM );
7866 }
7867
ScDBCount()7868 void ScInterpreter::ScDBCount()
7869 {
7870 bool bMissingField = true;
7871 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
7872 if (pQueryParam)
7873 {
7874 sal_uLong nCount = 0;
7875 if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL )
7876 { // count all matching records
7877 // TODO: currently the QueryIterators only return cell pointers of
7878 // existing cells, so if a query matches an empty cell there's
7879 // nothing returned, and therefore not counted!
7880 // Since this has ever been the case and this code here only came
7881 // into existence to fix #i6899 and it never worked before we'll
7882 // have to live with it until we reimplement the iterators to also
7883 // return empty cells, which would mean to adapt all callers of
7884 // iterators.
7885 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get());
7886 p->nCol2 = p->nCol1; // Don't forget to select only one column.
7887 SCTAB nTab = p->nTab;
7888 // ScQueryCellIterator doesn't make use of ScDBQueryParamBase::mnField,
7889 // so the source range has to be restricted, like before the introduction
7890 // of ScDBQueryParamBase.
7891 p->nCol1 = p->nCol2 = p->mnField;
7892 ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab, *p, true);
7893 if ( aCellIter.GetFirst() )
7894 {
7895 do
7896 {
7897 nCount++;
7898 } while ( aCellIter.GetNext() );
7899 }
7900 }
7901 else
7902 { // count only matching records with a value in the "result" field
7903 if (!pQueryParam->IsValidFieldIndex())
7904 {
7905 SetError(FormulaError::NoValue);
7906 return;
7907 }
7908 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
7909 ScDBQueryDataIterator::Value aValue;
7910 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
7911 {
7912 do
7913 {
7914 nCount++;
7915 }
7916 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
7917 }
7918 SetError(aValue.mnError);
7919 }
7920 PushDouble( nCount );
7921 }
7922 else
7923 PushIllegalParameter();
7924 }
7925
ScDBCount2()7926 void ScInterpreter::ScDBCount2()
7927 {
7928 bool bMissingField = true;
7929 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
7930 if (pQueryParam)
7931 {
7932 if (!pQueryParam->IsValidFieldIndex())
7933 {
7934 SetError(FormulaError::NoValue);
7935 return;
7936 }
7937 sal_uLong nCount = 0;
7938 pQueryParam->mbSkipString = false;
7939 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
7940 ScDBQueryDataIterator::Value aValue;
7941 if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE )
7942 {
7943 do
7944 {
7945 nCount++;
7946 }
7947 while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE );
7948 }
7949 SetError(aValue.mnError);
7950 PushDouble( nCount );
7951 }
7952 else
7953 PushIllegalParameter();
7954 }
7955
ScDBAverage()7956 void ScInterpreter::ScDBAverage()
7957 {
7958 DBIterator( ifAVERAGE );
7959 }
7960
ScDBMax()7961 void ScInterpreter::ScDBMax()
7962 {
7963 DBIterator( ifMAX );
7964 }
7965
ScDBMin()7966 void ScInterpreter::ScDBMin()
7967 {
7968 DBIterator( ifMIN );
7969 }
7970
ScDBProduct()7971 void ScInterpreter::ScDBProduct()
7972 {
7973 DBIterator( ifPRODUCT );
7974 }
7975
GetDBStVarParams(double & rVal,double & rValCount)7976 void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount )
7977 {
7978 std::vector<double> values;
7979 KahanSum vSum = 0.0;
7980 KahanSum fSum = 0.0;
7981
7982 rValCount = 0.0;
7983 bool bMissingField = false;
7984 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
7985 if (pQueryParam)
7986 {
7987 if (!pQueryParam->IsValidFieldIndex())
7988 {
7989 SetError(FormulaError::NoValue);
7990 return;
7991 }
7992 ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam));
7993 ScDBQueryDataIterator::Value aValue;
7994 if (aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE)
7995 {
7996 do
7997 {
7998 rValCount++;
7999 values.push_back(aValue.mfValue);
8000 fSum += aValue.mfValue;
8001 }
8002 while ((aValue.mnError == FormulaError::NONE) && aValIter.GetNext(aValue));
8003 }
8004 SetError(aValue.mnError);
8005 }
8006 else
8007 SetError( FormulaError::IllegalParameter);
8008
8009 double vMean = fSum.get() / values.size();
8010
8011 for (double v : values)
8012 vSum += (v - vMean) * (v - vMean);
8013
8014 rVal = vSum.get();
8015 }
8016
ScDBStdDev()8017 void ScInterpreter::ScDBStdDev()
8018 {
8019 double fVal, fCount;
8020 GetDBStVarParams( fVal, fCount );
8021 PushDouble( sqrt(fVal/(fCount-1)));
8022 }
8023
ScDBStdDevP()8024 void ScInterpreter::ScDBStdDevP()
8025 {
8026 double fVal, fCount;
8027 GetDBStVarParams( fVal, fCount );
8028 PushDouble( sqrt(fVal/fCount));
8029 }
8030
ScDBVar()8031 void ScInterpreter::ScDBVar()
8032 {
8033 double fVal, fCount;
8034 GetDBStVarParams( fVal, fCount );
8035 PushDouble(fVal/(fCount-1));
8036 }
8037
ScDBVarP()8038 void ScInterpreter::ScDBVarP()
8039 {
8040 double fVal, fCount;
8041 GetDBStVarParams( fVal, fCount );
8042 PushDouble(fVal/fCount);
8043 }
8044
ScIndirect()8045 void ScInterpreter::ScIndirect()
8046 {
8047 sal_uInt8 nParamCount = GetByte();
8048 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
8049 return;
8050
8051 // Reference address syntax for INDIRECT is configurable.
8052 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
8053 if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
8054 // Use the current address syntax if unspecified.
8055 eConv = mrDoc.GetAddressConvention();
8056
8057 // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible
8058 // to determine which syntax to use during doc import
8059 bool bTryXlA1 = (eConv == FormulaGrammar::CONV_A1_XL_A1);
8060
8061 if (nParamCount == 2 && 0.0 == GetDouble() )
8062 {
8063 // Overwrite the config and try Excel R1C1.
8064 eConv = FormulaGrammar::CONV_XL_R1C1;
8065 bTryXlA1 = false;
8066 }
8067
8068 OUString sRefStr = GetString().getString();
8069 if (sRefStr.isEmpty())
8070 {
8071 // Bail out early for empty cells, rely on "we do have a string" below.
8072 PushError( FormulaError::NoRef);
8073 return;
8074 }
8075
8076 const ScAddress::Details aDetails( bTryXlA1 ? FormulaGrammar::CONV_OOO : eConv, aPos );
8077 const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos );
8078 SCTAB nTab = aPos.Tab();
8079
8080 ScRefAddress aRefAd, aRefAd2;
8081 ScAddress::ExternalInfo aExtInfo;
8082 if ( ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) ||
8083 ( bTryXlA1 && ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd,
8084 aRefAd2, aDetailsXlA1, &aExtInfo) ) )
8085 {
8086 if (aExtInfo.mbExternal)
8087 {
8088 PushExternalDoubleRef(
8089 aExtInfo.mnFileId, aExtInfo.maTabName,
8090 aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
8091 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab());
8092 }
8093 else
8094 PushDoubleRef( aRefAd, aRefAd2);
8095 }
8096 else if ( ConvertSingleRef(mrDoc, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) ||
8097 ( bTryXlA1 && ConvertSingleRef (mrDoc, sRefStr, nTab, aRefAd,
8098 aDetailsXlA1, &aExtInfo) ) )
8099 {
8100 if (aExtInfo.mbExternal)
8101 {
8102 PushExternalSingleRef(
8103 aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab());
8104 }
8105 else
8106 PushSingleRef( aRefAd);
8107 }
8108 else
8109 {
8110 do
8111 {
8112 ScRangeData* pData = ScRangeStringConverter::GetRangeDataFromString( sRefStr, nTab, mrDoc, eConv);
8113 if (!pData)
8114 break;
8115
8116 // We need this in order to obtain a good range.
8117 pData->ValidateTabRefs();
8118
8119 ScRange aRange;
8120
8121 // This is the usual way to treat named ranges containing
8122 // relative references.
8123 if (!pData->IsReference( aRange, aPos))
8124 break;
8125
8126 if (aRange.aStart == aRange.aEnd)
8127 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8128 aRange.aStart.Tab());
8129 else
8130 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8131 aRange.aStart.Tab(), aRange.aEnd.Col(),
8132 aRange.aEnd.Row(), aRange.aEnd.Tab());
8133
8134 // success!
8135 return;
8136 }
8137 while (false);
8138
8139 do
8140 {
8141 OUString aName( ScGlobal::getCharClassPtr()->uppercase( sRefStr));
8142 ScDBCollection::NamedDBs& rDBs = mrDoc.GetDBCollection()->getNamedDBs();
8143 const ScDBData* pData = rDBs.findByUpperName( aName);
8144 if (!pData)
8145 break;
8146
8147 ScRange aRange;
8148 pData->GetArea( aRange);
8149
8150 // In Excel, specifying a table name without [] resolves to the
8151 // same as with [], a range that excludes header and totals
8152 // rows and contains only data rows. Do the same.
8153 if (pData->HasHeader())
8154 aRange.aStart.IncRow();
8155 if (pData->HasTotals())
8156 aRange.aEnd.IncRow(-1);
8157
8158 if (aRange.aStart.Row() > aRange.aEnd.Row())
8159 break;
8160
8161 if (aRange.aStart == aRange.aEnd)
8162 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8163 aRange.aStart.Tab());
8164 else
8165 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
8166 aRange.aStart.Tab(), aRange.aEnd.Col(),
8167 aRange.aEnd.Row(), aRange.aEnd.Tab());
8168
8169 // success!
8170 return;
8171 }
8172 while (false);
8173
8174 // It may be even a TableRef or an external name.
8175 // Anything else that resolves to one reference could be added
8176 // here, but we don't want to compile every arbitrary string. This
8177 // is already nasty enough...
8178 sal_Int32 nIndex = ScGlobal::FindUnquoted( sRefStr, '[');
8179 const bool bTableRef = (nIndex > 0 && ScGlobal::FindUnquoted( sRefStr, ']', nIndex+1) > nIndex);
8180 bool bExternalName = false; // External references would had been consumed above already.
8181 if (!bTableRef)
8182 {
8183 // This is our own file name reference representation centric.. but
8184 // would work also for XL '[doc]'!name and also for
8185 // '[doc]Sheet1'!name ... sickos.
8186 if (sRefStr[0] == '\'')
8187 {
8188 // Minimum 'a'#name or 'a'!name
8189 // bTryXlA1 means try both, first our own.
8190 if (bTryXlA1 || eConv == FormulaGrammar::CONV_OOO)
8191 {
8192 nIndex = ScGlobal::FindUnquoted( sRefStr, '#');
8193 if (nIndex >= 3 && sRefStr[nIndex-1] == '\'')
8194 {
8195 bExternalName = true;
8196 eConv = FormulaGrammar::CONV_OOO;
8197 }
8198 }
8199 if (!bExternalName && (bTryXlA1 || eConv != FormulaGrammar::CONV_OOO))
8200 {
8201 nIndex = ScGlobal::FindUnquoted( sRefStr, '!');
8202 if (nIndex >= 3 && sRefStr[nIndex-1] == '\'')
8203 {
8204 bExternalName = true;
8205 }
8206 }
8207 }
8208
8209 }
8210 if (bExternalName || bTableRef)
8211 {
8212 do
8213 {
8214 ScCompiler aComp( mrDoc, aPos, mrDoc.GetGrammar());
8215 aComp.SetRefConvention( eConv); // must be after grammar
8216 std::unique_ptr<ScTokenArray> pTokArr( aComp.CompileString( sRefStr));
8217
8218 if (pTokArr->GetCodeError() != FormulaError::NONE || !pTokArr->GetLen())
8219 break;
8220
8221 // Whatever... use only the specific case.
8222 if (bExternalName)
8223 {
8224 const formula::FormulaToken* pTok = pTokArr->FirstToken();
8225 if (!pTok || pTok->GetType() != svExternalName)
8226 break;
8227 }
8228 else if (!pTokArr->HasOpCode( ocTableRef))
8229 break;
8230
8231 aComp.CompileTokenArray();
8232
8233 // A syntactically valid reference will generate exactly
8234 // one RPN token, a reference or error. Discard everything
8235 // else as error.
8236 if (pTokArr->GetCodeLen() != 1)
8237 break;
8238
8239 ScTokenRef xTok( pTokArr->FirstRPNToken());
8240 if (!xTok)
8241 break;
8242
8243 switch (xTok->GetType())
8244 {
8245 case svSingleRef:
8246 case svDoubleRef:
8247 case svExternalSingleRef:
8248 case svExternalDoubleRef:
8249 case svError:
8250 PushTokenRef( xTok);
8251 // success!
8252 return;
8253 default:
8254 ; // nothing
8255 }
8256 }
8257 while (false);
8258 }
8259
8260 PushError( FormulaError::NoRef);
8261 }
8262 }
8263
ScAddressFunc()8264 void ScInterpreter::ScAddressFunc()
8265 {
8266 OUString sTabStr;
8267
8268 sal_uInt8 nParamCount = GetByte();
8269 if( !MustHaveParamCount( nParamCount, 2, 5 ) )
8270 return;
8271
8272 if( nParamCount >= 5 )
8273 sTabStr = GetString().getString();
8274
8275 FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default
8276 if (nParamCount >= 4 && 0.0 == GetDoubleWithDefault( 1.0))
8277 eConv = FormulaGrammar::CONV_XL_R1C1;
8278 else
8279 {
8280 // If A1 syntax is requested then the actual sheet separator and format
8281 // convention depends on the syntax configured for INDIRECT to match
8282 // that, and if it is unspecified then the document's address syntax.
8283 FormulaGrammar::AddressConvention eForceConv = maCalcConfig.meStringRefAddressSyntax;
8284 if (eForceConv == FormulaGrammar::CONV_UNSPECIFIED)
8285 eForceConv = mrDoc.GetAddressConvention();
8286 if (eForceConv == FormulaGrammar::CONV_XL_A1 || eForceConv == FormulaGrammar::CONV_XL_R1C1)
8287 eConv = FormulaGrammar::CONV_XL_A1; // for anything Excel use Excel A1
8288 }
8289
8290 ScRefFlags nFlags = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; // default
8291 if( nParamCount >= 3 )
8292 {
8293 sal_Int32 n = GetInt32WithDefault(1);
8294 switch ( n )
8295 {
8296 default :
8297 PushNoValue();
8298 return;
8299
8300 case 5:
8301 case 1 : break; // default
8302 case 6:
8303 case 2 : nFlags = ScRefFlags::ROW_ABS; break;
8304 case 7:
8305 case 3 : nFlags = ScRefFlags::COL_ABS; break;
8306 case 8:
8307 case 4 : nFlags = ScRefFlags::ZERO; break; // both relative
8308 }
8309 }
8310 nFlags |= ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID;
8311
8312 SCCOL nCol = static_cast<SCCOL>(GetInt16());
8313 SCROW nRow = static_cast<SCROW>(GetInt32());
8314 if( eConv == FormulaGrammar::CONV_XL_R1C1 )
8315 {
8316 // YUCK! The XL interface actually treats rel R1C1 refs differently
8317 // than A1
8318 if( !(nFlags & ScRefFlags::COL_ABS) )
8319 nCol += aPos.Col() + 1;
8320 if( !(nFlags & ScRefFlags::ROW_ABS) )
8321 nRow += aPos.Row() + 1;
8322 }
8323
8324 --nCol;
8325 --nRow;
8326 if (nGlobalError != FormulaError::NONE || !mrDoc.ValidCol( nCol) || !mrDoc.ValidRow( nRow))
8327 {
8328 PushIllegalArgument();
8329 return;
8330 }
8331
8332 const ScAddress::Details aDetails( eConv, aPos );
8333 const ScAddress aAdr( nCol, nRow, 0);
8334 OUString aRefStr(aAdr.Format(nFlags, &mrDoc, aDetails));
8335
8336 if( nParamCount >= 5 && !sTabStr.isEmpty() )
8337 {
8338 OUString aDoc;
8339 if (eConv == FormulaGrammar::CONV_OOO)
8340 {
8341 // Isolate Tab from 'Doc'#Tab
8342 sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr);
8343 if (nPos != -1)
8344 {
8345 if (sTabStr[nPos+1] == '$')
8346 ++nPos; // also split 'Doc'#$Tab
8347 aDoc = sTabStr.copy( 0, nPos+1);
8348 sTabStr = sTabStr.copy( nPos+1);
8349 }
8350 }
8351 /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
8352 * need some extra handling to isolate Tab from Doc. */
8353 if (sTabStr[0] != '\'' || !sTabStr.endsWith("'"))
8354 ScCompiler::CheckTabQuotes( sTabStr, eConv);
8355 if (!aDoc.isEmpty())
8356 sTabStr = aDoc + sTabStr;
8357 sTabStr += (eConv == FormulaGrammar::CONV_XL_R1C1 || eConv == FormulaGrammar::CONV_XL_A1) ?
8358 OUStringLiteral(u"!") : OUStringLiteral(u".");
8359 sTabStr += aRefStr;
8360 PushString( sTabStr );
8361 }
8362 else
8363 PushString( aRefStr );
8364 }
8365
ScOffset()8366 void ScInterpreter::ScOffset()
8367 {
8368 sal_uInt8 nParamCount = GetByte();
8369 if ( !MustHaveParamCount( nParamCount, 3, 5 ) )
8370 return;
8371
8372 bool bNewWidth = false;
8373 bool bNewHeight = false;
8374 sal_Int32 nColNew = 1, nRowNew = 1;
8375 if (nParamCount == 5)
8376 {
8377 if (IsMissing())
8378 PopError();
8379 else
8380 {
8381 nColNew = GetInt32();
8382 bNewWidth = true;
8383 }
8384 }
8385 if (nParamCount >= 4)
8386 {
8387 if (IsMissing())
8388 PopError();
8389 else
8390 {
8391 nRowNew = GetInt32();
8392 bNewHeight = true;
8393 }
8394 }
8395 sal_Int32 nColPlus = GetInt32();
8396 sal_Int32 nRowPlus = GetInt32();
8397 if (nGlobalError != FormulaError::NONE)
8398 {
8399 PushError( nGlobalError);
8400 return;
8401 }
8402 if (nColNew <= 0 || nRowNew <= 0)
8403 {
8404 PushIllegalArgument();
8405 return;
8406 }
8407 SCCOL nCol1(0);
8408 SCROW nRow1(0);
8409 SCTAB nTab1(0);
8410 SCCOL nCol2(0);
8411 SCROW nRow2(0);
8412 SCTAB nTab2(0);
8413 switch (GetStackType())
8414 {
8415 case svSingleRef:
8416 {
8417 PopSingleRef(nCol1, nRow1, nTab1);
8418 if (!bNewWidth && !bNewHeight)
8419 {
8420 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus);
8421 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus);
8422 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
8423 PushIllegalArgument();
8424 else
8425 PushSingleRef(nCol1, nRow1, nTab1);
8426 }
8427 else
8428 {
8429 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
8430 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
8431 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
8432 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
8433 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8434 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
8435 PushIllegalArgument();
8436 else
8437 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
8438 }
8439 break;
8440 }
8441 case svExternalSingleRef:
8442 {
8443 sal_uInt16 nFileId;
8444 OUString aTabName;
8445 ScSingleRefData aRef;
8446 PopExternalSingleRef(nFileId, aTabName, aRef);
8447 ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos);
8448 nCol1 = aAbsRef.Col();
8449 nRow1 = aAbsRef.Row();
8450 nTab1 = aAbsRef.Tab();
8451
8452 if (!bNewWidth && !bNewHeight)
8453 {
8454 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1) + nColPlus);
8455 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1) + nRowPlus);
8456 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1))
8457 PushIllegalArgument();
8458 else
8459 PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1);
8460 }
8461 else
8462 {
8463 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
8464 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
8465 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
8466 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
8467 nTab2 = nTab1;
8468 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8469 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2))
8470 PushIllegalArgument();
8471 else
8472 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8473 }
8474 break;
8475 }
8476 case svDoubleRef:
8477 {
8478 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8479 if (!bNewWidth)
8480 nColNew = nCol2 - nCol1 + 1;
8481 if (!bNewHeight)
8482 nRowNew = nRow2 - nRow1 + 1;
8483 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
8484 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
8485 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
8486 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
8487 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8488 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
8489 PushIllegalArgument();
8490 else
8491 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
8492 break;
8493 }
8494 case svExternalDoubleRef:
8495 {
8496 sal_uInt16 nFileId;
8497 OUString aTabName;
8498 ScComplexRefData aRef;
8499 PopExternalDoubleRef(nFileId, aTabName, aRef);
8500 ScRange aAbs = aRef.toAbs(mrDoc, aPos);
8501 nCol1 = aAbs.aStart.Col();
8502 nRow1 = aAbs.aStart.Row();
8503 nTab1 = aAbs.aStart.Tab();
8504 nCol2 = aAbs.aEnd.Col();
8505 nRow2 = aAbs.aEnd.Row();
8506 nTab2 = aAbs.aEnd.Tab();
8507 if (!bNewWidth)
8508 nColNew = nCol2 - nCol1 + 1;
8509 if (!bNewHeight)
8510 nRowNew = nRow2 - nRow1 + 1;
8511 nCol1 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColPlus);
8512 nRow1 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowPlus);
8513 nCol2 = static_cast<SCCOL>(static_cast<tools::Long>(nCol1)+nColNew-1);
8514 nRow2 = static_cast<SCROW>(static_cast<tools::Long>(nRow1)+nRowNew-1);
8515 if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) ||
8516 !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2)
8517 PushIllegalArgument();
8518 else
8519 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8520 break;
8521 }
8522 default:
8523 PushIllegalParameter();
8524 break;
8525 } // end switch
8526 }
8527
ScIndex()8528 void ScInterpreter::ScIndex()
8529 {
8530 sal_uInt8 nParamCount = GetByte();
8531 if ( !MustHaveParamCount( nParamCount, 1, 4 ) )
8532 return;
8533
8534 sal_uInt32 nArea;
8535 size_t nAreaCount;
8536 SCCOL nCol;
8537 SCROW nRow;
8538 if (nParamCount == 4)
8539 nArea = GetUInt32();
8540 else
8541 nArea = 1;
8542 if (nParamCount >= 3)
8543 nCol = static_cast<SCCOL>(GetInt16());
8544 else
8545 nCol = 0;
8546 if (nParamCount >= 2)
8547 nRow = static_cast<SCROW>(GetInt32());
8548 else
8549 nRow = 0;
8550 if (GetStackType() == svRefList)
8551 nAreaCount = (sp ? pStack[sp-1]->GetRefList()->size() : 0);
8552 else
8553 nAreaCount = 1; // one reference or array or whatever
8554 if (nGlobalError != FormulaError::NONE || nAreaCount == 0 || static_cast<size_t>(nArea) > nAreaCount)
8555 {
8556 PushError( FormulaError::NoRef);
8557 return;
8558 }
8559 else if (nArea < 1 || nCol < 0 || nRow < 0)
8560 {
8561 PushIllegalArgument();
8562 return;
8563 }
8564 switch (GetStackType())
8565 {
8566 case svMatrix:
8567 case svExternalSingleRef:
8568 case svExternalDoubleRef:
8569 {
8570 if (nArea != 1)
8571 SetError(FormulaError::IllegalArgument);
8572 sal_uInt16 nOldSp = sp;
8573 ScMatrixRef pMat = GetMatrix();
8574 if (pMat)
8575 {
8576 SCSIZE nC, nR;
8577 pMat->GetDimensions(nC, nR);
8578 // Access one element of a vector independent of col/row
8579 // orientation?
8580 bool bVector = ((nCol == 0 || nRow == 0) && (nC == 1 || nR == 1));
8581 SCSIZE nElement = ::std::max( static_cast<SCSIZE>(nCol),
8582 static_cast<SCSIZE>(nRow));
8583 if (nC == 0 || nR == 0 ||
8584 (!bVector && (o3tl::make_unsigned(nCol) > nC ||
8585 o3tl::make_unsigned(nRow) > nR)) ||
8586 (bVector && nElement > nC * nR))
8587 PushIllegalArgument();
8588 else if (nCol == 0 && nRow == 0)
8589 sp = nOldSp;
8590 else if (bVector)
8591 {
8592 --nElement;
8593 if (pMat->IsStringOrEmpty( nElement))
8594 PushString( pMat->GetString(nElement).getString());
8595 else
8596 PushDouble( pMat->GetDouble( nElement));
8597 }
8598 else if (nCol == 0)
8599 {
8600 ScMatrixRef pResMat = GetNewMat(nC, 1, /*bEmpty*/true);
8601 if (pResMat)
8602 {
8603 SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1);
8604 for (SCSIZE i = 0; i < nC; i++)
8605 if (!pMat->IsStringOrEmpty(i, nRowMinus1))
8606 pResMat->PutDouble(pMat->GetDouble(i,
8607 nRowMinus1), i, 0);
8608 else
8609 pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0);
8610
8611 PushMatrix(pResMat);
8612 }
8613 else
8614 PushIllegalArgument();
8615 }
8616 else if (nRow == 0)
8617 {
8618 ScMatrixRef pResMat = GetNewMat(1, nR, /*bEmpty*/true);
8619 if (pResMat)
8620 {
8621 SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1);
8622 for (SCSIZE i = 0; i < nR; i++)
8623 if (!pMat->IsStringOrEmpty(nColMinus1, i))
8624 pResMat->PutDouble(pMat->GetDouble(nColMinus1,
8625 i), i);
8626 else
8627 pResMat->PutString(pMat->GetString(nColMinus1, i), i);
8628 PushMatrix(pResMat);
8629 }
8630 else
8631 PushIllegalArgument();
8632 }
8633 else
8634 {
8635 if (!pMat->IsStringOrEmpty( static_cast<SCSIZE>(nCol-1),
8636 static_cast<SCSIZE>(nRow-1)))
8637 PushDouble( pMat->GetDouble(
8638 static_cast<SCSIZE>(nCol-1),
8639 static_cast<SCSIZE>(nRow-1)));
8640 else
8641 PushString( pMat->GetString(
8642 static_cast<SCSIZE>(nCol-1),
8643 static_cast<SCSIZE>(nRow-1)).getString());
8644 }
8645 }
8646 }
8647 break;
8648 case svSingleRef:
8649 {
8650 SCCOL nCol1 = 0;
8651 SCROW nRow1 = 0;
8652 SCTAB nTab1 = 0;
8653 PopSingleRef( nCol1, nRow1, nTab1);
8654 if (nCol > 1 || nRow > 1)
8655 PushIllegalArgument();
8656 else
8657 PushSingleRef( nCol1, nRow1, nTab1);
8658 }
8659 break;
8660 case svDoubleRef:
8661 case svRefList:
8662 {
8663 SCCOL nCol1 = 0;
8664 SCROW nRow1 = 0;
8665 SCTAB nTab1 = 0;
8666 SCCOL nCol2 = 0;
8667 SCROW nRow2 = 0;
8668 SCTAB nTab2 = 0;
8669 bool bRowArray = false;
8670 if (GetStackType() == svRefList)
8671 {
8672 FormulaConstTokenRef xRef = PopToken();
8673 if (nGlobalError != FormulaError::NONE || !xRef)
8674 {
8675 PushIllegalParameter();
8676 return;
8677 }
8678 ScRange aRange( ScAddress::UNINITIALIZED);
8679 DoubleRefToRange( (*(xRef->GetRefList()))[nArea-1], aRange);
8680 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8681 if ( nParamCount == 2 && nRow1 == nRow2 )
8682 bRowArray = true;
8683 }
8684 else
8685 {
8686 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
8687 if ( nParamCount == 2 && nRow1 == nRow2 )
8688 bRowArray = true;
8689 }
8690 if ( nTab1 != nTab2 ||
8691 (nCol > 0 && nCol1+nCol-1 > nCol2) ||
8692 (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) ||
8693 ( nRow > nCol2 - nCol1 + 1 && bRowArray ))
8694 PushIllegalArgument();
8695 else if (nCol == 0 && nRow == 0)
8696 {
8697 if ( nCol1 == nCol2 && nRow1 == nRow2 )
8698 PushSingleRef( nCol1, nRow1, nTab1 );
8699 else
8700 PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 );
8701 }
8702 else if (nRow == 0)
8703 {
8704 if ( nRow1 == nRow2 )
8705 PushSingleRef( nCol1+nCol-1, nRow1, nTab1 );
8706 else
8707 PushDoubleRef( nCol1+nCol-1, nRow1, nTab1,
8708 nCol1+nCol-1, nRow2, nTab1 );
8709 }
8710 else if (nCol == 0)
8711 {
8712 if ( nCol1 == nCol2 )
8713 PushSingleRef( nCol1, nRow1+nRow-1, nTab1 );
8714 else if ( bRowArray )
8715 {
8716 nCol =static_cast<SCCOL>(nRow);
8717 nRow = 1;
8718 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
8719 }
8720 else
8721 PushDoubleRef( nCol1, nRow1+nRow-1, nTab1,
8722 nCol2, nRow1+nRow-1, nTab1);
8723 }
8724 else
8725 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
8726 }
8727 break;
8728 default:
8729 PushIllegalParameter();
8730 }
8731 }
8732
ScMultiArea()8733 void ScInterpreter::ScMultiArea()
8734 {
8735 // Legacy support, convert to RefList
8736 sal_uInt8 nParamCount = GetByte();
8737 if (MustHaveParamCountMin( nParamCount, 1))
8738 {
8739 while (nGlobalError == FormulaError::NONE && nParamCount-- > 1)
8740 {
8741 ScUnionFunc();
8742 }
8743 }
8744 }
8745
ScAreas()8746 void ScInterpreter::ScAreas()
8747 {
8748 sal_uInt8 nParamCount = GetByte();
8749 if (!MustHaveParamCount( nParamCount, 1))
8750 return;
8751
8752 size_t nCount = 0;
8753 switch (GetStackType())
8754 {
8755 case svSingleRef:
8756 {
8757 FormulaConstTokenRef xT = PopToken();
8758 ValidateRef( *xT->GetSingleRef());
8759 ++nCount;
8760 }
8761 break;
8762 case svDoubleRef:
8763 {
8764 FormulaConstTokenRef xT = PopToken();
8765 ValidateRef( *xT->GetDoubleRef());
8766 ++nCount;
8767 }
8768 break;
8769 case svRefList:
8770 {
8771 FormulaConstTokenRef xT = PopToken();
8772 ValidateRef( *(xT->GetRefList()));
8773 nCount += xT->GetRefList()->size();
8774 }
8775 break;
8776 default:
8777 SetError( FormulaError::IllegalParameter);
8778 }
8779 PushDouble( double(nCount));
8780 }
8781
ScCurrency()8782 void ScInterpreter::ScCurrency()
8783 {
8784 sal_uInt8 nParamCount = GetByte();
8785 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
8786 return;
8787
8788 OUString aStr;
8789 double fDec;
8790 if (nParamCount == 2)
8791 {
8792 fDec = ::rtl::math::approxFloor(GetDouble());
8793 if (fDec < -15.0 || fDec > 15.0)
8794 {
8795 PushIllegalArgument();
8796 return;
8797 }
8798 }
8799 else
8800 fDec = 2.0;
8801 double fVal = GetDouble();
8802 double fFac;
8803 if ( fDec != 0.0 )
8804 fFac = pow( double(10), fDec );
8805 else
8806 fFac = 1.0;
8807 if (fVal < 0.0)
8808 fVal = ceil(fVal*fFac-0.5)/fFac;
8809 else
8810 fVal = floor(fVal*fFac+0.5)/fFac;
8811 const Color* pColor = nullptr;
8812 if ( fDec < 0.0 )
8813 fDec = 0.0;
8814 sal_uLong nIndex = pFormatter->GetStandardFormat(
8815 SvNumFormatType::CURRENCY,
8816 ScGlobal::eLnge);
8817 if ( static_cast<sal_uInt16>(fDec) != pFormatter->GetFormatPrecision( nIndex ) )
8818 {
8819 OUString sFormatString = pFormatter->GenerateFormat(
8820 nIndex,
8821 ScGlobal::eLnge,
8822 true, // with thousands separator
8823 false, // not red
8824 static_cast<sal_uInt16>(fDec));// decimal places
8825 if (!pFormatter->GetPreviewString(sFormatString,
8826 fVal,
8827 aStr,
8828 &pColor,
8829 ScGlobal::eLnge))
8830 SetError(FormulaError::IllegalArgument);
8831 }
8832 else
8833 {
8834 pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor);
8835 }
8836 PushString(aStr);
8837 }
8838
ScReplace()8839 void ScInterpreter::ScReplace()
8840 {
8841 if ( !MustHaveParamCount( GetByte(), 4 ) )
8842 return;
8843
8844 OUString aNewStr = GetString().getString();
8845 sal_Int32 nCount = GetStringPositionArgument();
8846 sal_Int32 nPos = GetStringPositionArgument();
8847 OUString aOldStr = GetString().getString();
8848 if (nPos < 1 || nCount < 0)
8849 PushIllegalArgument();
8850 else
8851 {
8852 sal_Int32 nLen = aOldStr.getLength();
8853 if (nPos > nLen + 1)
8854 nPos = nLen + 1;
8855 if (nCount > nLen - nPos + 1)
8856 nCount = nLen - nPos + 1;
8857 sal_Int32 nIdx = 0;
8858 sal_Int32 nCnt = 0;
8859 while ( nIdx < nLen && nPos > nCnt + 1 )
8860 {
8861 aOldStr.iterateCodePoints( &nIdx );
8862 ++nCnt;
8863 }
8864 sal_Int32 nStart = nIdx;
8865 while ( nIdx < nLen && nPos + nCount - 1 > nCnt )
8866 {
8867 aOldStr.iterateCodePoints( &nIdx );
8868 ++nCnt;
8869 }
8870 aOldStr = aOldStr.replaceAt( nStart, nIdx - nStart, "" );
8871 if ( CheckStringResultLen( aOldStr, aNewStr ) )
8872 aOldStr = aOldStr.replaceAt( nStart, 0, aNewStr );
8873 PushString( aOldStr );
8874 }
8875 }
8876
ScFixed()8877 void ScInterpreter::ScFixed()
8878 {
8879 sal_uInt8 nParamCount = GetByte();
8880 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
8881 return;
8882
8883 OUString aStr;
8884 double fDec;
8885 bool bThousand;
8886 if (nParamCount == 3)
8887 bThousand = !GetBool(); // Param true: no thousands separator
8888 else
8889 bThousand = true;
8890 if (nParamCount >= 2)
8891 {
8892 fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
8893 if (fDec < -15.0 || fDec > 15.0)
8894 {
8895 PushIllegalArgument();
8896 return;
8897 }
8898 }
8899 else
8900 fDec = 2.0;
8901 double fVal = GetDouble();
8902 double fFac;
8903 if ( fDec != 0.0 )
8904 fFac = pow( double(10), fDec );
8905 else
8906 fFac = 1.0;
8907 if (fVal < 0.0)
8908 fVal = ceil(fVal*fFac-0.5)/fFac;
8909 else
8910 fVal = floor(fVal*fFac+0.5)/fFac;
8911 const Color* pColor = nullptr;
8912 if (fDec < 0.0)
8913 fDec = 0.0;
8914 sal_uLong nIndex = pFormatter->GetStandardFormat(
8915 SvNumFormatType::NUMBER,
8916 ScGlobal::eLnge);
8917 OUString sFormatString = pFormatter->GenerateFormat(
8918 nIndex,
8919 ScGlobal::eLnge,
8920 bThousand, // with thousands separator
8921 false, // not red
8922 static_cast<sal_uInt16>(fDec));// decimal places
8923 if (!pFormatter->GetPreviewString(sFormatString,
8924 fVal,
8925 aStr,
8926 &pColor,
8927 ScGlobal::eLnge))
8928 PushIllegalArgument();
8929 else
8930 PushString(aStr);
8931 }
8932
ScFind()8933 void ScInterpreter::ScFind()
8934 {
8935 sal_uInt8 nParamCount = GetByte();
8936 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
8937 return;
8938
8939 sal_Int32 nCnt;
8940 if (nParamCount == 3)
8941 nCnt = GetDouble();
8942 else
8943 nCnt = 1;
8944 OUString sStr = GetString().getString();
8945 if (nCnt < 1 || nCnt > sStr.getLength())
8946 PushNoValue();
8947 else
8948 {
8949 sal_Int32 nPos = sStr.indexOf(GetString().getString(), nCnt - 1);
8950 if (nPos == -1)
8951 PushNoValue();
8952 else
8953 {
8954 sal_Int32 nIdx = 0;
8955 nCnt = 0;
8956 while ( nIdx <= nPos )
8957 {
8958 sStr.iterateCodePoints( &nIdx );
8959 ++nCnt;
8960 }
8961 PushDouble( static_cast<double>(nCnt) );
8962 }
8963 }
8964 }
8965
ScExact()8966 void ScInterpreter::ScExact()
8967 {
8968 nFuncFmtType = SvNumFormatType::LOGICAL;
8969 if ( MustHaveParamCount( GetByte(), 2 ) )
8970 {
8971 svl::SharedString s1 = GetString();
8972 svl::SharedString s2 = GetString();
8973 PushInt( int(s1.getData() == s2.getData()) );
8974 }
8975 }
8976
ScLeft()8977 void ScInterpreter::ScLeft()
8978 {
8979 sal_uInt8 nParamCount = GetByte();
8980 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
8981 return;
8982
8983 sal_Int32 n;
8984 if (nParamCount == 2)
8985 {
8986 n = GetStringPositionArgument();
8987 if (n < 0)
8988 {
8989 PushIllegalArgument();
8990 return ;
8991 }
8992 }
8993 else
8994 n = 1;
8995 OUString aStr = GetString().getString();
8996 sal_Int32 nIdx = 0;
8997 sal_Int32 nCnt = 0;
8998 while ( nIdx < aStr.getLength() && n > nCnt++ )
8999 aStr.iterateCodePoints( &nIdx );
9000 aStr = aStr.copy( 0, nIdx );
9001 PushString( aStr );
9002 }
9003
9004 namespace {
9005
9006 struct UBlockScript {
9007 UBlockCode from;
9008 UBlockCode to;
9009 };
9010
9011 }
9012
9013 const UBlockScript scriptList[] = {
9014 {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO},
9015 {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES},
9016 {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT },
9017 {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS},
9018 {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS},
9019 {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS},
9020 {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT},
9021 {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES}
9022 };
IsDBCS(sal_Unicode currentChar)9023 static bool IsDBCS(sal_Unicode currentChar)
9024 {
9025 // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
9026 if( (currentChar == 0x005c || currentChar == 0x20ac) &&
9027 (MsLangId::getSystemLanguage() == LANGUAGE_JAPANESE) )
9028 return true;
9029 sal_uInt16 i;
9030 bool bRet = false;
9031 UBlockCode block = ublock_getCode(currentChar);
9032 for ( i = 0; i < SAL_N_ELEMENTS(scriptList); i++) {
9033 if (block <= scriptList[i].to) break;
9034 }
9035 bRet = (i < SAL_N_ELEMENTS(scriptList) && block >= scriptList[i].from);
9036 return bRet;
9037 }
lcl_getLengthB(std::u16string_view str,sal_Int32 nPos)9038 static sal_Int32 lcl_getLengthB( std::u16string_view str, sal_Int32 nPos )
9039 {
9040 sal_Int32 index = 0;
9041 sal_Int32 length = 0;
9042 while ( index < nPos )
9043 {
9044 if (IsDBCS(str[index]))
9045 length += 2;
9046 else
9047 length++;
9048 index++;
9049 }
9050 return length;
9051 }
getLengthB(const OUString & str)9052 static sal_Int32 getLengthB(const OUString &str)
9053 {
9054 if(str.isEmpty())
9055 return 0;
9056 else
9057 return lcl_getLengthB( str, str.getLength() );
9058 }
ScLenB()9059 void ScInterpreter::ScLenB()
9060 {
9061 PushDouble( getLengthB(GetString().getString()) );
9062 }
lcl_RightB(const OUString & rStr,sal_Int32 n)9063 static OUString lcl_RightB(const OUString &rStr, sal_Int32 n)
9064 {
9065 if( n < getLengthB(rStr) )
9066 {
9067 OUStringBuffer aBuf(rStr);
9068 sal_Int32 index = aBuf.getLength();
9069 while(index-- >= 0)
9070 {
9071 if(0 == n)
9072 {
9073 aBuf.remove( 0, index + 1);
9074 break;
9075 }
9076 if(-1 == n)
9077 {
9078 aBuf.remove( 0, index + 2 );
9079 aBuf.insert( 0, " ");
9080 break;
9081 }
9082 if(IsDBCS(aBuf[index]))
9083 n -= 2;
9084 else
9085 n--;
9086 }
9087 return aBuf.makeStringAndClear();
9088 }
9089 return rStr;
9090 }
ScRightB()9091 void ScInterpreter::ScRightB()
9092 {
9093 sal_uInt8 nParamCount = GetByte();
9094 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9095 return;
9096
9097 sal_Int32 n;
9098 if (nParamCount == 2)
9099 {
9100 n = GetStringPositionArgument();
9101 if (n < 0)
9102 {
9103 PushIllegalArgument();
9104 return ;
9105 }
9106 }
9107 else
9108 n = 1;
9109 OUString aStr(lcl_RightB(GetString().getString(), n));
9110 PushString( aStr );
9111 }
lcl_LeftB(const OUString & rStr,sal_Int32 n)9112 static OUString lcl_LeftB(const OUString &rStr, sal_Int32 n)
9113 {
9114 if( n < getLengthB(rStr) )
9115 {
9116 OUStringBuffer aBuf(rStr);
9117 sal_Int32 index = -1;
9118 while(index++ < aBuf.getLength())
9119 {
9120 if(0 == n)
9121 {
9122 aBuf.truncate(index);
9123 break;
9124 }
9125 if(-1 == n)
9126 {
9127 aBuf.truncate( index - 1 );
9128 aBuf.append(" ");
9129 break;
9130 }
9131 if(IsDBCS(aBuf[index]))
9132 n -= 2;
9133 else
9134 n--;
9135 }
9136 return aBuf.makeStringAndClear();
9137 }
9138 return rStr;
9139 }
ScLeftB()9140 void ScInterpreter::ScLeftB()
9141 {
9142 sal_uInt8 nParamCount = GetByte();
9143 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9144 return;
9145
9146 sal_Int32 n;
9147 if (nParamCount == 2)
9148 {
9149 n = GetStringPositionArgument();
9150 if (n < 0)
9151 {
9152 PushIllegalArgument();
9153 return ;
9154 }
9155 }
9156 else
9157 n = 1;
9158 OUString aStr(lcl_LeftB(GetString().getString(), n));
9159 PushString( aStr );
9160 }
ScMidB()9161 void ScInterpreter::ScMidB()
9162 {
9163 if ( !MustHaveParamCount( GetByte(), 3 ) )
9164 return;
9165
9166 const sal_Int32 nCount = GetStringPositionArgument();
9167 const sal_Int32 nStart = GetStringPositionArgument();
9168 OUString aStr = GetString().getString();
9169 if (nStart < 1 || nCount < 0)
9170 PushIllegalArgument();
9171 else
9172 {
9173
9174 aStr = lcl_LeftB(aStr, nStart + nCount - 1);
9175 sal_Int32 nCnt = getLengthB(aStr) - nStart + 1;
9176 aStr = lcl_RightB(aStr, std::max<sal_Int32>(nCnt,0));
9177 PushString(aStr);
9178 }
9179 }
9180
ScReplaceB()9181 void ScInterpreter::ScReplaceB()
9182 {
9183 if ( !MustHaveParamCount( GetByte(), 4 ) )
9184 return;
9185
9186 OUString aNewStr = GetString().getString();
9187 const sal_Int32 nCount = GetStringPositionArgument();
9188 const sal_Int32 nPos = GetStringPositionArgument();
9189 OUString aOldStr = GetString().getString();
9190 int nLen = getLengthB( aOldStr );
9191 if (nPos < 1.0 || nPos > nLen || nCount < 0.0 || nPos + nCount -1 > nLen)
9192 PushIllegalArgument();
9193 else
9194 {
9195 // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as
9196 // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount)
9197 OUString aStr1 = lcl_LeftB( aOldStr, nPos - 1 );
9198 OUString aStr3 = lcl_RightB( aOldStr, nLen - nPos - nCount + 1);
9199
9200 PushString( aStr1 + aNewStr + aStr3 );
9201 }
9202 }
9203
ScFindB()9204 void ScInterpreter::ScFindB()
9205 {
9206 sal_uInt8 nParamCount = GetByte();
9207 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9208 return;
9209
9210 sal_Int32 nStart;
9211 if ( nParamCount == 3 )
9212 nStart = GetStringPositionArgument();
9213 else
9214 nStart = 1;
9215 OUString aStr = GetString().getString();
9216 int nLen = getLengthB( aStr );
9217 OUString asStr = GetString().getString();
9218 int nsLen = getLengthB( asStr );
9219 if ( nStart < 1 || nStart > nLen - nsLen + 1 )
9220 PushIllegalArgument();
9221 else
9222 {
9223 // create a string from sStr starting at nStart
9224 OUString aBuf = lcl_RightB( aStr, nLen - nStart + 1 );
9225 // search aBuf for asStr
9226 sal_Int32 nPos = aBuf.indexOf( asStr, 0 );
9227 if ( nPos == -1 )
9228 PushNoValue();
9229 else
9230 {
9231 // obtain byte value of nPos
9232 int nBytePos = lcl_getLengthB( aBuf, nPos );
9233 PushDouble( nBytePos + nStart );
9234 }
9235 }
9236 }
9237
ScSearchB()9238 void ScInterpreter::ScSearchB()
9239 {
9240 sal_uInt8 nParamCount = GetByte();
9241 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9242 return;
9243
9244 sal_Int32 nStart;
9245 if ( nParamCount == 3 )
9246 {
9247 nStart = GetStringPositionArgument();
9248 if( nStart < 1 )
9249 {
9250 PushIllegalArgument();
9251 return;
9252 }
9253 }
9254 else
9255 nStart = 1;
9256 OUString aStr = GetString().getString();
9257 sal_Int32 nLen = getLengthB( aStr );
9258 OUString asStr = GetString().getString();
9259 sal_Int32 nsLen = nStart - 1;
9260 if( nsLen >= nLen )
9261 PushNoValue();
9262 else
9263 {
9264 // create a string from sStr starting at nStart
9265 OUString aSubStr( lcl_RightB( aStr, nLen - nStart + 1 ) );
9266 // search aSubStr for asStr
9267 sal_Int32 nPos = 0;
9268 sal_Int32 nEndPos = aSubStr.getLength();
9269 utl::SearchParam::SearchType eSearchType = DetectSearchType( asStr, mrDoc );
9270 utl::SearchParam sPar( asStr, eSearchType, false, '~', false );
9271 utl::TextSearch sT( sPar, *ScGlobal::getCharClassPtr() );
9272 if ( !sT.SearchForward( aSubStr, &nPos, &nEndPos ) )
9273 PushNoValue();
9274 else
9275 {
9276 // obtain byte value of nPos
9277 int nBytePos = lcl_getLengthB( aSubStr, nPos );
9278 PushDouble( nBytePos + nStart );
9279 }
9280 }
9281 }
9282
ScRight()9283 void ScInterpreter::ScRight()
9284 {
9285 sal_uInt8 nParamCount = GetByte();
9286 if ( !MustHaveParamCount( nParamCount, 1, 2 ) )
9287 return;
9288
9289 sal_Int32 n;
9290 if (nParamCount == 2)
9291 {
9292 n = GetStringPositionArgument();
9293 if (n < 0)
9294 {
9295 PushIllegalArgument();
9296 return ;
9297 }
9298 }
9299 else
9300 n = 1;
9301 OUString aStr = GetString().getString();
9302 sal_Int32 nLen = aStr.getLength();
9303 if ( nLen <= n )
9304 PushString( aStr );
9305 else
9306 {
9307 sal_Int32 nIdx = nLen;
9308 sal_Int32 nCnt = 0;
9309 while ( nIdx > 0 && n > nCnt )
9310 {
9311 aStr.iterateCodePoints( &nIdx, -1 );
9312 ++nCnt;
9313 }
9314 aStr = aStr.copy( nIdx, nLen - nIdx );
9315 PushString( aStr );
9316 }
9317 }
9318
ScSearch()9319 void ScInterpreter::ScSearch()
9320 {
9321 sal_uInt8 nParamCount = GetByte();
9322 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
9323 return;
9324
9325 sal_Int32 nStart;
9326 if (nParamCount == 3)
9327 {
9328 nStart = GetStringPositionArgument();
9329 if( nStart < 1 )
9330 {
9331 PushIllegalArgument();
9332 return;
9333 }
9334 }
9335 else
9336 nStart = 1;
9337 OUString sStr = GetString().getString();
9338 OUString SearchStr = GetString().getString();
9339 sal_Int32 nPos = nStart - 1;
9340 sal_Int32 nEndPos = sStr.getLength();
9341 if( nPos >= nEndPos )
9342 PushNoValue();
9343 else
9344 {
9345 utl::SearchParam::SearchType eSearchType = DetectSearchType( SearchStr, mrDoc );
9346 utl::SearchParam sPar(SearchStr, eSearchType, false, '~', false);
9347 utl::TextSearch sT( sPar, *ScGlobal::getCharClassPtr() );
9348 bool bBool = sT.SearchForward(sStr, &nPos, &nEndPos);
9349 if (!bBool)
9350 PushNoValue();
9351 else
9352 {
9353 sal_Int32 nIdx = 0;
9354 sal_Int32 nCnt = 0;
9355 while ( nIdx <= nPos )
9356 {
9357 sStr.iterateCodePoints( &nIdx );
9358 ++nCnt;
9359 }
9360 PushDouble( static_cast<double>(nCnt) );
9361 }
9362 }
9363 }
9364
ScRegex()9365 void ScInterpreter::ScRegex()
9366 {
9367 const sal_uInt8 nParamCount = GetByte();
9368 if (!MustHaveParamCount( nParamCount, 2, 4))
9369 return;
9370
9371 // Flags are supported only for replacement, search match flags can be
9372 // individually and much more flexible set in the regular expression
9373 // pattern using (?ismwx-ismwx)
9374 bool bGlobalReplacement = false;
9375 sal_Int32 nOccurrence = 1; // default first occurrence, if any
9376 if (nParamCount == 4)
9377 {
9378 // Argument can be either string or double.
9379 double fOccurrence;
9380 svl::SharedString aFlagsString;
9381 bool bDouble;
9382 if (!IsMissing())
9383 bDouble = GetDoubleOrString( fOccurrence, aFlagsString);
9384 else
9385 {
9386 // For an omitted argument keep the default.
9387 PopError();
9388 bDouble = true;
9389 fOccurrence = nOccurrence;
9390 }
9391 if (nGlobalError != FormulaError::NONE)
9392 {
9393 PushError( nGlobalError);
9394 return;
9395 }
9396 if (bDouble)
9397 {
9398 if (!CheckStringPositionArgument( fOccurrence))
9399 {
9400 PushError( FormulaError::IllegalArgument);
9401 return;
9402 }
9403 nOccurrence = static_cast<sal_Int32>(fOccurrence);
9404 }
9405 else
9406 {
9407 const OUString aFlags( aFlagsString.getString());
9408 // Empty flags string is valid => no flag set.
9409 if (aFlags.getLength() > 1)
9410 {
9411 // Only one flag supported.
9412 PushIllegalArgument();
9413 return;
9414 }
9415 if (aFlags.getLength() == 1)
9416 {
9417 if (aFlags.indexOf('g') >= 0)
9418 bGlobalReplacement = true;
9419 else
9420 {
9421 // Unsupported flag.
9422 PushIllegalArgument();
9423 return;
9424 }
9425 }
9426 }
9427 }
9428
9429 bool bReplacement = false;
9430 OUString aReplacement;
9431 if (nParamCount >= 3)
9432 {
9433 // A missing argument is not an empty string to replace the match.
9434 // nOccurrence==0 forces no replacement, so simply discard the
9435 // argument.
9436 if (IsMissing() || nOccurrence == 0)
9437 PopError();
9438 else
9439 {
9440 aReplacement = GetString().getString();
9441 bReplacement = true;
9442 }
9443 }
9444 // If bGlobalReplacement==true and bReplacement==false then
9445 // bGlobalReplacement is silently ignored.
9446
9447 OUString aExpression = GetString().getString();
9448 OUString aText = GetString().getString();
9449
9450 if (nGlobalError != FormulaError::NONE)
9451 {
9452 PushError( nGlobalError);
9453 return;
9454 }
9455
9456 // 0-th match or replacement is none, return original string early.
9457 if (nOccurrence == 0)
9458 {
9459 PushString( aText);
9460 return;
9461 }
9462
9463 const icu::UnicodeString aIcuExpression(
9464 reinterpret_cast<const UChar*>(aExpression.getStr()), aExpression.getLength());
9465 UErrorCode status = U_ZERO_ERROR;
9466 icu::RegexMatcher aRegexMatcher( aIcuExpression, 0, status);
9467 if (U_FAILURE(status))
9468 {
9469 // Invalid regex.
9470 PushIllegalArgument();
9471 return;
9472 }
9473 // Guard against pathological patterns, limit steps of engine, see
9474 // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00
9475 aRegexMatcher.setTimeLimit( 23*1000, status);
9476
9477 const icu::UnicodeString aIcuText( reinterpret_cast<const UChar*>(aText.getStr()), aText.getLength());
9478 aRegexMatcher.reset( aIcuText);
9479
9480 if (!bReplacement)
9481 {
9482 // Find n-th occurrence.
9483 sal_Int32 nCount = 0;
9484 #if (U_ICU_VERSION_MAJOR_NUM < 55)
9485 int32_t nStartPos = 0;
9486 while (aRegexMatcher.find(nStartPos, status) && U_SUCCESS(status) && ++nCount < nOccurrence)
9487 #else
9488 while (aRegexMatcher.find(status) && U_SUCCESS(status) && ++nCount < nOccurrence)
9489 #endif
9490 ;
9491 if (U_FAILURE(status))
9492 {
9493 // Some error.
9494 PushIllegalArgument();
9495 return;
9496 }
9497 // n-th match found?
9498 if (nCount != nOccurrence)
9499 {
9500 PushError( FormulaError::NotAvailable);
9501 return;
9502 }
9503 // Extract matched text.
9504 icu::UnicodeString aMatch( aRegexMatcher.group( status));
9505 if (U_FAILURE(status))
9506 {
9507 // Some error.
9508 PushIllegalArgument();
9509 return;
9510 }
9511 OUString aResult( reinterpret_cast<const sal_Unicode*>(aMatch.getBuffer()), aMatch.length());
9512 PushString( aResult);
9513 return;
9514 }
9515
9516 const icu::UnicodeString aIcuReplacement(
9517 reinterpret_cast<const UChar*>(aReplacement.getStr()), aReplacement.getLength());
9518 icu::UnicodeString aReplaced;
9519 if (bGlobalReplacement)
9520 // Replace all occurrences of match with replacement.
9521 aReplaced = aRegexMatcher.replaceAll( aIcuReplacement, status);
9522 else if (nOccurrence == 1)
9523 // Replace first occurrence of match with replacement.
9524 aReplaced = aRegexMatcher.replaceFirst( aIcuReplacement, status);
9525 else
9526 {
9527 // Replace n-th occurrence of match with replacement.
9528 sal_Int32 nCount = 0;
9529 #if (U_ICU_VERSION_MAJOR_NUM < 55)
9530 int32_t nStartPos = 0;
9531 while (aRegexMatcher.find(nStartPos, status) && U_SUCCESS(status))
9532 #else
9533 while (aRegexMatcher.find(status) && U_SUCCESS(status))
9534 #endif
9535 {
9536 // XXX NOTE: After several RegexMatcher::find() the
9537 // RegexMatcher::appendReplacement() still starts at the
9538 // beginning (or after the last appendReplacement() position
9539 // which is none here) and copies the original text up to the
9540 // current found match and then replaces the found match.
9541 if (++nCount == nOccurrence)
9542 {
9543 aRegexMatcher.appendReplacement( aReplaced, aIcuReplacement, status);
9544 break;
9545 }
9546 }
9547 aRegexMatcher.appendTail( aReplaced);
9548 }
9549 if (U_FAILURE(status))
9550 {
9551 // Some error, e.g. extraneous $1 without group.
9552 PushIllegalArgument();
9553 return;
9554 }
9555 OUString aResult( reinterpret_cast<const sal_Unicode*>(aReplaced.getBuffer()), aReplaced.length());
9556 PushString( aResult);
9557 }
9558
ScMid()9559 void ScInterpreter::ScMid()
9560 {
9561 if ( !MustHaveParamCount( GetByte(), 3 ) )
9562 return;
9563
9564 const sal_Int32 nSubLen = GetStringPositionArgument();
9565 const sal_Int32 nStart = GetStringPositionArgument();
9566 OUString aStr = GetString().getString();
9567 if ( nStart < 1 || nSubLen < 0 )
9568 PushIllegalArgument();
9569 else if (nStart > kScInterpreterMaxStrLen || nSubLen > kScInterpreterMaxStrLen)
9570 PushError(FormulaError::StringOverflow);
9571 else
9572 {
9573 sal_Int32 nLen = aStr.getLength();
9574 sal_Int32 nIdx = 0;
9575 sal_Int32 nCnt = 0;
9576 while ( nIdx < nLen && nStart - 1 > nCnt )
9577 {
9578 aStr.iterateCodePoints( &nIdx );
9579 ++nCnt;
9580 }
9581 sal_Int32 nIdx0 = nIdx; //start position
9582
9583 while ( nIdx < nLen && nStart + nSubLen - 1 > nCnt )
9584 {
9585 aStr.iterateCodePoints( &nIdx );
9586 ++nCnt;
9587 }
9588 aStr = aStr.copy( nIdx0, nIdx - nIdx0 );
9589 PushString( aStr );
9590 }
9591 }
9592
ScText()9593 void ScInterpreter::ScText()
9594 {
9595 if ( !MustHaveParamCount( GetByte(), 2 ) )
9596 return;
9597
9598 OUString sFormatString = GetString().getString();
9599 svl::SharedString aStr;
9600 bool bString = false;
9601 double fVal = 0.0;
9602 switch (GetStackType())
9603 {
9604 case svError:
9605 PopError();
9606 break;
9607 case svDouble:
9608 fVal = PopDouble();
9609 break;
9610 default:
9611 {
9612 FormulaConstTokenRef xTok( PopToken());
9613 if (nGlobalError == FormulaError::NONE)
9614 {
9615 PushTokenRef( xTok);
9616 // Temporarily override the ConvertStringToValue()
9617 // error for GetCellValue() / GetCellValueOrZero()
9618 FormulaError nSErr = mnStringNoValueError;
9619 mnStringNoValueError = FormulaError::NotNumericString;
9620 fVal = GetDouble();
9621 mnStringNoValueError = nSErr;
9622 if (nGlobalError == FormulaError::NotNumericString)
9623 {
9624 // Not numeric.
9625 nGlobalError = FormulaError::NONE;
9626 PushTokenRef( xTok);
9627 aStr = GetString();
9628 bString = true;
9629 }
9630 }
9631 }
9632 }
9633 if (nGlobalError != FormulaError::NONE)
9634 PushError( nGlobalError);
9635 else if (sFormatString.isEmpty())
9636 {
9637 // Mimic the Excel behaviour that
9638 // * anything numeric returns an empty string
9639 // * text convertible to numeric returns an empty string
9640 // * any other text returns that text
9641 // Conversion was detected above.
9642 if (bString)
9643 PushString( aStr);
9644 else
9645 PushString( OUString());
9646 }
9647 else
9648 {
9649 OUString aResult;
9650 const Color* pColor = nullptr;
9651 LanguageType eCellLang;
9652 const ScPatternAttr* pPattern = mrDoc.GetPattern(
9653 aPos.Col(), aPos.Row(), aPos.Tab() );
9654 if ( pPattern )
9655 eCellLang = pPattern->GetItem( ATTR_LANGUAGE_FORMAT ).GetValue();
9656 else
9657 eCellLang = ScGlobal::eLnge;
9658 if (bString)
9659 {
9660 if (!pFormatter->GetPreviewString( sFormatString, aStr.getString(),
9661 aResult, &pColor, eCellLang))
9662 PushIllegalArgument();
9663 else
9664 PushString( aResult);
9665 }
9666 else
9667 {
9668 if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal,
9669 aResult, &pColor, eCellLang))
9670 PushIllegalArgument();
9671 else
9672 PushString( aResult);
9673 }
9674 }
9675 }
9676
ScSubstitute()9677 void ScInterpreter::ScSubstitute()
9678 {
9679 sal_uInt8 nParamCount = GetByte();
9680 if ( !MustHaveParamCount( nParamCount, 3, 4 ) )
9681 return;
9682
9683 sal_Int32 nCnt;
9684 if (nParamCount == 4)
9685 {
9686 nCnt = GetStringPositionArgument();
9687 if (nCnt < 1)
9688 {
9689 PushIllegalArgument();
9690 return;
9691 }
9692 }
9693 else
9694 nCnt = 0;
9695 OUString sNewStr = GetString().getString();
9696 OUString sOldStr = GetString().getString();
9697 OUString sStr = GetString().getString();
9698 sal_Int32 nPos = 0;
9699 sal_Int32 nCount = 0;
9700 sal_Int32 nNewLen = sNewStr.getLength();
9701 sal_Int32 nOldLen = sOldStr.getLength();
9702 while( true )
9703 {
9704 nPos = sStr.indexOf( sOldStr, nPos );
9705 if (nPos != -1)
9706 {
9707 nCount++;
9708 if( !nCnt || nCount == nCnt )
9709 {
9710 sStr = sStr.replaceAt(nPos,nOldLen, "");
9711 if ( CheckStringResultLen( sStr, sNewStr ) )
9712 {
9713 sStr = sStr.replaceAt(nPos, 0, sNewStr);
9714 nPos = sal::static_int_cast<sal_Int32>( nPos + nNewLen );
9715 }
9716 else
9717 break;
9718 }
9719 else
9720 nPos++;
9721 }
9722 else
9723 break;
9724 }
9725 PushString( sStr );
9726 }
9727
ScRept()9728 void ScInterpreter::ScRept()
9729 {
9730 if ( !MustHaveParamCount( GetByte(), 2 ) )
9731 return;
9732
9733 sal_Int32 nCnt = GetStringPositionArgument();
9734 OUString aStr = GetString().getString();
9735 if (nCnt < 0)
9736 PushIllegalArgument();
9737 else if (static_cast<double>(nCnt) * aStr.getLength() > kScInterpreterMaxStrLen)
9738 {
9739 PushError( FormulaError::StringOverflow );
9740 }
9741 else if (nCnt == 0)
9742 PushString( EMPTY_OUSTRING );
9743 else
9744 {
9745 const sal_Int32 nLen = aStr.getLength();
9746 OUStringBuffer aRes(nCnt*nLen);
9747 while( nCnt-- )
9748 aRes.append(aStr);
9749 PushString( aRes.makeStringAndClear() );
9750 }
9751 }
9752
ScConcat()9753 void ScInterpreter::ScConcat()
9754 {
9755 sal_uInt8 nParamCount = GetByte();
9756 OUString aRes;
9757 while( nParamCount-- > 0)
9758 {
9759 OUString aStr = GetString().getString();
9760 if (CheckStringResultLen( aRes, aStr))
9761 aRes = aStr + aRes;
9762 else
9763 break;
9764 }
9765 PushString( aRes );
9766 }
9767
GetErrorType()9768 FormulaError ScInterpreter::GetErrorType()
9769 {
9770 FormulaError nErr;
9771 FormulaError nOldError = nGlobalError;
9772 nGlobalError = FormulaError::NONE;
9773 switch ( GetStackType() )
9774 {
9775 case svRefList :
9776 {
9777 FormulaConstTokenRef x = PopToken();
9778 if (nGlobalError != FormulaError::NONE)
9779 nErr = nGlobalError;
9780 else
9781 {
9782 const ScRefList* pRefList = x->GetRefList();
9783 size_t n = pRefList->size();
9784 if (!n)
9785 nErr = FormulaError::NoRef;
9786 else if (n > 1)
9787 nErr = FormulaError::NoValue;
9788 else
9789 {
9790 ScRange aRange;
9791 DoubleRefToRange( (*pRefList)[0], aRange);
9792 if (nGlobalError != FormulaError::NONE)
9793 nErr = nGlobalError;
9794 else
9795 {
9796 ScAddress aAdr;
9797 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
9798 nErr = mrDoc.GetErrCode( aAdr );
9799 else
9800 nErr = nGlobalError;
9801 }
9802 }
9803 }
9804 }
9805 break;
9806 case svDoubleRef :
9807 {
9808 ScRange aRange;
9809 PopDoubleRef( aRange );
9810 if ( nGlobalError != FormulaError::NONE )
9811 nErr = nGlobalError;
9812 else
9813 {
9814 ScAddress aAdr;
9815 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
9816 nErr = mrDoc.GetErrCode( aAdr );
9817 else
9818 nErr = nGlobalError;
9819 }
9820 }
9821 break;
9822 case svSingleRef :
9823 {
9824 ScAddress aAdr;
9825 PopSingleRef( aAdr );
9826 if ( nGlobalError != FormulaError::NONE )
9827 nErr = nGlobalError;
9828 else
9829 nErr = mrDoc.GetErrCode( aAdr );
9830 }
9831 break;
9832 default:
9833 PopError();
9834 nErr = nGlobalError;
9835 }
9836 nGlobalError = nOldError;
9837 return nErr;
9838 }
9839
ScErrorType()9840 void ScInterpreter::ScErrorType()
9841 {
9842 FormulaError nErr = GetErrorType();
9843 if ( nErr != FormulaError::NONE )
9844 {
9845 nGlobalError = FormulaError::NONE;
9846 PushDouble( static_cast<double>(nErr) );
9847 }
9848 else
9849 {
9850 PushNA();
9851 }
9852 }
9853
ScErrorType_ODF()9854 void ScInterpreter::ScErrorType_ODF()
9855 {
9856 FormulaError nErr = GetErrorType();
9857 sal_uInt16 nErrType;
9858
9859 switch ( nErr )
9860 {
9861 case FormulaError::ParameterExpected : // #NULL!
9862 nErrType = 1;
9863 break;
9864 case FormulaError::DivisionByZero : // #DIV/0!
9865 nErrType = 2;
9866 break;
9867 case FormulaError::NoValue : // #VALUE!
9868 nErrType = 3;
9869 break;
9870 case FormulaError::NoRef : // #REF!
9871 nErrType = 4;
9872 break;
9873 case FormulaError::NoName : // #NAME?
9874 nErrType = 5;
9875 break;
9876 case FormulaError::IllegalFPOperation : // #NUM!
9877 nErrType = 6;
9878 break;
9879 case FormulaError::NotAvailable : // #N/A
9880 nErrType = 7;
9881 break;
9882 /*
9883 #GETTING_DATA is a message that can appear in Excel when a large or
9884 complex worksheet is being calculated. In Excel 2007 and newer,
9885 operations are grouped so more complicated cells may finish after
9886 earlier ones do. While the calculations are still processing, the
9887 unfinished cells may display #GETTING_DATA.
9888 Because the message is temporary and disappears when the calculations
9889 complete, this isn’t a true error.
9890 No calc error code known (yet).
9891
9892 case : // GETTING_DATA
9893 nErrType = 8;
9894 break;
9895 */
9896 default :
9897 nErrType = 0;
9898 break;
9899 }
9900
9901 if ( nErrType )
9902 {
9903 nGlobalError =FormulaError::NONE;
9904 PushDouble( nErrType );
9905 }
9906 else
9907 PushNA();
9908 }
9909
MayBeRegExp(const OUString & rStr)9910 bool ScInterpreter::MayBeRegExp( const OUString& rStr )
9911 {
9912 if ( rStr.isEmpty() || (rStr.getLength() == 1 && !rStr.startsWith(".")) )
9913 return false; // single meta characters can not be a regexp
9914 // First two characters are wildcard '?' and '*' characters.
9915 static const sal_Unicode cre[] = { '?','*','+','.','[',']','^','$','\\','<','>','(',')','|', 0 };
9916 const sal_Unicode* p1 = rStr.getStr();
9917 sal_Unicode c1;
9918 while ( ( c1 = *p1++ ) != 0 )
9919 {
9920 const sal_Unicode* p2 = cre;
9921 while ( *p2 )
9922 {
9923 if ( c1 == *p2++ )
9924 return true;
9925 }
9926 }
9927 return false;
9928 }
9929
MayBeWildcard(const OUString & rStr)9930 bool ScInterpreter::MayBeWildcard( const OUString& rStr )
9931 {
9932 // Wildcards with '~' escape, if there are no wildcards then an escaped
9933 // character does not make sense, but it modifies the search pattern in an
9934 // Excel compatible wildcard search...
9935 static const sal_Unicode cw[] = { '*','?','~', 0 };
9936 const sal_Unicode* p1 = rStr.getStr();
9937 sal_Unicode c1;
9938 while ( ( c1 = *p1++ ) != 0 )
9939 {
9940 const sal_Unicode* p2 = cw;
9941 while ( *p2 )
9942 {
9943 if ( c1 == *p2++ )
9944 return true;
9945 }
9946 }
9947 return false;
9948 }
9949
DetectSearchType(const OUString & rStr,const ScDocument & rDoc)9950 utl::SearchParam::SearchType ScInterpreter::DetectSearchType( const OUString& rStr, const ScDocument& rDoc )
9951 {
9952 if (rDoc.GetDocOptions().IsFormulaWildcardsEnabled())
9953 return MayBeWildcard( rStr ) ? utl::SearchParam::SearchType::Wildcard : utl::SearchParam::SearchType::Normal;
9954 if (rDoc.GetDocOptions().IsFormulaRegexEnabled())
9955 return MayBeRegExp( rStr ) ? utl::SearchParam::SearchType::Regexp : utl::SearchParam::SearchType::Normal;
9956 return utl::SearchParam::SearchType::Normal;
9957 }
9958
lcl_LookupQuery(ScAddress & o_rResultPos,ScDocument & rDoc,const ScInterpreterContext & rContext,const ScQueryParam & rParam,const ScQueryEntry & rEntry)9959 static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument& rDoc, const ScInterpreterContext& rContext,
9960 const ScQueryParam & rParam, const ScQueryEntry & rEntry )
9961 {
9962 bool bFound = false;
9963 ScQueryCellIterator aCellIter( rDoc, rContext, rParam.nTab, rParam, false);
9964 if (rEntry.eOp != SC_EQUAL)
9965 {
9966 // range lookup <= or >=
9967 SCCOL nCol;
9968 SCROW nRow;
9969 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow);
9970 if (bFound)
9971 {
9972 o_rResultPos.SetCol( nCol);
9973 o_rResultPos.SetRow( nRow);
9974 }
9975 }
9976 else if (aCellIter.GetFirst())
9977 {
9978 // EQUAL
9979 bFound = true;
9980 o_rResultPos.SetCol( aCellIter.GetCol());
9981 o_rResultPos.SetRow( aCellIter.GetRow());
9982 }
9983 return bFound;
9984 }
9985
9986 // tdf#121052:
9987 // =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted)
9988 // [SearchCriterion] is the value searched for in the first column of the array.
9989 // [RangeArray] is the reference, which is to comprise at least two columns.
9990 // [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1.
9991 //
9992 // Prerequisite of lcl_getPrevRowWithEmptyValueLookup():
9993 // Value referenced by [SearchCriterion] is empty.
9994 // lcl_getPrevRowWithEmptyValueLookup() performs following checks:
9995 // - if we run query with "exact match" mode (i.e. VLOOKUP)
9996 // - and if we already have the same lookup done before but for another row
9997 // which is also had empty [SearchCriterion]
9998 //
9999 // then
10000 // we could say, that for current row we could reuse results of the cached call which was done for the row2
10001 // In this case we return row index, which is >= 0.
10002 //
10003 // Elsewhere
10004 // -1 is returned, which will lead to default behavior =>
10005 // complete lookup will be done in RangeArray inside lcl_LookupQuery() method.
10006 //
10007 // This method was added only for speed up to avoid several useless complete
10008 // lookups inside [RangeArray] for searching empty strings.
10009 //
lcl_getPrevRowWithEmptyValueLookup(const ScLookupCache & rCache,const ScLookupCache::QueryCriteria & rCriteria,const ScQueryParam & rParam)10010 static SCROW lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache& rCache,
10011 const ScLookupCache::QueryCriteria& rCriteria, const ScQueryParam & rParam)
10012 {
10013 // is lookup value empty?
10014 const ScQueryEntry& rEntry = rParam.GetEntry(0);
10015 const ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
10016 if (! rItem.maString.getString().isEmpty())
10017 return -1; // not found
10018
10019 // try to find the row index for which we have already performed lookup
10020 // and have some result of it inside cache
10021 return rCache.lookup( rCriteria );
10022 }
10023
LookupQueryWithCache(ScAddress & o_rResultPos,const ScQueryParam & rParam) const10024 bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos,
10025 const ScQueryParam & rParam ) const
10026 {
10027 bool bFound = false;
10028 const ScQueryEntry& rEntry = rParam.GetEntry(0);
10029 bool bColumnsMatch = (rParam.nCol1 == rEntry.nField);
10030 OSL_ENSURE( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match");
10031 // At least all volatile functions that generate indirect references have
10032 // to force non-cached lookup.
10033 /* TODO: We could further classify volatile functions into reference
10034 * generating and not reference generating functions to have to force less
10035 * direct lookups here. We could even further attribute volatility per
10036 * parameter so it would affect only the lookup range parameter. */
10037 if (!bColumnsMatch || GetVolatileType() != NOT_VOLATILE)
10038 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry);
10039 else
10040 {
10041 ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab,
10042 rParam.nCol2, rParam.nRow2, rParam.nTab);
10043 ScLookupCache& rCache = mrDoc.GetLookupCache( aLookupRange, &mrContext );
10044 ScLookupCache::QueryCriteria aCriteria( rEntry);
10045 ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos,
10046 aCriteria, aPos);
10047
10048 // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells
10049 // This check was added only for speed up to avoid several useless complete
10050 // lookups inside [RangeArray] for searching empty strings.
10051 if (eCacheResult == ScLookupCache::NOT_CACHED && aCriteria.isEmptyStringQuery())
10052 {
10053 const SCROW nPrevRowWithEmptyValueLookup = lcl_getPrevRowWithEmptyValueLookup(rCache, aCriteria, rParam);
10054 if (nPrevRowWithEmptyValueLookup >= 0)
10055 {
10056 // make the same lookup using cache with different row index
10057 // (this lookup was already cached)
10058 ScAddress aPosPrev(aPos);
10059 aPosPrev.SetRow(nPrevRowWithEmptyValueLookup);
10060
10061 eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPosPrev );
10062 }
10063 }
10064
10065 switch (eCacheResult)
10066 {
10067 case ScLookupCache::NOT_CACHED :
10068 case ScLookupCache::CRITERIA_DIFFERENT :
10069 bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry);
10070 if (eCacheResult == ScLookupCache::NOT_CACHED)
10071 rCache.insert( o_rResultPos, aCriteria, aPos, bFound);
10072 break;
10073 case ScLookupCache::FOUND :
10074 bFound = true;
10075 break;
10076 case ScLookupCache::NOT_AVAILABLE :
10077 ; // nothing, bFound remains FALSE
10078 break;
10079 }
10080 }
10081 return bFound;
10082 }
10083
10084 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
10085