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