fix baseline build (old cairo) - 'cairo_rectangle_int_t' does not name a type
[LibreOffice.git] / sc / source / core / tool / interpr1.cxx
blob7349771e004d981588a19edc017708a5404f57b8
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "interpre.hxx"
22 #include "scitems.hxx"
23 #include <editeng/langitem.hxx>
24 #include <editeng/justifyitem.hxx>
25 #include <formula/random.hxx>
26 #include <osl/thread.h>
27 #include <svx/algitem.hxx>
28 #include <unotools/textsearch.hxx>
29 #include <svl/zforlist.hxx>
30 #include <svl/zformat.hxx>
31 #include <tools/urlobj.hxx>
32 #include <unotools/charclass.hxx>
33 #include <sfx2/docfile.hxx>
34 #include <sfx2/printer.hxx>
35 #include <unotools/collatorwrapper.hxx>
36 #include <unotools/transliterationwrapper.hxx>
37 #include <rtl/ustring.hxx>
38 #include <unicode/uchar.h>
40 #include "patattr.hxx"
41 #include "global.hxx"
42 #include "document.hxx"
43 #include "dociter.hxx"
44 #include "formulacell.hxx"
45 #include "scmatrix.hxx"
46 #include "docoptio.hxx"
47 #include "globstr.hrc"
48 #include "attrib.hxx"
49 #include "jumpmatrix.hxx"
50 #include "cellkeytranslator.hxx"
51 #include "lookupcache.hxx"
52 #include "rangenam.hxx"
53 #include "rangeutl.hxx"
54 #include "compiler.hxx"
55 #include "externalrefmgr.hxx"
56 #include <basic/sbstar.hxx>
57 #include "doubleref.hxx"
58 #include "queryparam.hxx"
59 #include "queryentry.hxx"
60 #include "tokenarray.hxx"
61 #include "compare.hxx"
63 #include <comphelper/processfactory.hxx>
64 #include <comphelper/random.hxx>
65 #include <comphelper/string.hxx>
66 #include <svl/sharedstringpool.hxx>
68 #include <stdlib.h>
69 #include <string.h>
70 #include <math.h>
71 #include <vector>
72 #include <memory>
73 #include <limits>
75 static const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48
77 IMPL_FIXEDMEMPOOL_NEWDEL( ScTokenStack )
78 IMPL_FIXEDMEMPOOL_NEWDEL( ScInterpreter )
80 ScCalcConfig ScInterpreter::maGlobalConfig;
81 ScTokenStack* ScInterpreter::pGlobalStack = NULL;
82 bool ScInterpreter::bGlobalStackInUse = false;
84 using namespace formula;
85 using ::std::unique_ptr;
87 void ScInterpreter::ScIfJump()
89 const short* pJump = pCur->GetJump();
90 short nJumpCount = pJump[ 0 ];
91 MatrixDoubleRefToMatrix();
92 switch ( GetStackType() )
94 case svMatrix:
96 ScMatrixRef pMat = PopMatrix();
97 if ( !pMat )
98 PushIllegalParameter();
99 else
101 FormulaTokenRef xNew;
102 ScTokenMatrixMap::const_iterator aMapIter;
103 // DoubleError handled by JumpMatrix
104 pMat->SetErrorInterpreter( NULL);
105 SCSIZE nCols, nRows;
106 pMat->GetDimensions( nCols, nRows );
107 if ( nCols == 0 || nRows == 0 )
109 PushIllegalArgument();
110 return;
112 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
113 pCur)) != pTokenMatrixMap->end()))
114 xNew = (*aMapIter).second;
115 else
117 ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
118 for ( SCSIZE nC=0; nC < nCols; ++nC )
120 for ( SCSIZE nR=0; nR < nRows; ++nR )
122 double fVal;
123 bool bTrue;
124 bool bIsValue = pMat->IsValue(nC, nR);
125 if (bIsValue)
127 fVal = pMat->GetDouble(nC, nR);
128 bIsValue = ::rtl::math::isFinite(fVal);
129 bTrue = bIsValue && (fVal != 0.0);
130 if (bTrue)
131 fVal = 1.0;
133 else
135 // Treat empty and empty path as 0, but string
136 // as error.
137 bIsValue = (!pMat->IsString(nC, nR) || pMat->IsEmpty(nC, nR));
138 bTrue = false;
139 fVal = (bIsValue ? 0.0 : CreateDoubleError( errNoValue));
141 if ( bTrue )
142 { // TRUE
143 if( nJumpCount >= 2 )
144 { // THEN path
145 pJumpMat->SetJump( nC, nR, fVal,
146 pJump[ 1 ],
147 pJump[ nJumpCount ]);
149 else
150 { // no parameter given for THEN
151 pJumpMat->SetJump( nC, nR, fVal,
152 pJump[ nJumpCount ],
153 pJump[ nJumpCount ]);
156 else
157 { // FALSE
158 if( nJumpCount == 3 && bIsValue )
159 { // ELSE path
160 pJumpMat->SetJump( nC, nR, fVal,
161 pJump[ 2 ],
162 pJump[ nJumpCount ]);
164 else
165 { // no parameter given for ELSE,
166 // or DoubleError
167 pJumpMat->SetJump( nC, nR, fVal,
168 pJump[ nJumpCount ],
169 pJump[ nJumpCount ]);
174 xNew = new ScJumpMatrixToken( pJumpMat );
175 GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type(pCur, xNew));
177 if (!xNew.get())
179 PushIllegalArgument();
180 return;
182 PushTempToken( xNew.get());
183 // set endpoint of path for main code line
184 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
187 break;
188 default:
190 if ( GetBool() )
191 { // TRUE
192 if( nJumpCount >= 2 )
193 { // THEN path
194 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
196 else
197 { // no parameter given for THEN
198 nFuncFmtType = css::util::NumberFormat::LOGICAL;
199 PushInt(1);
200 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
203 else
204 { // FALSE
205 if( nJumpCount == 3 )
206 { // ELSE path
207 aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
209 else
210 { // no parameter given for ELSE
211 nFuncFmtType = css::util::NumberFormat::LOGICAL;
212 PushInt(0);
213 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
220 /** Store a matrix value in another matrix in the context of that other matrix
221 is the result matrix of a jump matrix. All arguments must be valid and are
222 not checked. */
223 static void lcl_storeJumpMatResult(
224 const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
226 if ( pMat->IsValue( nC, nR ) )
228 double fVal = pMat->GetDouble( nC, nR );
229 pJumpMat->PutResultDouble( fVal, nC, nR );
231 else if ( pMat->IsEmpty( nC, nR ) )
233 pJumpMat->PutResultEmpty( nC, nR );
235 else
237 pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
241 void ScInterpreter::ScIfError( bool bNAonly )
243 const short* pJump = pCur->GetJump();
244 short nJumpCount = pJump[ 0 ];
245 if (!sp || nJumpCount != 2)
247 // Reset nGlobalError here to not propagate the old error, if any.
248 nGlobalError = (sp ? errParameterExpected : errUnknownStackVariable);
249 PushError( nGlobalError);
250 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
251 return;
254 FormulaTokenRef xToken( pStack[ sp - 1 ] );
255 bool bError = false;
256 sal_uInt16 nOldGlobalError = nGlobalError;
257 nGlobalError = 0;
259 MatrixDoubleRefToMatrix();
260 switch (GetStackType())
262 default:
263 Pop();
264 // Act on implicitly propagated error, if any.
265 if (nOldGlobalError)
266 nGlobalError = nOldGlobalError;
267 if (nGlobalError)
268 bError = true;
269 break;
270 case svError:
271 PopError();
272 bError = true;
273 break;
274 case svDoubleRef:
275 case svSingleRef:
277 ScAddress aAdr;
278 if (!PopDoubleRefOrSingleRef( aAdr))
279 bError = true;
280 else
283 ScRefCellValue aCell;
284 aCell.assign(*pDok, aAdr);
285 nGlobalError = GetCellErrCode(aCell);
286 if (nGlobalError)
287 bError = true;
290 break;
291 case svExternalSingleRef:
292 case svExternalDoubleRef:
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)
300 bError = true;
302 break;
303 case svMatrix:
305 const ScMatrixRef pMat = PopMatrix();
306 if (!pMat || (nGlobalError && (!bNAonly || nGlobalError == NOTAVAILABLE)))
308 bError = true;
309 break; // switch
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)
319 bError = true;
320 break; // switch
322 for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
324 for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
326 sal_uInt16 nErr = pMat->GetError( nC, nR );
327 if (nErr && (!bNAonly || nErr == NOTAVAILABLE))
329 bError = true;
330 nErrorCol = nC;
331 nErrorRow = nR;
335 if (!bError)
336 break; // switch, we're done and have the result
338 FormulaTokenRef xNew;
339 ScTokenMatrixMap::const_iterator aMapIter;
340 if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
342 xNew = (*aMapIter).second;
344 else
346 const ScMatrix* pMatPtr = pMat.get();
347 ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
348 // Init all jumps to no error to save single calls. Error
349 // is the exceptional condition.
350 const double fFlagResult = CreateDoubleError( errJumpMatHasResult);
351 pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
352 // Up to first error position simply store results, no need
353 // to evaluate error conditions again.
354 SCSIZE nC = 0, nR = 0;
355 for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
357 for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
359 lcl_storeJumpMatResult(pMatPtr, pJumpMat, nC, nR);
361 if (nC != nErrorCol && nR != nErrorRow)
362 ++nC;
364 // Now the mixed cases.
365 for ( ; nC < nCols; ++nC)
367 for ( ; nR < nRows; ++nR)
369 sal_uInt16 nErr = pMat->GetError( nC, nR );
370 if (nErr && (!bNAonly || nErr == NOTAVAILABLE))
371 { // TRUE, THEN path
372 pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
374 else
375 { // FALSE, EMPTY path, store result instead
376 lcl_storeJumpMatResult(pMatPtr, pJumpMat, nC, nR);
379 nR = 0;
381 xNew = new ScJumpMatrixToken( pJumpMat );
382 GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type( pCur, xNew ));
384 nGlobalError = nOldGlobalError;
385 PushTempToken( xNew.get() );
386 // set endpoint of path for main code line
387 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
388 return;
390 break;
393 if (bError && (!bNAonly || nGlobalError == NOTAVAILABLE))
395 // error, calculate 2nd argument
396 nGlobalError = 0;
397 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
399 else
401 // no error, push 1st argument and continue
402 nGlobalError = nOldGlobalError;
403 PushTempToken( xToken.get());
404 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
408 void ScInterpreter::ScChooseJump()
410 // We have to set a jump, if there was none chosen because of an error set
411 // it to endpoint.
412 bool bHaveJump = false;
413 const short* pJump = pCur->GetJump();
414 short nJumpCount = pJump[ 0 ];
415 MatrixDoubleRefToMatrix();
416 switch ( GetStackType() )
418 case svMatrix:
420 ScMatrixRef pMat = PopMatrix();
421 if ( !pMat )
422 PushIllegalParameter();
423 else
425 FormulaTokenRef xNew;
426 ScTokenMatrixMap::const_iterator aMapIter;
427 // DoubleError handled by JumpMatrix
428 pMat->SetErrorInterpreter( NULL);
429 SCSIZE nCols, nRows;
430 pMat->GetDimensions( nCols, nRows );
431 if ( nCols == 0 || nRows == 0 )
432 PushIllegalParameter();
433 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
434 pCur)) != pTokenMatrixMap->end()))
435 xNew = (*aMapIter).second;
436 else
438 ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
439 for ( SCSIZE nC=0; nC < nCols; ++nC )
441 for ( SCSIZE nR=0; nR < nRows; ++nR )
443 double fVal;
444 bool bIsValue = pMat->IsValue(nC, nR);
445 if ( bIsValue )
447 fVal = pMat->GetDouble(nC, nR);
448 bIsValue = ::rtl::math::isFinite( fVal );
449 if ( bIsValue )
451 fVal = ::rtl::math::approxFloor( fVal);
452 if ( (fVal < 1) || (fVal >= nJumpCount))
454 bIsValue = false;
455 fVal = CreateDoubleError(
456 errIllegalArgument);
460 else
462 fVal = CreateDoubleError( errNoValue);
464 if ( bIsValue )
466 pJumpMat->SetJump( nC, nR, fVal,
467 pJump[ (short)fVal ],
468 pJump[ nJumpCount ]);
470 else
472 pJumpMat->SetJump( nC, nR, fVal,
473 pJump[ nJumpCount ],
474 pJump[ nJumpCount ]);
478 xNew = new ScJumpMatrixToken( pJumpMat );
479 GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type(
480 pCur, xNew));
482 if (xNew.get())
484 PushTempToken( xNew.get());
485 // set endpoint of path for main code line
486 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
487 bHaveJump = true;
491 break;
492 default:
494 double nJumpIndex = ::rtl::math::approxFloor( GetDouble() );
495 if (!nGlobalError && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
497 aCode.Jump( pJump[ (short) nJumpIndex ], pJump[ nJumpCount ] );
498 bHaveJump = true;
500 else
501 PushIllegalArgument();
504 if (!bHaveJump)
505 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
508 static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
510 SCSIZE nJumpCols, nJumpRows;
511 SCSIZE nResCols, nResRows;
512 SCSIZE nAdjustCols, nAdjustRows;
513 pJumpM->GetDimensions( nJumpCols, nJumpRows );
514 pJumpM->GetResMatDimensions( nResCols, nResRows );
515 if (( nJumpCols == 1 && nParmCols > nResCols ) ||
516 ( nJumpRows == 1 && nParmRows > nResRows ))
518 if ( nJumpCols == 1 && nJumpRows == 1 )
520 nAdjustCols = nParmCols > nResCols ? nParmCols : nResCols;
521 nAdjustRows = nParmRows > nResRows ? nParmRows : nResRows;
523 else if ( nJumpCols == 1 )
525 nAdjustCols = nParmCols;
526 nAdjustRows = nResRows;
528 else
530 nAdjustCols = nResCols;
531 nAdjustRows = nParmRows;
533 pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
537 bool ScInterpreter::JumpMatrix( short nStackLevel )
539 pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix();
540 bool bHasResMat = pJumpMatrix->HasResultMatrix();
541 SCSIZE nC, nR;
542 if ( nStackLevel == 2 )
544 if ( aCode.HasStacked() )
545 aCode.Pop(); // pop what Jump() pushed
546 else
548 OSL_FAIL( "ScInterpreter::JumpMatrix: pop goes the weasel" );
551 if ( !bHasResMat )
553 Pop();
554 SetError( errUnknownStackVariable );
556 else
558 pJumpMatrix->GetPos( nC, nR );
559 switch ( GetStackType() )
561 case svDouble:
563 double fVal = GetDouble();
564 if ( nGlobalError )
566 fVal = CreateDoubleError( nGlobalError );
567 nGlobalError = 0;
569 pJumpMatrix->PutResultDouble( fVal, nC, nR );
571 break;
572 case svString:
574 svl::SharedString aStr = GetString();
575 if ( nGlobalError )
577 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
578 nC, nR);
579 nGlobalError = 0;
581 else
582 pJumpMatrix->PutResultString(aStr, nC, nR);
584 break;
585 case svSingleRef:
587 ScAddress aAdr;
588 PopSingleRef( aAdr );
589 if ( nGlobalError )
591 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
592 nC, nR);
593 nGlobalError = 0;
595 else
597 ScRefCellValue aCell;
598 aCell.assign(*pDok, aAdr);
599 if (aCell.hasEmptyValue())
600 pJumpMatrix->PutResultEmpty( nC, nR );
601 else if (aCell.hasNumeric())
603 double fVal = GetCellValue(aAdr, aCell);
604 if ( nGlobalError )
606 fVal = CreateDoubleError(
607 nGlobalError);
608 nGlobalError = 0;
610 pJumpMatrix->PutResultDouble( fVal, nC, nR );
612 else
614 svl::SharedString aStr;
615 GetCellString(aStr, aCell);
616 if ( nGlobalError )
618 pJumpMatrix->PutResultDouble( CreateDoubleError(
619 nGlobalError), nC, nR);
620 nGlobalError = 0;
622 else
623 pJumpMatrix->PutResultString(aStr, nC, nR);
627 break;
628 case svDoubleRef:
629 { // upper left plus offset within matrix
630 double fVal;
631 ScRange aRange;
632 PopDoubleRef( aRange );
633 if ( nGlobalError )
635 fVal = CreateDoubleError( nGlobalError );
636 nGlobalError = 0;
637 pJumpMatrix->PutResultDouble( fVal, nC, nR );
639 else
641 // Do not modify the original range because we use it
642 // to adjust the size of the result matrix if necessary.
643 ScAddress aAdr( aRange.aStart);
644 sal_uLong nCol = (sal_uLong)aAdr.Col() + nC;
645 sal_uLong nRow = (sal_uLong)aAdr.Row() + nR;
646 if ((nCol > static_cast<sal_uLong>(aRange.aEnd.Col()) &&
647 aRange.aEnd.Col() != aRange.aStart.Col())
648 || (nRow > static_cast<sal_uLong>(aRange.aEnd.Row()) &&
649 aRange.aEnd.Row() != aRange.aStart.Row()))
651 fVal = CreateDoubleError( NOTAVAILABLE );
652 pJumpMatrix->PutResultDouble( fVal, nC, nR );
654 else
656 // Replicate column and/or row of a vector if it is
657 // one. Note that this could be a range reference
658 // that in fact consists of only one cell, e.g. A1:A1
659 if (aRange.aEnd.Col() == aRange.aStart.Col())
660 nCol = aRange.aStart.Col();
661 if (aRange.aEnd.Row() == aRange.aStart.Row())
662 nRow = aRange.aStart.Row();
663 aAdr.SetCol( static_cast<SCCOL>(nCol) );
664 aAdr.SetRow( static_cast<SCROW>(nRow) );
665 ScRefCellValue aCell;
666 aCell.assign(*pDok, aAdr);
667 if (aCell.hasEmptyValue())
668 pJumpMatrix->PutResultEmpty( nC, nR );
669 else if (aCell.hasNumeric())
671 double fCellVal = GetCellValue(aAdr, aCell);
672 if ( nGlobalError )
674 fCellVal = CreateDoubleError(
675 nGlobalError);
676 nGlobalError = 0;
678 pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
680 else
682 svl::SharedString aStr;
683 GetCellString(aStr, aCell);
684 if ( nGlobalError )
686 pJumpMatrix->PutResultDouble( CreateDoubleError(
687 nGlobalError), nC, nR);
688 nGlobalError = 0;
690 else
691 pJumpMatrix->PutResultString(aStr, nC, nR);
694 SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
695 SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
696 lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
699 break;
700 case svMatrix:
701 { // match matrix offsets
702 double fVal;
703 ScMatrixRef pMat = PopMatrix();
704 if ( nGlobalError )
706 fVal = CreateDoubleError( nGlobalError );
707 nGlobalError = 0;
708 pJumpMatrix->PutResultDouble( fVal, nC, nR );
710 else if ( !pMat )
712 fVal = CreateDoubleError( errUnknownVariable );
713 pJumpMatrix->PutResultDouble( fVal, nC, nR );
715 else
717 SCSIZE nCols, nRows;
718 pMat->GetDimensions( nCols, nRows );
719 if ((nCols <= nC && nCols != 1) ||
720 (nRows <= nR && nRows != 1))
722 fVal = CreateDoubleError( NOTAVAILABLE );
723 pJumpMatrix->PutResultDouble( fVal, nC, nR );
725 else
727 lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
729 lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
732 break;
733 case svError:
735 PopError();
736 double fVal = CreateDoubleError( nGlobalError);
737 nGlobalError = 0;
738 pJumpMatrix->PutResultDouble( fVal, nC, nR );
740 break;
741 default:
743 Pop();
744 double fVal = CreateDoubleError( errIllegalArgument);
745 pJumpMatrix->PutResultDouble( fVal, nC, nR );
750 bool bCont = pJumpMatrix->Next( nC, nR );
751 if ( bCont )
753 double fBool;
754 short nStart, nNext, nStop;
755 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
756 while ( bCont && nStart == nNext )
757 { // push all results that have no jump path
758 if ( bHasResMat && (GetDoubleErrorValue( fBool) != errJumpMatHasResult) )
760 // a false without path results in an empty path value
761 if ( fBool == 0.0 )
762 pJumpMatrix->PutResultEmptyPath( nC, nR );
763 else
764 pJumpMatrix->PutResultDouble( fBool, nC, nR );
766 bCont = pJumpMatrix->Next( nC, nR );
767 if ( bCont )
768 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
770 if ( bCont && nStart != nNext )
772 const ScTokenVec* pParams = pJumpMatrix->GetJumpParameters();
773 if ( pParams )
775 for ( ScTokenVec::const_iterator i = pParams->begin();
776 i != pParams->end(); ++i )
778 // This is not the current state of the interpreter, so
779 // push without error, and elements' errors are coded into
780 // double.
781 PushWithoutError( *(*i));
784 aCode.Jump( nStart, nNext, nStop );
787 if ( !bCont )
788 { // we're done with it, throw away jump matrix, keep result
789 ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
790 pJumpMatrix = NULL;
791 Pop();
792 PushMatrix( pResMat );
793 // Remove jump matrix from map and remember result matrix in case it
794 // could be reused in another path of the same condition.
795 if (pTokenMatrixMap)
797 pTokenMatrixMap->erase( pCur);
798 pTokenMatrixMap->insert( ScTokenMatrixMap::value_type( pCur,
799 pStack[sp-1]));
801 return true;
803 return false;
806 double ScInterpreter::Compare( ScQueryOp eOp )
808 sc::Compare aComp;
809 aComp.meOp = eOp;
810 aComp.mbIgnoreCase = pDok->GetDocOptions().IsIgnoreCase();
811 for( short i = 1; i >= 0; i-- )
813 sc::Compare::Cell& rCell = aComp.maCells[i];
815 switch ( GetRawStackType() )
817 case svEmptyCell:
818 Pop();
819 rCell.mbEmpty = true;
820 break;
821 case svMissing:
822 case svDouble:
823 rCell.mfValue = GetDouble();
824 rCell.mbValue = true;
825 break;
826 case svString:
827 rCell.maStr = GetString();
828 rCell.mbValue = false;
829 break;
830 case svDoubleRef :
831 case svSingleRef :
833 ScAddress aAdr;
834 if ( !PopDoubleRefOrSingleRef( aAdr ) )
835 break;
836 ScRefCellValue aCell;
837 aCell.assign(*pDok, aAdr);
838 if (aCell.hasEmptyValue())
839 rCell.mbEmpty = true;
840 else if (aCell.hasString())
842 svl::SharedString aStr;
843 GetCellString(aStr, aCell);
844 rCell.maStr = aStr;
845 rCell.mbValue = false;
847 else
849 rCell.mfValue = GetCellValue(aAdr, aCell);
850 rCell.mbValue = true;
853 break;
854 case svExternalSingleRef:
856 ScMatrixRef pMat = GetMatrix();
857 if (!pMat)
859 SetError( errIllegalParameter);
860 break;
863 SCSIZE nC, nR;
864 pMat->GetDimensions(nC, nR);
865 if (!nC || !nR)
867 SetError( errIllegalParameter);
868 break;
870 if (pMat->IsEmpty(0, 0))
871 rCell.mbEmpty = true;
872 else if (pMat->IsString(0, 0))
874 rCell.maStr = pMat->GetString(0, 0);
875 rCell.mbValue = false;
877 else
879 rCell.mfValue = pMat->GetDouble(0, 0);
880 rCell.mbValue = true;
883 break;
884 case svExternalDoubleRef:
885 // TODO: Find out how to handle this...
886 default:
887 SetError( errIllegalParameter);
888 break;
891 if( nGlobalError )
892 return 0;
893 nCurFmtType = nFuncFmtType = css::util::NumberFormat::LOGICAL;
894 return sc::CompareFunc(aComp);
897 sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions )
899 sc::Compare aComp;
900 aComp.meOp = eOp;
901 aComp.mbIgnoreCase = pDok->GetDocOptions().IsIgnoreCase();
902 sc::RangeMatrix aMat[2];
903 ScAddress aAdr;
904 for( short i = 1; i >= 0; i-- )
906 sc::Compare::Cell& rCell = aComp.maCells[i];
908 switch (GetRawStackType())
910 case svEmptyCell:
911 Pop();
912 rCell.mbEmpty = true;
913 break;
914 case svMissing:
915 case svDouble:
916 rCell.mfValue = GetDouble();
917 rCell.mbValue = true;
918 break;
919 case svString:
920 rCell.maStr = GetString();
921 rCell.mbValue = false;
922 break;
923 case svSingleRef:
925 PopSingleRef( aAdr );
926 ScRefCellValue aCell;
927 aCell.assign(*pDok, aAdr);
928 if (aCell.hasEmptyValue())
929 rCell.mbEmpty = true;
930 else if (aCell.hasString())
932 svl::SharedString aStr;
933 GetCellString(aStr, aCell);
934 rCell.maStr = aStr;
935 rCell.mbValue = false;
937 else
939 rCell.mfValue = GetCellValue(aAdr, aCell);
940 rCell.mbValue = true;
943 break;
944 case svDoubleRef:
945 case svMatrix:
946 aMat[i] = GetRangeMatrix();
947 if (!aMat[i].mpMat)
948 SetError( errIllegalParameter);
949 else
950 aMat[i].mpMat->SetErrorInterpreter(NULL);
951 // errors are transported as DoubleError inside matrix
952 break;
953 default:
954 SetError( errIllegalParameter);
955 break;
959 sc::RangeMatrix aRes;
961 if (nGlobalError)
963 nCurFmtType = nFuncFmtType = css::util::NumberFormat::LOGICAL;
964 return aRes;
967 if (aMat[0].mpMat && aMat[1].mpMat)
969 SCSIZE nC0, nC1;
970 SCSIZE nR0, nR1;
971 aMat[0].mpMat->GetDimensions(nC0, nR0);
972 aMat[1].mpMat->GetDimensions(nC1, nR1);
973 SCSIZE nC = std::max( nC0, nC1 );
974 SCSIZE nR = std::max( nR0, nR1 );
975 aRes.mpMat = GetNewMat( nC, nR);
976 if (!aRes.mpMat)
977 return aRes;
978 for ( SCSIZE j=0; j<nC; j++ )
980 for ( SCSIZE k=0; k<nR; k++ )
982 SCSIZE nCol = j, nRow = k;
983 if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
984 aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
986 for ( short i=1; i>=0; i-- )
988 sc::Compare::Cell& rCell = aComp.maCells[i];
990 if (aMat[i].mpMat->IsString(j, k))
992 rCell.mbValue = false;
993 rCell.maStr = aMat[i].mpMat->GetString(j, k);
994 rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
996 else
998 rCell.mbValue = true;
999 rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
1000 rCell.mbEmpty = false;
1003 aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k);
1005 else
1006 aRes.mpMat->PutError( errNoValue, j, k);
1010 switch (eOp)
1012 case SC_EQUAL:
1013 aRes.mpMat->CompareEqual();
1014 break;
1015 case SC_LESS:
1016 aRes.mpMat->CompareLess();
1017 break;
1018 case SC_GREATER:
1019 aRes.mpMat->CompareGreater();
1020 break;
1021 case SC_LESS_EQUAL:
1022 aRes.mpMat->CompareLessEqual();
1023 break;
1024 case SC_GREATER_EQUAL:
1025 aRes.mpMat->CompareGreaterEqual();
1026 break;
1027 case SC_NOT_EQUAL:
1028 aRes.mpMat->CompareNotEqual();
1029 break;
1030 default:
1031 OSL_TRACE( "ScInterpreter::QueryMat: unhandled comparison operator: %d", (int)eOp);
1032 aRes.mpMat.reset();
1033 return aRes;
1036 else if (aMat[0].mpMat || aMat[1].mpMat)
1038 size_t i = ( aMat[0].mpMat ? 0 : 1);
1040 aRes.mnCol1 = aMat[i].mnCol1;
1041 aRes.mnRow1 = aMat[i].mnRow1;
1042 aRes.mnTab1 = aMat[i].mnTab1;
1043 aRes.mnCol2 = aMat[i].mnCol2;
1044 aRes.mnRow2 = aMat[i].mnRow2;
1045 aRes.mnTab2 = aMat[i].mnTab2;
1047 ScMatrix& rMat = *aMat[i].mpMat;
1048 aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
1049 if (!aRes.mpMat)
1050 return aRes;
1053 nCurFmtType = nFuncFmtType = css::util::NumberFormat::LOGICAL;
1054 return aRes;
1057 ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions )
1059 short nSaveCurFmtType = nCurFmtType;
1060 short nSaveFuncFmtType = nFuncFmtType;
1061 PushMatrix( pMat);
1062 const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
1063 if (rItem.meType == ScQueryEntry::ByString)
1064 PushString(rItem.maString.getString());
1065 else
1066 PushDouble(rItem.mfVal);
1067 ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
1068 nCurFmtType = nSaveCurFmtType;
1069 nFuncFmtType = nSaveFuncFmtType;
1070 if (nGlobalError || !pResultMatrix)
1072 SetError( errIllegalParameter);
1073 return pResultMatrix;
1076 return pResultMatrix;
1079 void ScInterpreter::ScEqual()
1081 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1083 sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
1084 if (!aMat.mpMat)
1086 PushIllegalParameter();
1087 return;
1090 PushMatrix(aMat);
1092 else
1093 PushInt( int(Compare( SC_EQUAL) == 0) );
1096 void ScInterpreter::ScNotEqual()
1098 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1100 sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
1101 if (!aMat.mpMat)
1103 PushIllegalParameter();
1104 return;
1107 PushMatrix(aMat);
1109 else
1110 PushInt( int(Compare( SC_NOT_EQUAL) != 0) );
1113 void ScInterpreter::ScLess()
1115 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1117 sc::RangeMatrix aMat = CompareMat(SC_LESS);
1118 if (!aMat.mpMat)
1120 PushIllegalParameter();
1121 return;
1124 PushMatrix(aMat);
1126 else
1127 PushInt( int(Compare( SC_LESS) < 0) );
1130 void ScInterpreter::ScGreater()
1132 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1134 sc::RangeMatrix aMat = CompareMat(SC_GREATER);
1135 if (!aMat.mpMat)
1137 PushIllegalParameter();
1138 return;
1141 PushMatrix(aMat);
1143 else
1144 PushInt( int(Compare( SC_GREATER) > 0) );
1147 void ScInterpreter::ScLessEqual()
1149 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1151 sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
1152 if (!aMat.mpMat)
1154 PushIllegalParameter();
1155 return;
1158 PushMatrix(aMat);
1160 else
1161 PushInt( int(Compare( SC_LESS_EQUAL) <= 0) );
1164 void ScInterpreter::ScGreaterEqual()
1166 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1168 sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
1169 if (!aMat.mpMat)
1171 PushIllegalParameter();
1172 return;
1175 PushMatrix(aMat);
1177 else
1178 PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) );
1181 void ScInterpreter::ScAnd()
1183 nFuncFmtType = css::util::NumberFormat::LOGICAL;
1184 short nParamCount = GetByte();
1185 if ( MustHaveParamCountMin( nParamCount, 1 ) )
1187 bool bHaveValue = false;
1188 bool nRes = true;
1189 size_t nRefInList = 0;
1190 while( nParamCount-- > 0)
1192 if ( !nGlobalError )
1194 switch ( GetStackType() )
1196 case svDouble :
1197 bHaveValue = true;
1198 nRes &= ( PopDouble() != 0.0 );
1199 break;
1200 case svString :
1201 Pop();
1202 SetError( errNoValue );
1203 break;
1204 case svSingleRef :
1206 ScAddress aAdr;
1207 PopSingleRef( aAdr );
1208 if ( !nGlobalError )
1210 ScRefCellValue aCell;
1211 aCell.assign(*pDok, aAdr);
1212 if (aCell.hasNumeric())
1214 bHaveValue = true;
1215 nRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
1217 // else: Xcl raises no error here
1220 break;
1221 case svDoubleRef:
1222 case svRefList:
1224 ScRange aRange;
1225 PopDoubleRef( aRange, nParamCount, nRefInList);
1226 if ( !nGlobalError )
1228 double fVal;
1229 sal_uInt16 nErr = 0;
1230 ScValueIterator aValIter( pDok, aRange );
1231 if ( aValIter.GetFirst( fVal, nErr ) && nErr == 0 )
1233 bHaveValue = true;
1236 nRes &= ( fVal != 0.0 );
1237 } while ( (nErr == 0) &&
1238 aValIter.GetNext( fVal, nErr ) );
1240 SetError( nErr );
1243 break;
1244 case svExternalSingleRef:
1245 case svExternalDoubleRef:
1246 case svMatrix:
1248 ScMatrixRef pMat = GetMatrix();
1249 if ( pMat )
1251 bHaveValue = true;
1252 double fVal = pMat->And();
1253 sal_uInt16 nErr = GetDoubleErrorValue( fVal );
1254 if ( nErr )
1256 SetError( nErr );
1257 nRes = false;
1259 else
1260 nRes &= (fVal != 0.0);
1262 // else: GetMatrix did set errIllegalParameter
1264 break;
1265 default:
1266 Pop();
1267 SetError( errIllegalParameter);
1270 else
1271 Pop();
1273 if ( bHaveValue )
1274 PushInt( int(nRes) );
1275 else
1276 PushNoValue();
1280 void ScInterpreter::ScOr()
1282 nFuncFmtType = css::util::NumberFormat::LOGICAL;
1283 short nParamCount = GetByte();
1284 if ( MustHaveParamCountMin( nParamCount, 1 ) )
1286 bool bHaveValue = false;
1287 bool nRes = false;
1288 size_t nRefInList = 0;
1289 while( nParamCount-- > 0)
1291 if ( !nGlobalError )
1293 switch ( GetStackType() )
1295 case svDouble :
1296 bHaveValue = true;
1297 nRes |= ( PopDouble() != 0.0 );
1298 break;
1299 case svString :
1300 Pop();
1301 SetError( errNoValue );
1302 break;
1303 case svSingleRef :
1305 ScAddress aAdr;
1306 PopSingleRef( aAdr );
1307 if ( !nGlobalError )
1309 ScRefCellValue aCell;
1310 aCell.assign(*pDok, aAdr);
1311 if (aCell.hasNumeric())
1313 bHaveValue = true;
1314 nRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
1316 // else: Xcl raises no error here
1319 break;
1320 case svDoubleRef:
1321 case svRefList:
1323 ScRange aRange;
1324 PopDoubleRef( aRange, nParamCount, nRefInList);
1325 if ( !nGlobalError )
1327 double fVal;
1328 sal_uInt16 nErr = 0;
1329 ScValueIterator aValIter( pDok, aRange );
1330 if ( aValIter.GetFirst( fVal, nErr ) )
1332 bHaveValue = true;
1335 nRes |= ( fVal != 0.0 );
1336 } while ( (nErr == 0) &&
1337 aValIter.GetNext( fVal, nErr ) );
1339 SetError( nErr );
1342 break;
1343 case svExternalSingleRef:
1344 case svExternalDoubleRef:
1345 case svMatrix:
1347 bHaveValue = true;
1348 ScMatrixRef pMat = GetMatrix();
1349 if ( pMat )
1351 bHaveValue = true;
1352 double fVal = pMat->Or();
1353 sal_uInt16 nErr = GetDoubleErrorValue( fVal );
1354 if ( nErr )
1356 SetError( nErr );
1357 nRes = false;
1359 else
1360 nRes |= (fVal != 0.0);
1362 // else: GetMatrix did set errIllegalParameter
1364 break;
1365 default:
1366 Pop();
1367 SetError( errIllegalParameter);
1370 else
1371 Pop();
1373 if ( bHaveValue )
1374 PushInt( int(nRes) );
1375 else
1376 PushNoValue();
1380 void ScInterpreter::ScXor()
1383 nFuncFmtType = css::util::NumberFormat::LOGICAL;
1384 short nParamCount = GetByte();
1385 if ( MustHaveParamCountMin( nParamCount, 1 ) )
1387 bool bHaveValue = false;
1388 bool nRes = false;
1389 size_t nRefInList = 0;
1390 while( nParamCount-- > 0)
1392 if ( !nGlobalError )
1394 switch ( GetStackType() )
1396 case svDouble :
1397 bHaveValue = true;
1398 nRes ^= ( PopDouble() != 0.0 );
1399 break;
1400 case svString :
1401 Pop();
1402 SetError( errNoValue );
1403 break;
1404 case svSingleRef :
1406 ScAddress aAdr;
1407 PopSingleRef( aAdr );
1408 if ( !nGlobalError )
1410 ScRefCellValue aCell;
1411 aCell.assign(*pDok, aAdr);
1412 if (aCell.hasNumeric())
1414 bHaveValue = true;
1415 nRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
1417 /* TODO: set error? Excel doesn't have XOR, but
1418 * doesn't set an error in this case for AND and
1419 * OR. */
1422 break;
1423 case svDoubleRef:
1424 case svRefList:
1426 ScRange aRange;
1427 PopDoubleRef( aRange, nParamCount, nRefInList);
1428 if ( !nGlobalError )
1430 double fVal;
1431 sal_uInt16 nErr = 0;
1432 ScValueIterator aValIter( pDok, aRange );
1433 if ( aValIter.GetFirst( fVal, nErr ) )
1435 bHaveValue = true;
1438 nRes ^= ( fVal != 0.0 );
1439 } while ( (nErr == 0) &&
1440 aValIter.GetNext( fVal, nErr ) );
1442 SetError( nErr );
1445 break;
1446 case svExternalSingleRef:
1447 case svExternalDoubleRef:
1448 case svMatrix:
1450 bHaveValue = true;
1451 ScMatrixRef pMat = GetMatrix();
1452 if ( pMat )
1454 bHaveValue = true;
1455 double fVal = pMat->Xor();
1456 sal_uInt16 nErr = GetDoubleErrorValue( fVal );
1457 if ( nErr )
1459 SetError( nErr );
1460 nRes = false;
1462 else
1463 nRes ^= ( fVal != 0.0 );
1465 // else: GetMatrix did set errIllegalParameter
1467 break;
1468 default:
1469 Pop();
1470 SetError( errIllegalParameter);
1473 else
1474 Pop();
1476 if ( bHaveValue )
1477 PushInt( int(nRes) );
1478 else
1479 PushNoValue();
1483 void ScInterpreter::ScNeg()
1485 // Simple negation doesn't change current format type to number, keep
1486 // current type.
1487 nFuncFmtType = nCurFmtType;
1488 switch ( GetStackType() )
1490 case svMatrix :
1492 ScMatrixRef pMat = GetMatrix();
1493 if ( !pMat )
1494 PushIllegalParameter();
1495 else
1497 SCSIZE nC, nR;
1498 pMat->GetDimensions( nC, nR );
1499 ScMatrixRef pResMat = GetNewMat( nC, nR);
1500 if ( !pResMat )
1501 PushIllegalArgument();
1502 else
1504 pMat->NegOp( *pResMat);
1505 PushMatrix( pResMat );
1509 break;
1510 default:
1511 PushDouble( -GetDouble() );
1515 void ScInterpreter::ScPercentSign()
1517 nFuncFmtType = css::util::NumberFormat::PERCENT;
1518 const FormulaToken* pSaveCur = pCur;
1519 sal_uInt8 nSavePar = cPar;
1520 PushInt( 100 );
1521 cPar = 2;
1522 FormulaByteToken aDivOp( ocDiv, cPar );
1523 pCur = &aDivOp;
1524 ScDiv();
1525 pCur = pSaveCur;
1526 cPar = nSavePar;
1529 void ScInterpreter::ScNot()
1531 nFuncFmtType = css::util::NumberFormat::LOGICAL;
1532 switch ( GetStackType() )
1534 case svMatrix :
1536 ScMatrixRef pMat = GetMatrix();
1537 if ( !pMat )
1538 PushIllegalParameter();
1539 else
1541 SCSIZE nC, nR;
1542 pMat->GetDimensions( nC, nR );
1543 ScMatrixRef pResMat = GetNewMat( nC, nR);
1544 if ( !pResMat )
1545 PushIllegalArgument();
1546 else
1548 pMat->NotOp( *pResMat);
1549 PushMatrix( pResMat );
1553 break;
1554 default:
1555 PushInt( int(GetDouble() == 0.0) );
1559 void ScInterpreter::ScBitAnd()
1562 if ( !MustHaveParamCount( GetByte(), 2 ) )
1563 return;
1565 double num1 = ::rtl::math::approxFloor( GetDouble());
1566 double num2 = ::rtl::math::approxFloor( GetDouble());
1567 if ( (num1 >= n2power48) || (num1 < 0) ||
1568 (num2 >= n2power48) || (num2 < 0))
1569 PushIllegalArgument();
1570 else
1571 PushDouble ((sal_uInt64) num1 & (sal_uInt64) num2);
1574 void ScInterpreter::ScBitOr()
1577 if ( !MustHaveParamCount( GetByte(), 2 ) )
1578 return;
1580 double num1 = ::rtl::math::approxFloor( GetDouble());
1581 double num2 = ::rtl::math::approxFloor( GetDouble());
1582 if ( (num1 >= n2power48) || (num1 < 0) ||
1583 (num2 >= n2power48) || (num2 < 0))
1584 PushIllegalArgument();
1585 else
1586 PushDouble ((sal_uInt64) num1 | (sal_uInt64) num2);
1589 void ScInterpreter::ScBitXor()
1592 if ( !MustHaveParamCount( GetByte(), 2 ) )
1593 return;
1595 double num1 = ::rtl::math::approxFloor( GetDouble());
1596 double num2 = ::rtl::math::approxFloor( GetDouble());
1597 if ( (num1 >= n2power48) || (num1 < 0) ||
1598 (num2 >= n2power48) || (num2 < 0))
1599 PushIllegalArgument();
1600 else
1601 PushDouble ((sal_uInt64) num1 ^ (sal_uInt64) num2);
1604 void ScInterpreter::ScBitLshift()
1607 if ( !MustHaveParamCount( GetByte(), 2 ) )
1608 return;
1610 double fShift = ::rtl::math::approxFloor( GetDouble());
1611 double num = ::rtl::math::approxFloor( GetDouble());
1612 if ((num >= n2power48) || (num < 0))
1613 PushIllegalArgument();
1614 else
1616 double fRes;
1617 if (fShift < 0)
1618 fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
1619 else if (fShift == 0)
1620 fRes = num;
1621 else
1622 fRes = num * pow( 2.0, fShift);
1623 PushDouble( fRes);
1627 void ScInterpreter::ScBitRshift()
1630 if ( !MustHaveParamCount( GetByte(), 2 ) )
1631 return;
1633 double fShift = ::rtl::math::approxFloor( GetDouble());
1634 double num = ::rtl::math::approxFloor( GetDouble());
1635 if ((num >= n2power48) || (num < 0))
1636 PushIllegalArgument();
1637 else
1639 double fRes;
1640 if (fShift < 0)
1641 fRes = num * pow( 2.0, -fShift);
1642 else if (fShift == 0)
1643 fRes = num;
1644 else
1645 fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
1646 PushDouble( fRes);
1650 void ScInterpreter::ScPi()
1652 PushDouble(F_PI);
1655 void ScInterpreter::ScRandom()
1657 PushDouble(formula::rng::fRandom(0, 1));
1660 void ScInterpreter::ScTrue()
1662 nFuncFmtType = css::util::NumberFormat::LOGICAL;
1663 PushInt(1);
1666 void ScInterpreter::ScFalse()
1668 nFuncFmtType = css::util::NumberFormat::LOGICAL;
1669 PushInt(0);
1672 void ScInterpreter::ScDeg()
1674 PushDouble((GetDouble() / F_PI) * 180.0);
1677 void ScInterpreter::ScRad()
1679 PushDouble(GetDouble() * (F_PI / 180));
1682 void ScInterpreter::ScSin()
1684 PushDouble(::rtl::math::sin(GetDouble()));
1687 void ScInterpreter::ScCos()
1689 PushDouble(::rtl::math::cos(GetDouble()));
1692 void ScInterpreter::ScTan()
1694 PushDouble(::rtl::math::tan(GetDouble()));
1697 void ScInterpreter::ScCot()
1699 PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1702 void ScInterpreter::ScArcSin()
1704 PushDouble(asin(GetDouble()));
1707 void ScInterpreter::ScArcCos()
1709 PushDouble(acos(GetDouble()));
1712 void ScInterpreter::ScArcTan()
1714 PushDouble(atan(GetDouble()));
1717 void ScInterpreter::ScArcCot()
1719 PushDouble((F_PI2) - atan(GetDouble()));
1722 void ScInterpreter::ScSinHyp()
1724 PushDouble(sinh(GetDouble()));
1727 void ScInterpreter::ScCosHyp()
1729 PushDouble(cosh(GetDouble()));
1732 void ScInterpreter::ScTanHyp()
1734 PushDouble(tanh(GetDouble()));
1737 void ScInterpreter::ScCotHyp()
1739 PushDouble(1.0 / tanh(GetDouble()));
1742 void ScInterpreter::ScArcSinHyp()
1744 PushDouble( ::rtl::math::asinh( GetDouble()));
1747 void ScInterpreter::ScArcCosHyp()
1749 double fVal = GetDouble();
1750 if (fVal < 1.0)
1751 PushIllegalArgument();
1752 else
1753 PushDouble( ::rtl::math::acosh( fVal));
1756 void ScInterpreter::ScArcTanHyp()
1758 double fVal = GetDouble();
1759 if (fabs(fVal) >= 1.0)
1760 PushIllegalArgument();
1761 else
1762 PushDouble( ::rtl::math::atanh( fVal));
1765 void ScInterpreter::ScArcCotHyp()
1767 double nVal = GetDouble();
1768 if (fabs(nVal) <= 1.0)
1769 PushIllegalArgument();
1770 else
1771 PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
1774 void ScInterpreter::ScCosecant()
1776 PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
1779 void ScInterpreter::ScSecant()
1781 PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
1784 void ScInterpreter::ScCosecantHyp()
1786 PushDouble(1.0 / sinh(GetDouble()));
1789 void ScInterpreter::ScSecantHyp()
1791 PushDouble(1.0 / cosh(GetDouble()));
1794 void ScInterpreter::ScExp()
1796 PushDouble(exp(GetDouble()));
1799 void ScInterpreter::ScSqrt()
1801 double fVal = GetDouble();
1802 if (fVal >= 0.0)
1803 PushDouble(sqrt(fVal));
1804 else
1805 PushIllegalArgument();
1808 void ScInterpreter::ScIsEmpty()
1810 short nRes = 0;
1811 nFuncFmtType = css::util::NumberFormat::LOGICAL;
1812 switch ( GetRawStackType() )
1814 case svEmptyCell:
1816 FormulaTokenRef p = PopToken();
1817 if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
1818 nRes = 1;
1820 break;
1821 case svDoubleRef :
1822 case svSingleRef :
1824 ScAddress aAdr;
1825 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1826 break;
1827 // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that
1828 // may treat ="" in the referenced cell as blank for Excel
1829 // interoperability.
1830 ScRefCellValue aCell;
1831 aCell.assign(*pDok, aAdr);
1832 if (aCell.meType == CELLTYPE_NONE)
1833 nRes = 1;
1835 break;
1836 case svExternalSingleRef:
1837 case svExternalDoubleRef:
1838 case svMatrix:
1840 ScMatrixRef pMat = GetMatrix();
1841 if ( !pMat )
1842 ; // nothing
1843 else if ( !pJumpMatrix )
1844 nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0;
1845 else
1847 SCSIZE nCols, nRows, nC, nR;
1848 pMat->GetDimensions( nCols, nRows);
1849 pJumpMatrix->GetPos( nC, nR);
1850 if ( nC < nCols && nR < nRows )
1851 nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0;
1852 // else: false, not empty (which is what Xcl does)
1855 break;
1856 default:
1857 Pop();
1859 nGlobalError = 0;
1860 PushInt( nRes );
1863 bool ScInterpreter::IsString()
1865 nFuncFmtType = css::util::NumberFormat::LOGICAL;
1866 bool nRes = false;
1867 switch ( GetRawStackType() )
1869 case svString:
1870 Pop();
1871 nRes = true;
1872 break;
1873 case svDoubleRef :
1874 case svSingleRef :
1876 ScAddress aAdr;
1877 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1878 break;
1880 ScRefCellValue aCell;
1881 aCell.assign(*pDok, aAdr);
1882 if (GetCellErrCode(aCell) == 0)
1884 switch (aCell.meType)
1886 case CELLTYPE_STRING :
1887 case CELLTYPE_EDIT :
1888 nRes = true;
1889 break;
1890 case CELLTYPE_FORMULA :
1891 nRes = (!aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
1892 break;
1893 default:
1894 ; // nothing
1898 break;
1899 case svMatrix:
1901 ScMatrixRef pMat = PopMatrix();
1902 if ( !pMat )
1903 ; // nothing
1904 else if ( !pJumpMatrix )
1905 nRes = pMat->IsString(0, 0) && !pMat->IsEmpty(0, 0);
1906 else
1908 SCSIZE nCols, nRows, nC, nR;
1909 pMat->GetDimensions( nCols, nRows);
1910 pJumpMatrix->GetPos( nC, nR);
1911 if ( nC < nCols && nR < nRows )
1912 nRes = pMat->IsString( nC, nR) && !pMat->IsEmpty( nC, nR);
1915 break;
1916 default:
1917 Pop();
1919 nGlobalError = 0;
1920 return nRes;
1923 void ScInterpreter::ScIsString()
1925 PushInt( int(IsString()) );
1928 void ScInterpreter::ScIsNonString()
1930 PushInt( int(!IsString()) );
1933 void ScInterpreter::ScIsLogical()
1935 bool nRes = false;
1936 switch ( GetStackType() )
1938 case svDoubleRef :
1939 case svSingleRef :
1941 ScAddress aAdr;
1942 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1943 break;
1945 ScRefCellValue aCell;
1946 aCell.assign(*pDok, aAdr);
1947 if (GetCellErrCode(aCell) == 0)
1949 if (aCell.hasNumeric())
1951 sal_uLong nFormat = GetCellNumberFormat(aAdr, aCell);
1952 nRes = (pFormatter->GetType(nFormat) == css::util::NumberFormat::LOGICAL);
1956 break;
1957 case svMatrix:
1958 // TODO: we don't have type information for arrays except
1959 // numerical/string.
1960 // Fall through
1961 default:
1962 PopError();
1963 if ( !nGlobalError )
1964 nRes = ( nCurFmtType == css::util::NumberFormat::LOGICAL );
1966 nCurFmtType = nFuncFmtType = css::util::NumberFormat::LOGICAL;
1967 nGlobalError = 0;
1968 PushInt( int(nRes) );
1971 void ScInterpreter::ScType()
1973 short nType = 0;
1974 switch ( GetStackType() )
1976 case svDoubleRef :
1977 case svSingleRef :
1979 ScAddress aAdr;
1980 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1981 break;
1983 ScRefCellValue aCell;
1984 aCell.assign(*pDok, aAdr);
1985 if (GetCellErrCode(aCell) == 0)
1987 switch (aCell.meType)
1989 // NOTE: this is Xcl nonsense!
1990 case CELLTYPE_STRING :
1991 case CELLTYPE_EDIT :
1992 nType = 2;
1993 break;
1994 case CELLTYPE_VALUE :
1996 sal_uLong nFormat = GetCellNumberFormat(aAdr, aCell);
1997 if (pFormatter->GetType(nFormat) == css::util::NumberFormat::LOGICAL)
1998 nType = 4;
1999 else
2000 nType = 1;
2002 break;
2003 case CELLTYPE_FORMULA :
2004 nType = 8;
2005 break;
2006 default:
2007 PushIllegalArgument();
2010 else
2011 nType = 16;
2013 break;
2014 case svString:
2015 PopError();
2016 if ( nGlobalError )
2018 nType = 16;
2019 nGlobalError = 0;
2021 else
2022 nType = 2;
2023 break;
2024 case svMatrix:
2025 PopMatrix();
2026 if ( nGlobalError )
2028 nType = 16;
2029 nGlobalError = 0;
2031 else
2032 nType = 64;
2033 // we could return the type of one element if in JumpMatrix or
2034 // ForceArray mode, but Xcl doesn't ...
2035 break;
2036 default:
2037 PopError();
2038 if ( nGlobalError )
2040 nType = 16;
2041 nGlobalError = 0;
2043 else
2044 nType = 1;
2046 PushInt( nType );
2049 static inline bool lcl_FormatHasNegColor( const SvNumberformat* pFormat )
2051 return pFormat && pFormat->GetColor( 1 );
2054 static inline bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
2056 return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1);
2059 namespace {
2061 void getFormatString(SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr)
2063 bool bAppendPrec = true;
2064 sal_uInt16 nPrec, nLeading;
2065 bool bThousand, bIsRed;
2066 pFormatter->GetFormatSpecialInfo( nFormat, bThousand, bIsRed, nPrec, nLeading );
2068 switch( pFormatter->GetType( nFormat ) )
2070 case css::util::NumberFormat::NUMBER: if(bThousand) rFmtStr = ","; else rFmtStr = "F"; break;
2071 case css::util::NumberFormat::CURRENCY: rFmtStr = "C"; break;
2072 case css::util::NumberFormat::SCIENTIFIC: rFmtStr = "S"; break;
2073 case css::util::NumberFormat::PERCENT: rFmtStr = "P"; break;
2074 default:
2076 bAppendPrec = false;
2077 switch( pFormatter->GetIndexTableOffset( nFormat ) )
2079 case NF_DATE_SYSTEM_SHORT:
2080 case NF_DATE_SYS_DMMMYY:
2081 case NF_DATE_SYS_DDMMYY:
2082 case NF_DATE_SYS_DDMMYYYY:
2083 case NF_DATE_SYS_DMMMYYYY:
2084 case NF_DATE_DIN_DMMMYYYY:
2085 case NF_DATE_SYS_DMMMMYYYY:
2086 case NF_DATE_DIN_DMMMMYYYY: rFmtStr = "D1"; break;
2087 case NF_DATE_SYS_DDMMM: rFmtStr = "D2"; break;
2088 case NF_DATE_SYS_MMYY: rFmtStr = "D3"; break;
2089 case NF_DATETIME_SYSTEM_SHORT_HHMM:
2090 case NF_DATETIME_SYS_DDMMYYYY_HHMMSS:
2091 rFmtStr = "D4"; break;
2092 case NF_DATE_DIN_MMDD: rFmtStr = "D5"; break;
2093 case NF_TIME_HHMMSSAMPM: rFmtStr = "D6"; break;
2094 case NF_TIME_HHMMAMPM: rFmtStr = "D7"; break;
2095 case NF_TIME_HHMMSS: rFmtStr = "D8"; break;
2096 case NF_TIME_HHMM: rFmtStr = "D9"; break;
2097 default: rFmtStr = "G";
2101 if( bAppendPrec )
2102 rFmtStr += OUString::number(nPrec);
2103 const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat );
2104 if( lcl_FormatHasNegColor( pFormat ) )
2105 rFmtStr += "-";
2106 if( lcl_FormatHasOpenPar( pFormat ) )
2107 rFmtStr += "()";
2112 void ScInterpreter::ScCell()
2113 { // ATTRIBUTE ; [REF]
2114 sal_uInt8 nParamCount = GetByte();
2115 if( MustHaveParamCount( nParamCount, 1, 2 ) )
2117 ScAddress aCellPos( aPos );
2118 bool bError = false;
2119 if( nParamCount == 2 )
2121 switch (GetStackType())
2123 case svExternalSingleRef:
2124 case svExternalDoubleRef:
2126 // Let's handle external reference separately...
2127 ScCellExternal();
2128 return;
2130 default:
2133 bError = !PopDoubleRefOrSingleRef( aCellPos );
2135 OUString aInfoType = GetString().getString();
2136 if( bError || nGlobalError )
2137 PushIllegalParameter();
2138 else
2140 ScRefCellValue aCell;
2141 aCell.assign(*pDok, aCellPos);
2143 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell);
2145 // *** ADDRESS INFO ***
2146 if( aInfoType == "COL" )
2147 { // column number (1-based)
2148 PushInt( aCellPos.Col() + 1 );
2150 else if( aInfoType == "ROW" )
2151 { // row number (1-based)
2152 PushInt( aCellPos.Row() + 1 );
2154 else if( aInfoType == "SHEET" )
2155 { // table number (1-based)
2156 PushInt( aCellPos.Tab() + 1 );
2158 else if( aInfoType == "ADDRESS" )
2159 { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
2160 sal_uInt16 nFlags = (aCellPos.Tab() == aPos.Tab()) ? (SCA_ABS) : (SCA_ABS_3D);
2161 OUString aStr(aCellPos.Format(nFlags, pDok, pDok->GetAddressConvention()));
2162 PushString(aStr);
2164 else if( aInfoType == "FILENAME" )
2165 { // file name and table name: 'FILENAME'#$TABLE
2166 SCTAB nTab = aCellPos.Tab();
2167 OUString aFuncResult;
2168 if( nTab < pDok->GetTableCount() )
2170 if( pDok->GetLinkMode( nTab ) == SC_LINK_VALUE )
2171 pDok->GetName( nTab, aFuncResult );
2172 else
2174 SfxObjectShell* pShell = pDok->GetDocumentShell();
2175 if( pShell && pShell->GetMedium() )
2177 OUStringBuffer aBuf;
2178 aBuf.append('\'');
2179 const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
2180 aBuf.append(rURLObj.GetMainURL(INetURLObject::DECODE_UNAMBIGUOUS));
2181 aBuf.appendAscii("'#$");
2182 OUString aTabName;
2183 pDok->GetName( nTab, aTabName );
2184 aBuf.append(aTabName);
2185 aFuncResult = aBuf.makeStringAndClear();
2189 PushString( aFuncResult );
2191 else if( aInfoType == "COORD" )
2192 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2193 // Yes, passing tab as col is intentional!
2194 OUStringBuffer aFuncResult;
2195 OUString aCellStr =
2196 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
2197 (SCA_COL_ABSOLUTE|SCA_VALID_COL), NULL, pDok->GetAddressConvention() );
2198 aFuncResult.append(aCellStr);
2199 aFuncResult.append(':');
2200 aCellStr = aCellPos.Format((SCA_COL_ABSOLUTE|SCA_VALID_COL|SCA_ROW_ABSOLUTE|SCA_VALID_ROW),
2201 NULL, pDok->GetAddressConvention());
2202 aFuncResult.append(aCellStr);
2203 PushString( aFuncResult.makeStringAndClear() );
2206 // *** CELL PROPERTIES ***
2207 else if( aInfoType == "CONTENTS" )
2208 { // contents of the cell, no formatting
2209 if (aCell.hasString())
2211 svl::SharedString aStr;
2212 GetCellString(aStr, aCell);
2213 PushString( aStr );
2215 else
2216 PushDouble(GetCellValue(aCellPos, aCell));
2218 else if( aInfoType == "TYPE" )
2219 { // b = blank; l = string (label); v = otherwise (value)
2220 sal_Unicode c;
2221 if (aCell.hasString())
2222 c = 'l';
2223 else
2224 c = aCell.hasNumeric() ? 'v' : 'b';
2225 PushString( OUString(c) );
2227 else if( aInfoType == "WIDTH" )
2228 { // column width (rounded off as count of zero characters in standard font and size)
2229 Printer* pPrinter = pDok->GetPrinter();
2230 MapMode aOldMode( pPrinter->GetMapMode() );
2231 vcl::Font aOldFont( pPrinter->GetFont() );
2232 vcl::Font aDefFont;
2234 pPrinter->SetMapMode( MAP_TWIP );
2235 // font color doesn't matter here
2236 pDok->GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter );
2237 pPrinter->SetFont( aDefFont );
2238 long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
2239 pPrinter->SetFont( aOldFont );
2240 pPrinter->SetMapMode( aOldMode );
2241 int nZeroCount = (int)(pDok->GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
2242 PushInt( nZeroCount );
2244 else if( aInfoType == "PREFIX" )
2245 { // ' = left; " = right; ^ = centered
2246 sal_Unicode c = 0;
2247 if (aCell.hasString())
2249 const SvxHorJustifyItem* pJustAttr = static_cast<const SvxHorJustifyItem*>(
2250 pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_HOR_JUSTIFY ));
2251 switch( pJustAttr->GetValue() )
2253 case SVX_HOR_JUSTIFY_STANDARD:
2254 case SVX_HOR_JUSTIFY_LEFT:
2255 case SVX_HOR_JUSTIFY_BLOCK: c = '\''; break;
2256 case SVX_HOR_JUSTIFY_CENTER: c = '^'; break;
2257 case SVX_HOR_JUSTIFY_RIGHT: c = '"'; break;
2258 case SVX_HOR_JUSTIFY_REPEAT: c = '\\'; break;
2261 PushString( OUString(c) );
2263 else if( aInfoType == "PROTECT" )
2264 { // 1 = cell locked
2265 const ScProtectionAttr* pProtAttr = static_cast<const ScProtectionAttr*>(
2266 pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_PROTECTION ));
2267 PushInt( pProtAttr->GetProtection() ? 1 : 0 );
2270 // *** FORMATTING ***
2271 else if( aInfoType == "FORMAT" )
2272 { // specific format code for standard formats
2273 OUString aFuncResult;
2274 sal_uLong nFormat = pDok->GetNumberFormat( aCellPos );
2275 getFormatString(pFormatter, nFormat, aFuncResult);
2276 PushString( aFuncResult );
2278 else if( aInfoType == "COLOR" )
2279 { // 1 = negative values are colored, otherwise 0
2280 const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) );
2281 PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2283 else if( aInfoType == "PARENTHESES" )
2284 { // 1 = format string contains a '(' character, otherwise 0
2285 const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) );
2286 PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2288 else
2289 PushIllegalArgument();
2294 void ScInterpreter::ScCellExternal()
2296 sal_uInt16 nFileId;
2297 OUString aTabName;
2298 ScSingleRefData aRef;
2299 ScExternalRefCache::TokenRef pToken;
2300 ScExternalRefCache::CellFormat aFmt;
2301 PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
2302 if (nGlobalError)
2304 PushIllegalParameter();
2305 return;
2308 OUString aInfoType = GetString().getString();
2309 if (nGlobalError)
2311 PushIllegalParameter();
2312 return;
2315 SCCOL nCol;
2316 SCROW nRow;
2317 SCTAB nTab;
2318 aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2319 SingleRefToVars(aRef, nCol, nRow, nTab);
2320 if (nGlobalError)
2322 PushIllegalParameter();
2323 return;
2325 aRef.SetAbsTab(-1); // revert the value.
2327 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell);
2328 ScExternalRefManager* pRefMgr = pDok->GetExternalRefManager();
2330 if ( aInfoType == "COL" )
2331 PushInt(nCol + 1);
2332 else if ( aInfoType == "ROW" )
2333 PushInt(nRow + 1);
2334 else if ( aInfoType == "SHEET" )
2336 // For SHEET, No idea what number we should set, but let's always set
2337 // 1 if the external sheet exists, no matter what sheet. Excel does
2338 // the same.
2339 if (pRefMgr->getCacheTable(nFileId, aTabName, false, NULL).get())
2340 PushInt(1);
2341 else
2342 SetError(errNoName);
2344 else if ( aInfoType == "ADDRESS" )
2346 // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2347 ScTokenArray aArray;
2348 aArray.AddExternalSingleReference(nFileId, aTabName, aRef);
2349 ScCompiler aComp(pDok, aPos, aArray);
2350 aComp.SetGrammar(formula::FormulaGrammar::GRAM_ODFF_A1);
2351 OUString aStr;
2352 aComp.CreateStringFromTokenArray(aStr);
2353 PushString(aStr);
2355 else if ( aInfoType == "FILENAME" )
2357 // 'file URI'#$SheetName
2359 const OUString* p = pRefMgr->getExternalFileName(nFileId);
2360 if (!p)
2362 // In theory this should never happen...
2363 SetError(errNoName);
2364 return;
2367 OUStringBuffer aBuf;
2368 aBuf.append('\'');
2369 aBuf.append(*p);
2370 aBuf.appendAscii("'#$");
2371 aBuf.append(aTabName);
2372 PushString(aBuf.makeStringAndClear());
2374 else if ( aInfoType == "CONTENTS" )
2376 switch (pToken->GetType())
2378 case svString:
2379 PushString(pToken->GetString());
2380 break;
2381 case svDouble:
2382 PushString(OUString::number(pToken->GetDouble()));
2383 break;
2384 case svError:
2385 PushString(ScGlobal::GetErrorString(pToken->GetError()));
2386 break;
2387 default:
2388 PushString(ScGlobal::GetEmptyOUString());
2391 else if ( aInfoType == "TYPE" )
2393 sal_Unicode c = 'v';
2394 switch (pToken->GetType())
2396 case svString:
2397 c = 'l';
2398 break;
2399 case svEmptyCell:
2400 c = 'b';
2401 break;
2402 default:
2405 PushString(OUString(c));
2407 else if ( aInfoType == "FORMAT" )
2409 OUString aFmtStr;
2410 sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
2411 getFormatString(pFormatter, nFmt, aFmtStr);
2412 PushString(aFmtStr);
2414 else if ( aInfoType == "COLOR" )
2416 // 1 = negative values are colored, otherwise 0
2417 int nVal = 0;
2418 if (aFmt.mbIsSet)
2420 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2421 nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
2423 PushInt(nVal);
2425 else if ( aInfoType == "PARENTHESES" )
2427 // 1 = format string contains a '(' character, otherwise 0
2428 int nVal = 0;
2429 if (aFmt.mbIsSet)
2431 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2432 nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
2434 PushInt(nVal);
2436 else
2437 PushIllegalParameter();
2440 void ScInterpreter::ScIsRef()
2442 nFuncFmtType = css::util::NumberFormat::LOGICAL;
2443 bool nRes = false;
2444 switch ( GetStackType() )
2446 case svSingleRef :
2448 ScAddress aAdr;
2449 PopSingleRef( aAdr );
2450 if ( !nGlobalError )
2451 nRes = true;
2453 break;
2454 case svDoubleRef :
2456 ScRange aRange;
2457 PopDoubleRef( aRange );
2458 if ( !nGlobalError )
2459 nRes = true;
2461 break;
2462 case svRefList :
2464 FormulaTokenRef x = PopToken();
2465 if ( !nGlobalError )
2466 nRes = !x.get()->GetRefList()->empty();
2468 break;
2469 default:
2470 Pop();
2472 nGlobalError = 0;
2473 PushInt( int(nRes) );
2476 void ScInterpreter::ScIsValue()
2478 nFuncFmtType = css::util::NumberFormat::LOGICAL;
2479 bool nRes = false;
2480 switch ( GetRawStackType() )
2482 case svDouble:
2483 Pop();
2484 nRes = true;
2485 break;
2486 case svDoubleRef :
2487 case svSingleRef :
2489 ScAddress aAdr;
2490 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2491 break;
2493 ScRefCellValue aCell;
2494 aCell.assign(*pDok, aAdr);
2495 if (GetCellErrCode(aCell) == 0)
2497 switch (aCell.meType)
2499 case CELLTYPE_VALUE :
2500 nRes = true;
2501 break;
2502 case CELLTYPE_FORMULA :
2503 nRes = (aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
2504 break;
2505 default:
2506 ; // nothing
2510 break;
2511 case svMatrix:
2513 ScMatrixRef pMat = PopMatrix();
2514 if ( !pMat )
2515 ; // nothing
2516 else if ( !pJumpMatrix )
2518 if (pMat->GetErrorIfNotString( 0, 0) == 0)
2519 nRes = pMat->IsValue( 0, 0);
2521 else
2523 SCSIZE nCols, nRows, nC, nR;
2524 pMat->GetDimensions( nCols, nRows);
2525 pJumpMatrix->GetPos( nC, nR);
2526 if ( nC < nCols && nR < nRows )
2527 if (pMat->GetErrorIfNotString( nC, nR) == 0)
2528 nRes = pMat->IsValue( nC, nR);
2531 break;
2532 default:
2533 Pop();
2535 nGlobalError = 0;
2536 PushInt( int(nRes) );
2539 void ScInterpreter::ScIsFormula()
2541 nFuncFmtType = css::util::NumberFormat::LOGICAL;
2542 bool nRes = false;
2543 switch ( GetStackType() )
2545 case svDoubleRef :
2546 case svSingleRef :
2548 ScAddress aAdr;
2549 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2550 break;
2552 nRes = (pDok->GetCellType(aAdr) == CELLTYPE_FORMULA);
2554 break;
2555 default:
2556 Pop();
2558 nGlobalError = 0;
2559 PushInt( int(nRes) );
2562 void ScInterpreter::ScFormula()
2564 OUString aFormula;
2565 switch ( GetStackType() )
2567 case svDoubleRef :
2568 case svSingleRef :
2570 ScAddress aAdr;
2571 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2572 break;
2574 ScRefCellValue aCell;
2575 aCell.assign(*pDok, aAdr);
2576 switch (aCell.meType)
2578 case CELLTYPE_FORMULA :
2579 aCell.mpFormula->GetFormula(aFormula);
2580 break;
2581 default:
2582 SetError( NOTAVAILABLE );
2585 break;
2586 default:
2587 Pop();
2588 SetError( NOTAVAILABLE );
2590 PushString( aFormula );
2593 void ScInterpreter::ScIsNV()
2595 nFuncFmtType = css::util::NumberFormat::LOGICAL;
2596 bool nRes = false;
2597 switch ( GetStackType() )
2599 case svDoubleRef :
2600 case svSingleRef :
2602 ScAddress aAdr;
2603 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2604 if ( nGlobalError == NOTAVAILABLE )
2605 nRes = true;
2606 else if (bOk)
2608 ScRefCellValue aCell;
2609 aCell.assign(*pDok, aAdr);
2610 sal_uInt16 nErr = GetCellErrCode(aCell);
2611 nRes = (nErr == NOTAVAILABLE);
2614 break;
2615 case svMatrix:
2617 ScMatrixRef pMat = PopMatrix();
2618 if ( !pMat )
2619 ; // nothing
2620 else if ( !pJumpMatrix )
2621 nRes = (pMat->GetErrorIfNotString( 0, 0) == NOTAVAILABLE);
2622 else
2624 SCSIZE nCols, nRows, nC, nR;
2625 pMat->GetDimensions( nCols, nRows);
2626 pJumpMatrix->GetPos( nC, nR);
2627 if ( nC < nCols && nR < nRows )
2628 nRes = (pMat->GetErrorIfNotString( nC, nR) == NOTAVAILABLE);
2631 break;
2632 default:
2633 PopError();
2634 if ( nGlobalError == NOTAVAILABLE )
2635 nRes = true;
2637 nGlobalError = 0;
2638 PushInt( int(nRes) );
2641 void ScInterpreter::ScIsErr()
2643 nFuncFmtType = css::util::NumberFormat::LOGICAL;
2644 bool nRes = false;
2645 switch ( GetStackType() )
2647 case svDoubleRef :
2648 case svSingleRef :
2650 ScAddress aAdr;
2651 bool bOk = PopDoubleRefOrSingleRef( aAdr );
2652 if ( !bOk || (nGlobalError && nGlobalError != NOTAVAILABLE) )
2653 nRes = true;
2654 else
2656 ScRefCellValue aCell;
2657 aCell.assign(*pDok, aAdr);
2658 sal_uInt16 nErr = GetCellErrCode(aCell);
2659 nRes = (nErr && nErr != NOTAVAILABLE);
2662 break;
2663 case svMatrix:
2665 ScMatrixRef pMat = PopMatrix();
2666 if ( nGlobalError || !pMat )
2667 nRes = ((nGlobalError && nGlobalError != NOTAVAILABLE) || !pMat);
2668 else if ( !pJumpMatrix )
2670 sal_uInt16 nErr = pMat->GetErrorIfNotString( 0, 0);
2671 nRes = (nErr && nErr != NOTAVAILABLE);
2673 else
2675 SCSIZE nCols, nRows, nC, nR;
2676 pMat->GetDimensions( nCols, nRows);
2677 pJumpMatrix->GetPos( nC, nR);
2678 if ( nC < nCols && nR < nRows )
2680 sal_uInt16 nErr = pMat->GetErrorIfNotString( nC, nR);
2681 nRes = (nErr && nErr != NOTAVAILABLE);
2685 break;
2686 default:
2687 PopError();
2688 if ( nGlobalError && nGlobalError != NOTAVAILABLE )
2689 nRes = true;
2691 nGlobalError = 0;
2692 PushInt( int(nRes) );
2695 void ScInterpreter::ScIsError()
2697 nFuncFmtType = css::util::NumberFormat::LOGICAL;
2698 bool nRes = false;
2699 switch ( GetStackType() )
2701 case svDoubleRef :
2702 case svSingleRef :
2704 ScAddress aAdr;
2705 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2707 nRes = true;
2708 break;
2710 if ( nGlobalError )
2711 nRes = true;
2712 else
2714 ScRefCellValue aCell;
2715 aCell.assign(*pDok, aAdr);
2716 nRes = (GetCellErrCode(aCell) != 0);
2719 break;
2720 case svMatrix:
2722 ScMatrixRef pMat = PopMatrix();
2723 if ( nGlobalError || !pMat )
2724 nRes = true;
2725 else if ( !pJumpMatrix )
2726 nRes = (pMat->GetErrorIfNotString( 0, 0) != 0);
2727 else
2729 SCSIZE nCols, nRows, nC, nR;
2730 pMat->GetDimensions( nCols, nRows);
2731 pJumpMatrix->GetPos( nC, nR);
2732 if ( nC < nCols && nR < nRows )
2733 nRes = (pMat->GetErrorIfNotString( nC, nR) != 0);
2736 break;
2737 default:
2738 PopError();
2739 if ( nGlobalError )
2740 nRes = true;
2742 nGlobalError = 0;
2743 PushInt( int(nRes) );
2746 bool ScInterpreter::IsEven()
2748 nFuncFmtType = css::util::NumberFormat::LOGICAL;
2749 bool nRes = false;
2750 double fVal = 0.0;
2751 switch ( GetStackType() )
2753 case svDoubleRef :
2754 case svSingleRef :
2756 ScAddress aAdr;
2757 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2758 break;
2760 ScRefCellValue aCell;
2761 aCell.assign(*pDok, aAdr);
2762 sal_uInt16 nErr = GetCellErrCode(aCell);
2763 if (nErr != 0)
2764 SetError(nErr);
2765 else
2767 switch (aCell.meType)
2769 case CELLTYPE_VALUE :
2770 fVal = GetCellValue(aAdr, aCell);
2771 nRes = true;
2772 break;
2773 case CELLTYPE_FORMULA :
2774 if (aCell.mpFormula->IsValue())
2776 fVal = GetCellValue(aAdr, aCell);
2777 nRes = true;
2779 break;
2780 default:
2781 ; // nothing
2785 break;
2786 case svDouble:
2788 fVal = PopDouble();
2789 nRes = true;
2791 break;
2792 case svMatrix:
2794 ScMatrixRef pMat = PopMatrix();
2795 if ( !pMat )
2796 ; // nothing
2797 else if ( !pJumpMatrix )
2799 nRes = pMat->IsValue( 0, 0);
2800 if ( nRes )
2801 fVal = pMat->GetDouble( 0, 0);
2803 else
2805 SCSIZE nCols, nRows, nC, nR;
2806 pMat->GetDimensions( nCols, nRows);
2807 pJumpMatrix->GetPos( nC, nR);
2808 if ( nC < nCols && nR < nRows )
2810 nRes = pMat->IsValue( nC, nR);
2811 if ( nRes )
2812 fVal = pMat->GetDouble( nC, nR);
2814 else
2815 SetError( errNoValue);
2818 break;
2819 default:
2820 ; // nothing
2822 if ( !nRes )
2823 SetError( errIllegalParameter);
2824 else
2825 nRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
2826 return nRes;
2829 void ScInterpreter::ScIsEven()
2831 PushInt( int(IsEven()) );
2834 void ScInterpreter::ScIsOdd()
2836 PushInt( int(!IsEven()) );
2839 void ScInterpreter::ScN()
2841 sal_uInt16 nErr = nGlobalError;
2842 nGlobalError = 0;
2843 // Temporarily override the ConvertStringToValue() error for
2844 // GetCellValue() / GetCellValueOrZero()
2845 sal_uInt16 nSErr = mnStringNoValueError;
2846 mnStringNoValueError = errCellNoValue;
2847 double fVal = GetDouble();
2848 mnStringNoValueError = nSErr;
2849 if (nErr)
2850 nGlobalError = nErr; // preserve previous error if any
2851 else if (nGlobalError == errCellNoValue)
2852 nGlobalError = 0; // reset temporary detection error
2853 PushDouble(fVal);
2856 void ScInterpreter::ScTrim()
2858 // Doesn't only trim but also removes duplicated blanks within!
2859 OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
2860 OUStringBuffer aStr;
2861 const sal_Unicode* p = aVal.getStr();
2862 const sal_Unicode* const pEnd = p + aVal.getLength();
2863 while ( p < pEnd )
2865 if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine
2866 aStr.append(*p);
2867 p++;
2869 PushString(aStr.makeStringAndClear());
2872 void ScInterpreter::ScUpper()
2874 OUString aString = ScGlobal::pCharClass->uppercase(GetString().getString());
2875 PushString(aString);
2878 void ScInterpreter::ScProper()
2880 //2do: what to do with I18N-CJK ?!?
2881 OUStringBuffer aStr(GetString().getString());
2882 const sal_Int32 nLen = aStr.getLength();
2883 if ( nLen > 0 )
2885 OUString aUpr(ScGlobal::pCharClass->uppercase(aStr.toString()));
2886 OUString aLwr(ScGlobal::pCharClass->lowercase(aStr.toString()));
2887 aStr[0] = aUpr[0];
2888 sal_Int32 nPos = 1;
2889 while( nPos < nLen )
2891 OUString aTmpStr( aStr[nPos-1] );
2892 if ( !ScGlobal::pCharClass->isLetter( aTmpStr, 0 ) )
2893 aStr[nPos] = aUpr[nPos];
2894 else
2895 aStr[nPos] = aLwr[nPos];
2896 ++nPos;
2899 PushString(aStr.makeStringAndClear());
2902 void ScInterpreter::ScLower()
2904 OUString aString = ScGlobal::pCharClass->lowercase(GetString().getString());
2905 PushString(aString);
2908 void ScInterpreter::ScLen()
2910 PushDouble(GetString().getLength());
2913 void ScInterpreter::ScT()
2915 switch ( GetStackType() )
2917 case svDoubleRef :
2918 case svSingleRef :
2920 ScAddress aAdr;
2921 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2923 PushInt(0);
2924 return ;
2926 bool bValue = false;
2927 ScRefCellValue aCell;
2928 aCell.assign(*pDok, aAdr);
2929 if (GetCellErrCode(aCell) == 0)
2931 switch (aCell.meType)
2933 case CELLTYPE_VALUE :
2934 bValue = true;
2935 break;
2936 case CELLTYPE_FORMULA :
2937 bValue = aCell.mpFormula->IsValue();
2938 break;
2939 default:
2940 ; // nothing
2943 if ( bValue )
2944 PushString(EMPTY_OUSTRING);
2945 else
2947 // like GetString()
2948 svl::SharedString aStr;
2949 GetCellString(aStr, aCell);
2950 PushString(aStr);
2953 break;
2954 case svMatrix:
2955 case svExternalSingleRef:
2956 case svExternalDoubleRef:
2958 double fVal;
2959 svl::SharedString aStr;
2960 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
2961 if (ScMatrix::IsValueType( nMatValType))
2962 PushString(svl::SharedString::getEmptyString());
2963 else
2964 PushString( aStr);
2966 break;
2967 case svDouble :
2969 PopError();
2970 PushString( EMPTY_OUSTRING );
2972 break;
2973 case svString :
2974 ; // leave on stack
2975 break;
2976 default :
2977 PushError( errUnknownOpCode);
2981 void ScInterpreter::ScValue()
2983 OUString aInputString;
2984 double fVal;
2986 switch ( GetRawStackType() )
2988 case svMissing:
2989 case svEmptyCell:
2990 Pop();
2991 PushInt(0);
2992 return;
2993 case svDouble:
2994 return; // leave on stack
2996 case svSingleRef:
2997 case svDoubleRef:
2999 ScAddress aAdr;
3000 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3002 PushInt(0);
3003 return;
3005 ScRefCellValue aCell;
3006 aCell.assign(*pDok, aAdr);
3007 if (aCell.hasString())
3009 svl::SharedString aSS;
3010 GetCellString(aSS, aCell);
3011 aInputString = aSS.getString();
3013 else if (aCell.hasNumeric())
3015 PushDouble( GetCellValue(aAdr, aCell) );
3016 return;
3018 else
3020 PushDouble(0.0);
3021 return;
3024 break;
3025 case svMatrix:
3027 svl::SharedString aSS;
3028 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
3029 aSS);
3030 aInputString = aSS.getString();
3031 switch (nType)
3033 case SC_MATVAL_EMPTY:
3034 fVal = 0.0;
3035 // fallthru
3036 case SC_MATVAL_VALUE:
3037 case SC_MATVAL_BOOLEAN:
3038 PushDouble( fVal);
3039 return;
3040 case SC_MATVAL_STRING:
3041 // evaluated below
3042 break;
3043 default:
3044 PushIllegalArgument();
3047 break;
3048 default:
3049 aInputString = GetString().getString();
3050 break;
3053 sal_uInt32 nFIndex = 0; // 0 for default locale
3054 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
3055 PushDouble(fVal);
3056 else
3057 PushIllegalArgument();
3060 // fdo#57180
3061 void ScInterpreter::ScNumberValue()
3064 sal_uInt8 nParamCount = GetByte();
3065 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
3066 return;
3068 OUString aInputString;
3069 OUString aDecimalSeparator, aGroupSeparator;
3070 sal_Unicode cDecimalSeparator = 0;
3072 if ( nParamCount == 3 )
3073 aGroupSeparator = GetString().getString();
3075 if ( nParamCount >= 2 )
3077 aDecimalSeparator = GetString().getString();
3078 if ( aDecimalSeparator.getLength() == 1 )
3079 cDecimalSeparator = aDecimalSeparator[ 0 ];
3080 else
3082 PushIllegalArgument(); //if given, separator length must be 1
3083 return;
3087 if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
3089 PushIllegalArgument(); //decimal separator cannot appear in group separator
3090 return;
3093 switch (GetStackType())
3095 case svDouble:
3096 return; // leave on stack
3097 default:
3098 aInputString = GetString().getString();
3100 if ( nGlobalError )
3102 PushError( nGlobalError );
3103 return;
3105 if ( aInputString.isEmpty() )
3107 if ( maCalcConfig.mbEmptyStringAsZero )
3108 PushDouble( 0.0 );
3109 else
3110 PushNoValue();
3111 return;
3114 sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
3115 if ( nDecSep != 0 )
3117 OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
3118 sal_Int32 nIndex = 0;
3119 while (nIndex < aGroupSeparator.getLength())
3121 sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
3122 aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
3124 if ( nDecSep >= 0 )
3125 aInputString = aTemporary + aInputString.copy( nDecSep );
3126 else
3127 aInputString = aTemporary;
3130 for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
3132 sal_Unicode c = aInputString[ i ];
3133 if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
3134 aInputString = aInputString.replaceAt( i, 1, "" ); // remove spaces etc.
3136 sal_Int32 nPercentCount = 0;
3137 for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
3139 aInputString = aInputString.replaceAt( i, 1, "" ); // remove and count trailing '%'
3140 nPercentCount++;
3143 rtl_math_ConversionStatus eStatus;
3144 sal_Int32 nParseEnd;
3145 double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
3146 if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
3148 if (nPercentCount)
3149 fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string
3150 PushDouble(fVal);
3151 return;
3153 PushNoValue();
3156 //2do: this should be a proper unicode string method
3157 static inline bool lcl_ScInterpreter_IsPrintable( sal_Unicode c )
3159 return 0x20 <= c && c != 0x7f;
3162 void ScInterpreter::ScClean()
3164 OUString aStr = GetString().getString();
3165 for ( sal_Int32 i = 0; i < aStr.getLength(); i++ )
3167 if ( !lcl_ScInterpreter_IsPrintable( aStr[i] ) )
3168 aStr = aStr.replaceAt(i,1,"");
3170 PushString(aStr);
3173 void ScInterpreter::ScCode()
3175 //2do: make it full range unicode?
3176 OUString aStr = GetString().getString();
3177 if (aStr.isEmpty())
3178 PushInt(0);
3179 else
3181 //"classic" ByteString conversion flags
3182 const sal_uInt32 convertFlags =
3183 RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE |
3184 RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE |
3185 RTL_UNICODETOTEXT_FLAGS_FLUSH |
3186 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT |
3187 RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT |
3188 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE;
3189 PushInt( (unsigned char) OUStringToOString(OUString(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar() );
3193 void ScInterpreter::ScChar()
3195 //2do: make it full range unicode?
3196 double fVal = GetDouble();
3197 if (fVal < 0.0 || fVal >= 256.0)
3198 PushIllegalArgument();
3199 else
3201 //"classic" ByteString conversion flags
3202 const sal_uInt32 convertFlags =
3203 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
3204 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
3205 RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
3207 sal_Char cEncodedChar = static_cast<sal_Char>(fVal);
3208 OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags);
3209 PushString(aStr);
3213 /* #i70213# fullwidth/halfwidth conversion provided by
3214 * Takashi Nakamoto <bluedwarf@ooo>
3215 * erAck: added Excel compatibility conversions as seen in issue's test case. */
3217 static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
3219 static bool bFirstASCCall = true;
3220 static utl::TransliterationWrapper aTrans( ::comphelper::getProcessComponentContext(), 0 );
3222 if( bFirstASCCall )
3224 aTrans.loadModuleByImplName( OUString( "FULLWIDTH_HALFWIDTH_LIKE_ASC" ), LANGUAGE_SYSTEM );
3225 bFirstASCCall = false;
3228 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ), NULL );
3231 static OUString lcl_convertIntoFullWidth( const OUString & rStr )
3233 static bool bFirstJISCall = true;
3234 static utl::TransliterationWrapper aTrans( ::comphelper::getProcessComponentContext(), 0 );
3236 if( bFirstJISCall )
3238 aTrans.loadModuleByImplName( OUString( "HALFWIDTH_FULLWIDTH_LIKE_JIS" ), LANGUAGE_SYSTEM );
3239 bFirstJISCall = false;
3242 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ), NULL );
3245 /* ODFF:
3246 * Summary: Converts half-width to full-width ASCII and katakana characters.
3247 * Semantics: Conversion is done for half-width ASCII and katakana characters,
3248 * other characters are simply copied from T to the result. This is the
3249 * complementary function to ASC.
3250 * For references regarding halfwidth and fullwidth characters see
3251 * http://www.unicode.org/reports/tr11/
3252 * http://www.unicode.org/charts/charindex2.html#H
3253 * http://www.unicode.org/charts/charindex2.html#F
3255 void ScInterpreter::ScJis()
3257 if (MustHaveParamCount( GetByte(), 1))
3258 PushString( lcl_convertIntoFullWidth( GetString().getString()));
3261 /* ODFF:
3262 * Summary: Converts full-width to half-width ASCII and katakana characters.
3263 * Semantics: Conversion is done for full-width ASCII and katakana characters,
3264 * other characters are simply copied from T to the result. This is the
3265 * complementary function to JIS.
3267 void ScInterpreter::ScAsc()
3269 if (MustHaveParamCount( GetByte(), 1))
3270 PushString( lcl_convertIntoHalfWidth( GetString().getString()));
3273 void ScInterpreter::ScUnicode()
3275 if ( MustHaveParamCount( GetByte(), 1 ) )
3277 OUString aStr = GetString().getString();
3278 if (aStr.isEmpty())
3279 PushIllegalParameter();
3280 else
3282 sal_Int32 i = 0;
3283 PushDouble(aStr.iterateCodePoints(&i));
3288 void ScInterpreter::ScUnichar()
3290 if ( MustHaveParamCount( GetByte(), 1 ) )
3292 double dVal = ::rtl::math::approxFloor( GetDouble() );
3293 if ((dVal < 0x000000) || (dVal > 0x10FFFF))
3294 PushIllegalArgument();
3295 else
3297 sal_uInt32 nCodePoint = static_cast<sal_uInt32>( dVal );
3298 OUString aStr( &nCodePoint, 1 );
3299 PushString( aStr );
3304 void ScInterpreter::ScMin( bool bTextAsZero )
3306 short nParamCount = GetByte();
3307 if (!MustHaveParamCountMin( nParamCount, 1))
3308 return;
3309 double nMin = ::std::numeric_limits<double>::max();
3310 double nVal = 0.0;
3311 ScAddress aAdr;
3312 ScRange aRange;
3313 size_t nRefInList = 0;
3314 while (nParamCount-- > 0)
3316 switch (GetStackType())
3318 case svDouble :
3320 nVal = GetDouble();
3321 if (nMin > nVal) nMin = nVal;
3322 nFuncFmtType = css::util::NumberFormat::NUMBER;
3324 break;
3325 case svSingleRef :
3327 PopSingleRef( aAdr );
3328 ScRefCellValue aCell;
3329 aCell.assign(*pDok, aAdr);
3330 if (aCell.hasNumeric())
3332 nVal = GetCellValue(aAdr, aCell);
3333 CurFmtToFuncFmt();
3334 if (nMin > nVal) nMin = nVal;
3336 else if (bTextAsZero && aCell.hasString())
3338 if ( nMin > 0.0 )
3339 nMin = 0.0;
3342 break;
3343 case svDoubleRef :
3344 case svRefList :
3346 sal_uInt16 nErr = 0;
3347 PopDoubleRef( aRange, nParamCount, nRefInList);
3348 ScValueIterator aValIter( pDok, aRange, mnSubTotalFlags, bTextAsZero );
3349 if (aValIter.GetFirst(nVal, nErr))
3351 if (nMin > nVal)
3352 nMin = nVal;
3353 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3354 while ((nErr == 0) && aValIter.GetNext(nVal, nErr))
3356 if (nMin > nVal)
3357 nMin = nVal;
3359 SetError(nErr);
3362 break;
3363 case svMatrix :
3364 case svExternalSingleRef:
3365 case svExternalDoubleRef:
3367 ScMatrixRef pMat = GetMatrix();
3368 if (pMat)
3370 nFuncFmtType = css::util::NumberFormat::NUMBER;
3371 nVal = pMat->GetMinValue(bTextAsZero);
3372 if (nMin > nVal)
3373 nMin = nVal;
3376 break;
3377 case svString :
3379 Pop();
3380 if ( bTextAsZero )
3382 if ( nMin > 0.0 )
3383 nMin = 0.0;
3385 else
3386 SetError(errIllegalParameter);
3388 break;
3389 default :
3390 Pop();
3391 SetError(errIllegalParameter);
3394 if ( nVal < nMin )
3395 PushDouble(0.0);
3396 else
3397 PushDouble(nMin);
3400 void ScInterpreter::ScMax( bool bTextAsZero )
3402 short nParamCount = GetByte();
3403 if (!MustHaveParamCountMin( nParamCount, 1))
3404 return;
3405 double nMax = -(::std::numeric_limits<double>::max());
3406 double nVal = 0.0;
3407 ScAddress aAdr;
3408 ScRange aRange;
3409 size_t nRefInList = 0;
3410 while (nParamCount-- > 0)
3412 switch (GetStackType())
3414 case svDouble :
3416 nVal = GetDouble();
3417 if (nMax < nVal) nMax = nVal;
3418 nFuncFmtType = css::util::NumberFormat::NUMBER;
3420 break;
3421 case svSingleRef :
3423 PopSingleRef( aAdr );
3424 ScRefCellValue aCell;
3425 aCell.assign(*pDok, aAdr);
3426 if (aCell.hasNumeric())
3428 nVal = GetCellValue(aAdr, aCell);
3429 CurFmtToFuncFmt();
3430 if (nMax < nVal) nMax = nVal;
3432 else if (bTextAsZero && aCell.hasString())
3434 if ( nMax < 0.0 )
3435 nMax = 0.0;
3438 break;
3439 case svDoubleRef :
3440 case svRefList :
3442 sal_uInt16 nErr = 0;
3443 PopDoubleRef( aRange, nParamCount, nRefInList);
3444 ScValueIterator aValIter( pDok, aRange, mnSubTotalFlags, bTextAsZero );
3445 if (aValIter.GetFirst(nVal, nErr))
3447 if (nMax < nVal)
3448 nMax = nVal;
3449 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3450 while ((nErr == 0) && aValIter.GetNext(nVal, nErr))
3452 if (nMax < nVal)
3453 nMax = nVal;
3455 SetError(nErr);
3458 break;
3459 case svMatrix :
3460 case svExternalSingleRef:
3461 case svExternalDoubleRef:
3463 ScMatrixRef pMat = GetMatrix();
3464 if (pMat)
3466 nFuncFmtType = css::util::NumberFormat::NUMBER;
3467 nVal = pMat->GetMaxValue(bTextAsZero);
3468 if (nMax < nVal)
3469 nMax = nVal;
3472 break;
3473 case svString :
3475 Pop();
3476 if ( bTextAsZero )
3478 if ( nMax < 0.0 )
3479 nMax = 0.0;
3481 else
3482 SetError(errIllegalParameter);
3484 break;
3485 default :
3486 Pop();
3487 SetError(errIllegalParameter);
3490 if ( nVal > nMax )
3491 PushDouble(0.0);
3492 else
3493 PushDouble(nMax);
3496 void ScInterpreter::GetStVarParams( double& rVal, double& rValCount,
3497 bool bTextAsZero )
3499 short nParamCount = GetByte();
3501 std::vector<double> values;
3502 double fSum = 0.0;
3503 double vSum = 0.0;
3504 double vMean = 0.0;
3505 double fVal = 0.0;
3506 rValCount = 0.0;
3507 ScAddress aAdr;
3508 ScRange aRange;
3509 size_t nRefInList = 0;
3510 while (nParamCount-- > 0)
3512 switch (GetStackType())
3514 case svDouble :
3516 fVal = GetDouble();
3517 values.push_back(fVal);
3518 fSum += fVal;
3519 rValCount++;
3521 break;
3522 case svSingleRef :
3524 PopSingleRef( aAdr );
3525 ScRefCellValue aCell;
3526 aCell.assign(*pDok, aAdr);
3527 if (aCell.hasNumeric())
3529 fVal = GetCellValue(aAdr, aCell);
3530 values.push_back(fVal);
3531 fSum += fVal;
3532 rValCount++;
3534 else if (bTextAsZero && aCell.hasString())
3536 values.push_back(0.0);
3537 rValCount++;
3540 break;
3541 case svDoubleRef :
3542 case svRefList :
3544 sal_uInt16 nErr = 0;
3545 PopDoubleRef( aRange, nParamCount, nRefInList);
3546 ScValueIterator aValIter( pDok, aRange, mnSubTotalFlags, bTextAsZero );
3547 if (aValIter.GetFirst(fVal, nErr))
3551 values.push_back(fVal);
3552 fSum += fVal;
3553 rValCount++;
3555 while ((nErr == 0) && aValIter.GetNext(fVal, nErr));
3558 break;
3559 case svMatrix :
3561 ScMatrixRef pMat = PopMatrix();
3562 if (pMat)
3564 SCSIZE nC, nR;
3565 pMat->GetDimensions(nC, nR);
3566 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
3568 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
3570 if (!pMat->IsString(nMatCol,nMatRow))
3572 fVal= pMat->GetDouble(nMatCol,nMatRow);
3573 values.push_back(fVal);
3574 fSum += fVal;
3575 rValCount++;
3577 else if ( bTextAsZero )
3579 values.push_back(0.0);
3580 rValCount++;
3586 break;
3587 case svString :
3589 Pop();
3590 if ( bTextAsZero )
3592 values.push_back(0.0);
3593 rValCount++;
3595 else
3596 SetError(errIllegalParameter);
3598 break;
3599 default :
3600 Pop();
3601 SetError(errIllegalParameter);
3605 ::std::vector<double>::size_type n = values.size();
3606 vMean = fSum / n;
3607 for (::std::vector<double>::size_type i = 0; i < n; i++)
3608 vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
3609 rVal = vSum;
3612 void ScInterpreter::ScVar( bool bTextAsZero )
3614 double nVal;
3615 double nValCount;
3616 GetStVarParams( nVal, nValCount, bTextAsZero );
3618 if (nValCount <= 1.0)
3619 PushError( errDivisionByZero );
3620 else
3621 PushDouble( nVal / (nValCount - 1.0));
3624 void ScInterpreter::ScVarP( bool bTextAsZero )
3626 double nVal;
3627 double nValCount;
3628 GetStVarParams( nVal, nValCount, bTextAsZero );
3630 PushDouble( div( nVal, nValCount));
3633 void ScInterpreter::ScStDev( bool bTextAsZero )
3635 double nVal;
3636 double nValCount;
3637 GetStVarParams( nVal, nValCount, bTextAsZero );
3638 if (nValCount <= 1.0)
3639 PushError( errDivisionByZero );
3640 else
3641 PushDouble( sqrt( nVal / (nValCount - 1.0)));
3644 void ScInterpreter::ScStDevP( bool bTextAsZero )
3646 double nVal;
3647 double nValCount;
3648 GetStVarParams( nVal, nValCount, bTextAsZero );
3649 if (nValCount == 0.0)
3650 PushError( errDivisionByZero );
3651 else
3652 PushDouble( sqrt( nVal / nValCount));
3654 /* this was: PushDouble( sqrt( div( nVal, nValCount)));
3656 * Besides that the special NAN gets lost in the call through sqrt(),
3657 * unxlngi6.pro then looped back and forth somewhere between div() and
3658 * ::rtl::math::setNan(). Tests showed that
3660 * sqrt( div( 1, 0));
3662 * produced a loop, but
3664 * double f1 = div( 1, 0);
3665 * sqrt( f1 );
3667 * was fine. There seems to be some compiler optimization problem. It does
3668 * not occur when compiled with debug=t.
3672 void ScInterpreter::ScColumns()
3674 sal_uInt8 nParamCount = GetByte();
3675 sal_uLong nVal = 0;
3676 SCCOL nCol1;
3677 SCROW nRow1;
3678 SCTAB nTab1;
3679 SCCOL nCol2;
3680 SCROW nRow2;
3681 SCTAB nTab2;
3682 while (nParamCount-- > 0)
3684 switch ( GetStackType() )
3686 case svSingleRef:
3687 PopError();
3688 nVal++;
3689 break;
3690 case svDoubleRef:
3691 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3692 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
3693 static_cast<sal_uLong>(nCol2 - nCol1 + 1);
3694 break;
3695 case svMatrix:
3697 ScMatrixRef pMat = PopMatrix();
3698 if (pMat)
3700 SCSIZE nC, nR;
3701 pMat->GetDimensions(nC, nR);
3702 nVal += nC;
3705 break;
3706 case svExternalSingleRef:
3707 PopError();
3708 nVal++;
3709 break;
3710 case svExternalDoubleRef:
3712 sal_uInt16 nFileId;
3713 OUString aTabName;
3714 ScComplexRefData aRef;
3715 PopExternalDoubleRef( nFileId, aTabName, aRef);
3716 ScRange aAbs = aRef.toAbs(aPos);
3717 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
3718 static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
3720 break;
3721 default:
3722 PopError();
3723 SetError(errIllegalParameter);
3726 PushDouble((double)nVal);
3729 void ScInterpreter::ScRows()
3731 sal_uInt8 nParamCount = GetByte();
3732 sal_uLong nVal = 0;
3733 SCCOL nCol1;
3734 SCROW nRow1;
3735 SCTAB nTab1;
3736 SCCOL nCol2;
3737 SCROW nRow2;
3738 SCTAB nTab2;
3739 while (nParamCount-- > 0)
3741 switch ( GetStackType() )
3743 case svSingleRef:
3744 PopError();
3745 nVal++;
3746 break;
3747 case svDoubleRef:
3748 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3749 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
3750 static_cast<sal_uLong>(nRow2 - nRow1 + 1);
3751 break;
3752 case svMatrix:
3754 ScMatrixRef pMat = PopMatrix();
3755 if (pMat)
3757 SCSIZE nC, nR;
3758 pMat->GetDimensions(nC, nR);
3759 nVal += nR;
3762 break;
3763 case svExternalSingleRef:
3764 PopError();
3765 nVal++;
3766 break;
3767 case svExternalDoubleRef:
3769 sal_uInt16 nFileId;
3770 OUString aTabName;
3771 ScComplexRefData aRef;
3772 PopExternalDoubleRef( nFileId, aTabName, aRef);
3773 ScRange aAbs = aRef.toAbs(aPos);
3774 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
3775 static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
3777 break;
3778 default:
3779 PopError();
3780 SetError(errIllegalParameter);
3783 PushDouble((double)nVal);
3786 void ScInterpreter::ScSheets()
3788 sal_uInt8 nParamCount = GetByte();
3789 sal_uLong nVal;
3790 if ( nParamCount == 0 )
3791 nVal = pDok->GetTableCount();
3792 else
3794 nVal = 0;
3795 SCCOL nCol1;
3796 SCROW nRow1;
3797 SCTAB nTab1;
3798 SCCOL nCol2;
3799 SCROW nRow2;
3800 SCTAB nTab2;
3801 while (nParamCount-- > 0)
3803 switch ( GetStackType() )
3805 case svSingleRef:
3806 PopError();
3807 nVal++;
3808 break;
3809 case svDoubleRef:
3810 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3811 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
3812 break;
3813 case svMatrix:
3814 PopError();
3815 nVal++;
3816 break;
3817 case svExternalSingleRef:
3818 PopError();
3819 nVal++;
3820 break;
3821 case svExternalDoubleRef:
3823 sal_uInt16 nFileId;
3824 OUString aTabName;
3825 ScComplexRefData aRef;
3826 PopExternalDoubleRef( nFileId, aTabName, aRef);
3827 ScRange aAbs = aRef.toAbs(aPos);
3828 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
3830 break;
3831 default:
3832 PopError();
3833 SetError( errIllegalParameter );
3837 PushDouble( (double) nVal );
3840 void ScInterpreter::ScColumn()
3842 sal_uInt8 nParamCount = GetByte();
3843 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
3845 double nVal = 0;
3846 if (nParamCount == 0)
3848 nVal = aPos.Col() + 1;
3849 if (bMatrixFormula)
3851 SCCOL nCols;
3852 SCROW nRows;
3853 pMyFormulaCell->GetMatColsRows( nCols, nRows);
3854 if (nCols == 0)
3856 // Happens if called via ScViewFunc::EnterMatrix()
3857 // ScFormulaCell::GetResultDimensions() as of course a
3858 // matrix result is not available yet.
3859 nCols = 1;
3861 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1);
3862 if (pResMat)
3864 for (SCCOL i=0; i < nCols; ++i)
3865 pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
3866 PushMatrix( pResMat);
3867 return;
3871 else
3873 switch ( GetStackType() )
3875 case svSingleRef :
3877 SCCOL nCol1;
3878 SCROW nRow1;
3879 SCTAB nTab1;
3880 PopSingleRef( nCol1, nRow1, nTab1 );
3881 nVal = (double) (nCol1 + 1);
3883 break;
3884 case svDoubleRef :
3886 SCCOL nCol1;
3887 SCROW nRow1;
3888 SCTAB nTab1;
3889 SCCOL nCol2;
3890 SCROW nRow2;
3891 SCTAB nTab2;
3892 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
3893 if (nCol2 > nCol1)
3895 ScMatrixRef pResMat = GetNewMat(
3896 static_cast<SCSIZE>(nCol2-nCol1+1), 1);
3897 if (pResMat)
3899 for (SCCOL i = nCol1; i <= nCol2; i++)
3900 pResMat->PutDouble((double)(i+1),
3901 static_cast<SCSIZE>(i-nCol1), 0);
3902 PushMatrix(pResMat);
3903 return;
3905 else
3906 nVal = 0.0;
3908 else
3909 nVal = (double) (nCol1 + 1);
3911 break;
3912 default:
3913 SetError( errIllegalParameter );
3914 nVal = 0.0;
3917 PushDouble( nVal );
3921 void ScInterpreter::ScRow()
3923 sal_uInt8 nParamCount = GetByte();
3924 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
3926 double nVal = 0;
3927 if (nParamCount == 0)
3929 nVal = aPos.Row() + 1;
3930 if (bMatrixFormula)
3932 SCCOL nCols;
3933 SCROW nRows;
3934 pMyFormulaCell->GetMatColsRows( nCols, nRows);
3935 if (nRows == 0)
3937 // Happens if called via ScViewFunc::EnterMatrix()
3938 // ScFormulaCell::GetResultDimensions() as of course a
3939 // matrix result is not available yet.
3940 nRows = 1;
3942 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows));
3943 if (pResMat)
3945 for (SCROW i=0; i < nRows; i++)
3946 pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
3947 PushMatrix( pResMat);
3948 return;
3952 else
3954 switch ( GetStackType() )
3956 case svSingleRef :
3958 SCCOL nCol1;
3959 SCROW nRow1;
3960 SCTAB nTab1;
3961 PopSingleRef( nCol1, nRow1, nTab1 );
3962 nVal = (double) (nRow1 + 1);
3964 break;
3965 case svDoubleRef :
3967 SCCOL nCol1;
3968 SCROW nRow1;
3969 SCTAB nTab1;
3970 SCCOL nCol2;
3971 SCROW nRow2;
3972 SCTAB nTab2;
3973 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
3974 if (nRow2 > nRow1)
3976 ScMatrixRef pResMat = GetNewMat( 1,
3977 static_cast<SCSIZE>(nRow2-nRow1+1));
3978 if (pResMat)
3980 for (SCROW i = nRow1; i <= nRow2; i++)
3981 pResMat->PutDouble((double)(i+1), 0,
3982 static_cast<SCSIZE>(i-nRow1));
3983 PushMatrix(pResMat);
3984 return;
3986 else
3987 nVal = 0.0;
3989 else
3990 nVal = (double) (nRow1 + 1);
3992 break;
3993 default:
3994 SetError( errIllegalParameter );
3995 nVal = 0.0;
3998 PushDouble( nVal );
4002 void ScInterpreter::ScSheet()
4004 sal_uInt8 nParamCount = GetByte();
4005 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
4007 SCTAB nVal = 0;
4008 if ( nParamCount == 0 )
4009 nVal = aPos.Tab() + 1;
4010 else
4012 switch ( GetStackType() )
4014 case svString :
4016 svl::SharedString aStr = PopString();
4017 if ( pDok->GetTable(aStr.getString(), nVal))
4018 ++nVal;
4019 else
4020 SetError( errIllegalArgument );
4022 break;
4023 case svSingleRef :
4025 SCCOL nCol1;
4026 SCROW nRow1;
4027 SCTAB nTab1;
4028 PopSingleRef( nCol1, nRow1, nTab1 );
4029 nVal = nTab1 + 1;
4031 break;
4032 case svDoubleRef :
4034 SCCOL nCol1;
4035 SCROW nRow1;
4036 SCTAB nTab1;
4037 SCCOL nCol2;
4038 SCROW nRow2;
4039 SCTAB nTab2;
4040 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4041 nVal = nTab1 + 1;
4043 break;
4044 default:
4045 SetError( errIllegalParameter );
4047 if ( nGlobalError )
4048 nVal = 0;
4050 PushDouble( (double) nVal );
4054 namespace {
4056 class VectorMatrixAccessor
4058 public:
4059 VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
4060 mrMat(rMat), mbColVec(bColVec) {}
4062 bool IsEmpty(SCSIZE i) const
4064 return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
4067 bool IsEmptyPath(SCSIZE i) const
4069 return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
4072 bool IsValue(SCSIZE i) const
4074 return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
4077 bool IsString(SCSIZE i) const
4079 return mbColVec ? mrMat.IsString(0, i) : mrMat.IsString(i, 0);
4082 double GetDouble(SCSIZE i) const
4084 return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
4087 OUString GetString(SCSIZE i) const
4089 return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
4092 SCSIZE GetElementCount() const
4094 SCSIZE nC, nR;
4095 mrMat.GetDimensions(nC, nR);
4096 return mbColVec ? nR : nC;
4099 private:
4100 const ScMatrix& mrMat;
4101 bool mbColVec;
4104 /** returns -1 when the matrix value is smaller than the query value, 0 when
4105 they are equal, and 1 when the matrix value is larger than the query
4106 value. */
4107 static sal_Int32 lcl_CompareMatrix2Query(
4108 SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry)
4110 if (rMat.IsEmpty(i))
4112 /* TODO: in case we introduced query for real empty this would have to
4113 * be changed! */
4114 return -1; // empty always less than anything else
4117 /* FIXME: what is an empty path (result of IF(false;true_path) in
4118 * comparisons? */
4120 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
4121 if (rMat.IsValue(i))
4123 if (bByString)
4124 return -1; // numeric always less than string
4126 const double nVal1 = rMat.GetDouble(i);
4127 const double nVal2 = rEntry.GetQueryItem().mfVal;
4128 if (nVal1 == nVal2)
4129 return 0;
4131 return nVal1 < nVal2 ? -1 : 1;
4134 if (!bByString)
4135 return 1; // string always greater than numeric
4137 OUString aStr1 = rMat.GetString(i);
4138 OUString aStr2 = rEntry.GetQueryItem().maString.getString();
4140 return ScGlobal::GetCollator()->compareString(aStr1, aStr2); // case-insensitive
4143 /** returns the last item with the identical value as the original item
4144 value. */
4145 static void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
4146 SCSIZE nMatCount, bool bReverse)
4148 if (rMat.IsValue(rIndex))
4150 double nVal = rMat.GetDouble(rIndex);
4151 if (bReverse)
4152 while (rIndex > 0 && rMat.IsValue(rIndex-1) &&
4153 nVal == rMat.GetDouble(rIndex-1))
4154 --rIndex;
4155 else
4156 while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
4157 nVal == rMat.GetDouble(rIndex+1))
4158 ++rIndex;
4160 // Order of IsEmptyPath, IsEmpty, IsString is significant!
4161 else if (rMat.IsEmptyPath(rIndex))
4163 if (bReverse)
4164 while (rIndex > 0 && rMat.IsEmptyPath(rIndex-1))
4165 --rIndex;
4166 else
4167 while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
4168 ++rIndex;
4170 else if (rMat.IsEmpty(rIndex))
4172 if (bReverse)
4173 while (rIndex > 0 && rMat.IsEmpty(rIndex-1))
4174 --rIndex;
4175 else
4176 while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4177 ++rIndex;
4179 else if (rMat.IsString(rIndex))
4181 OUString aStr( rMat.GetString(rIndex));
4182 if (bReverse)
4183 while (rIndex > 0 && rMat.IsString(rIndex-1) &&
4184 aStr == rMat.GetString(rIndex-1))
4185 --rIndex;
4186 else
4187 while (rIndex < nMatCount-1 && rMat.IsString(rIndex+1) &&
4188 aStr == rMat.GetString(rIndex+1))
4189 ++rIndex;
4191 else
4193 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4199 void ScInterpreter::ScMatch()
4202 sal_uInt8 nParamCount = GetByte();
4203 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
4205 double fTyp;
4206 if (nParamCount == 3)
4207 fTyp = GetDouble();
4208 else
4209 fTyp = 1.0;
4210 SCCOL nCol1 = 0;
4211 SCROW nRow1 = 0;
4212 SCTAB nTab1 = 0;
4213 SCCOL nCol2 = 0;
4214 SCROW nRow2 = 0;
4215 SCTAB nTab2 = 0;
4216 ScMatrixRef pMatSrc = NULL;
4218 switch (GetStackType())
4220 case svSingleRef:
4221 PopSingleRef( nCol1, nRow1, nTab1);
4222 nCol2 = nCol1;
4223 nRow2 = nRow1;
4224 nTab2 = nTab1;
4225 break;
4226 case svDoubleRef:
4228 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4229 if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
4231 PushIllegalParameter();
4232 return;
4235 break;
4236 case svMatrix:
4237 case svExternalDoubleRef:
4239 if (GetStackType() == svMatrix)
4240 pMatSrc = PopMatrix();
4241 else
4242 PopExternalDoubleRef(pMatSrc);
4244 if (!pMatSrc)
4246 PushIllegalParameter();
4247 return;
4250 break;
4251 default:
4252 PushIllegalParameter();
4253 return;
4256 if (nGlobalError == 0)
4258 double fVal;
4259 ScQueryParam rParam;
4260 rParam.nCol1 = nCol1;
4261 rParam.nRow1 = nRow1;
4262 rParam.nCol2 = nCol2;
4263 rParam.nTab = nTab1;
4265 ScQueryEntry& rEntry = rParam.GetEntry(0);
4266 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4267 rEntry.bDoQuery = true;
4268 if (fTyp < 0.0)
4269 rEntry.eOp = SC_GREATER_EQUAL;
4270 else if (fTyp > 0.0)
4271 rEntry.eOp = SC_LESS_EQUAL;
4272 switch ( GetStackType() )
4274 case svDouble:
4276 fVal = GetDouble();
4277 rItem.mfVal = fVal;
4278 rItem.meType = ScQueryEntry::ByValue;
4280 break;
4281 case svString:
4283 rItem.meType = ScQueryEntry::ByString;
4284 rItem.maString = GetString();
4286 break;
4287 case svDoubleRef :
4288 case svSingleRef :
4290 ScAddress aAdr;
4291 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4293 PushInt(0);
4294 return ;
4296 ScRefCellValue aCell;
4297 aCell.assign(*pDok, aAdr);
4298 if (aCell.hasNumeric())
4300 fVal = GetCellValue(aAdr, aCell);
4301 rItem.meType = ScQueryEntry::ByValue;
4302 rItem.mfVal = fVal;
4304 else
4306 GetCellString(rItem.maString, aCell);
4307 rItem.meType = ScQueryEntry::ByString;
4310 break;
4311 case svExternalSingleRef:
4313 ScExternalRefCache::TokenRef pToken;
4314 PopExternalSingleRef(pToken);
4315 if (!pToken)
4317 PushInt(0);
4318 return;
4320 if (pToken->GetType() == svDouble)
4322 rItem.meType = ScQueryEntry::ByValue;
4323 rItem.mfVal = pToken->GetDouble();
4325 else
4327 rItem.meType = ScQueryEntry::ByString;
4328 rItem.maString = pToken->GetString();
4331 break;
4332 case svExternalDoubleRef:
4333 // TODO: Implement this.
4334 PushIllegalParameter();
4335 return;
4336 break;
4337 case svMatrix :
4339 svl::SharedString aStr;
4340 ScMatValType nType = GetDoubleOrStringFromMatrix(
4341 rItem.mfVal, aStr);
4342 rItem.maString = aStr;
4343 rItem.meType = ScMatrix::IsNonValueType(nType) ?
4344 ScQueryEntry::ByString : ScQueryEntry::ByValue;
4346 break;
4347 default:
4349 PushIllegalParameter();
4350 return;
4353 if (rItem.meType == ScQueryEntry::ByString)
4355 bool bIsVBAMode = pDok->IsInVBAMode();
4357 // #TODO handle MSO wildcards
4358 if ( bIsVBAMode )
4359 rParam.bRegExp = false;
4360 else
4361 rParam.bRegExp = MayBeRegExp(rEntry.GetQueryItem().maString.getString(), pDok);
4364 if (pMatSrc) // The source data is matrix array.
4366 SCSIZE nC, nR;
4367 pMatSrc->GetDimensions( nC, nR);
4368 if (nC > 1 && nR > 1)
4370 // The source matrix must be a vector.
4371 PushIllegalParameter();
4372 return;
4374 SCSIZE nMatCount = (nC == 1) ? nR : nC;
4375 VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1);
4377 // simple serial search for equality mode (source data doesn't
4378 // need to be sorted).
4380 if (rEntry.eOp == SC_EQUAL)
4382 for (SCSIZE i = 0; i < nMatCount; ++i)
4384 if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0)
4386 PushDouble(i+1); // found !
4387 return;
4390 PushNA(); // not found
4391 return;
4394 // binary search for non-equality mode (the source data is
4395 // assumed to be sorted).
4397 bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
4398 SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
4399 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
4401 SCSIZE nMid = nFirst + nLen/2;
4402 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry);
4403 if (nCmp == 0)
4405 // exact match. find the last item with the same value.
4406 lcl_GetLastMatch( nMid, aMatAcc, nMatCount, !bAscOrder);
4407 PushDouble( nMid+1);
4408 return;
4411 if (nLen == 1) // first and last items are next to each other.
4413 if (nCmp < 0)
4414 nHitIndex = bAscOrder ? nLast : nFirst;
4415 else
4416 nHitIndex = bAscOrder ? nFirst : nLast;
4417 break;
4420 if (nCmp < 0)
4422 if (bAscOrder)
4423 nFirst = nMid;
4424 else
4425 nLast = nMid;
4427 else
4429 if (bAscOrder)
4430 nLast = nMid;
4431 else
4432 nFirst = nMid;
4436 if (nHitIndex == nMatCount-1) // last item
4438 sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry);
4439 if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
4441 // either the last item is an exact match or the real
4442 // hit is beyond the last item.
4443 PushDouble( nHitIndex+1);
4444 return;
4448 if (nHitIndex > 0) // valid hit must be 2nd item or higher
4450 PushDouble( nHitIndex); // non-exact match
4451 return;
4454 PushNA();
4455 return;
4458 SCCOLROW nDelta = 0;
4459 if (nCol1 == nCol2)
4460 { // search row in column
4461 rParam.nRow2 = nRow2;
4462 rEntry.nField = nCol1;
4463 ScAddress aResultPos( nCol1, nRow1, nTab1);
4464 if (!LookupQueryWithCache( aResultPos, rParam))
4466 PushNA();
4467 return;
4469 nDelta = aResultPos.Row() - nRow1;
4471 else
4472 { // search column in row
4473 SCCOL nC;
4474 rParam.bByRow = false;
4475 rParam.nRow2 = nRow1;
4476 rEntry.nField = nCol1;
4477 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false);
4478 // Advance Entry.nField in Iterator if column changed
4479 aCellIter.SetAdvanceQueryParamEntryField( true );
4480 if (fTyp == 0.0)
4481 { // EQUAL
4482 if ( aCellIter.GetFirst() )
4483 nC = aCellIter.GetCol();
4484 else
4486 PushNA();
4487 return;
4490 else
4491 { // <= or >=
4492 SCROW nR;
4493 if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
4495 PushNA();
4496 return;
4499 nDelta = nC - nCol1;
4501 PushDouble((double) (nDelta + 1));
4503 else
4504 PushIllegalParameter();
4508 namespace {
4510 bool isCellContentEmpty( const ScRefCellValue& rCell )
4512 switch (rCell.meType)
4514 case CELLTYPE_VALUE:
4515 case CELLTYPE_STRING:
4516 case CELLTYPE_EDIT:
4517 return false;
4518 case CELLTYPE_FORMULA:
4520 // NOTE: Excel treats ="" in a referenced cell as blank in
4521 // COUNTBLANK() but not in ISBLANK(), which is inconsistent.
4522 // COUNTBLANK() tests the (display) result whereas ISBLANK() tests
4523 // the cell content.
4524 // ODFF allows both for COUNTBLANK().
4525 // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in
4526 // COUNTBLANK(), we now do for Excel interoperability.
4527 /* TODO: introduce yet another compatibility option? */
4528 sc::FormulaResultValue aRes = rCell.mpFormula->GetResult();
4529 if (aRes.meType != sc::FormulaResultValue::String)
4530 return false;
4531 if (!aRes.maString.isEmpty())
4532 return false;
4534 break;
4535 default:
4539 return true;
4544 void ScInterpreter::ScCountEmptyCells()
4546 if ( MustHaveParamCount( GetByte(), 1 ) )
4548 sal_uLong nMaxCount = 0, nCount = 0;
4549 switch (GetStackType())
4551 case svSingleRef :
4553 nMaxCount = 1;
4554 ScAddress aAdr;
4555 PopSingleRef( aAdr );
4556 ScRefCellValue aCell;
4557 aCell.assign(*pDok, aAdr);
4558 if (!isCellContentEmpty(aCell))
4559 nCount = 1;
4561 break;
4562 case svDoubleRef :
4563 case svRefList :
4565 ScRange aRange;
4566 short nParam = 1;
4567 size_t nRefInList = 0;
4568 while (nParam-- > 0)
4570 PopDoubleRef( aRange, nParam, nRefInList);
4571 nMaxCount +=
4572 static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
4573 static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
4574 static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
4576 ScCellIterator aIter( pDok, aRange, mnSubTotalFlags);
4577 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
4579 const ScRefCellValue& rCell = aIter.getRefCellValue();
4580 if (!isCellContentEmpty(rCell))
4581 ++nCount;
4585 break;
4586 default : SetError(errIllegalParameter); break;
4588 PushDouble(nMaxCount - nCount);
4592 double ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc )
4594 sal_uInt8 nParamCount = GetByte();
4595 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
4596 return 0;
4598 SCCOL nCol3 = 0;
4599 SCROW nRow3 = 0;
4600 SCTAB nTab3 = 0;
4602 ScMatrixRef pSumExtraMatrix;
4603 bool bSumExtraRange = (nParamCount == 3);
4604 if (bSumExtraRange)
4606 // Save only the upperleft cell in case of cell range. The geometry
4607 // of the 3rd parameter is taken from the 1st parameter.
4609 switch ( GetStackType() )
4611 case svDoubleRef :
4613 SCCOL nColJunk = 0;
4614 SCROW nRowJunk = 0;
4615 SCTAB nTabJunk = 0;
4616 PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
4617 if ( nTabJunk != nTab3 )
4619 SetError( errIllegalParameter);
4620 return 0;
4623 break;
4624 case svSingleRef :
4625 PopSingleRef( nCol3, nRow3, nTab3 );
4626 break;
4627 case svMatrix:
4628 pSumExtraMatrix = PopMatrix();
4629 // nCol3, nRow3, nTab3 remain 0
4630 break;
4631 case svExternalSingleRef:
4633 pSumExtraMatrix = new ScMatrix(1, 1, 0.0);
4634 ScExternalRefCache::TokenRef pToken;
4635 PopExternalSingleRef(pToken);
4636 if (!pToken)
4638 SetError( errIllegalParameter);
4639 return 0;
4642 if (pToken->GetType() == svDouble)
4643 pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
4644 else
4645 pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);
4647 break;
4648 case svExternalDoubleRef:
4649 PopExternalDoubleRef(pSumExtraMatrix);
4650 break;
4651 default:
4652 SetError( errIllegalParameter);
4653 return 0;
4657 svl::SharedString aString;
4658 double fVal = 0.0;
4659 bool bIsString = true;
4660 switch ( GetStackType() )
4662 case svDoubleRef :
4663 case svSingleRef :
4665 ScAddress aAdr;
4666 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4667 return 0;
4669 ScRefCellValue aCell;
4670 aCell.assign(*pDok, aAdr);
4671 switch (aCell.meType)
4673 case CELLTYPE_VALUE :
4674 fVal = GetCellValue(aAdr, aCell);
4675 bIsString = false;
4676 break;
4677 case CELLTYPE_FORMULA :
4678 if (aCell.mpFormula->IsValue())
4680 fVal = GetCellValue(aAdr, aCell);
4681 bIsString = false;
4683 else
4684 GetCellString(aString, aCell);
4685 break;
4686 case CELLTYPE_STRING :
4687 case CELLTYPE_EDIT :
4688 GetCellString(aString, aCell);
4689 break;
4690 default:
4691 fVal = 0.0;
4692 bIsString = false;
4695 break;
4696 case svString:
4697 aString = GetString();
4698 break;
4699 case svMatrix :
4700 case svExternalDoubleRef:
4702 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
4703 bIsString = ScMatrix::IsNonValueType( nType);
4705 break;
4706 case svExternalSingleRef:
4708 ScExternalRefCache::TokenRef pToken;
4709 PopExternalSingleRef(pToken);
4710 if (pToken)
4712 if (pToken->GetType() == svDouble)
4714 fVal = pToken->GetDouble();
4715 bIsString = false;
4717 else
4718 aString = pToken->GetString();
4721 break;
4722 default:
4724 fVal = GetDouble();
4725 bIsString = false;
4729 double fSum = 0.0;
4730 double fMem = 0.0;
4731 double fRes = 0.0;
4732 double fCount = 0.0;
4733 bool bNull = true;
4734 short nParam = 1;
4735 size_t nRefInList = 0;
4736 while (nParam-- > 0)
4738 SCCOL nCol1 = 0;
4739 SCROW nRow1 = 0;
4740 SCTAB nTab1 = 0;
4741 SCCOL nCol2 = 0;
4742 SCROW nRow2 = 0;
4743 SCTAB nTab2 = 0;
4744 ScMatrixRef pQueryMatrix;
4745 switch ( GetStackType() )
4747 case svRefList :
4748 if (bSumExtraRange)
4750 SetError( errIllegalParameter);
4752 else
4754 ScRange aRange;
4755 PopDoubleRef( aRange, nParam, nRefInList);
4756 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4758 break;
4759 case svDoubleRef :
4760 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4761 break;
4762 case svSingleRef :
4763 PopSingleRef( nCol1, nRow1, nTab1 );
4764 nCol2 = nCol1;
4765 nRow2 = nRow1;
4766 nTab2 = nTab1;
4767 break;
4768 case svMatrix:
4769 case svExternalSingleRef:
4770 case svExternalDoubleRef:
4772 pQueryMatrix = GetMatrix();
4773 if (!pQueryMatrix)
4775 SetError( errIllegalParameter);
4776 return 0;
4778 nCol1 = 0;
4779 nRow1 = 0;
4780 nTab1 = 0;
4781 SCSIZE nC, nR;
4782 pQueryMatrix->GetDimensions( nC, nR);
4783 nCol2 = static_cast<SCCOL>(nC - 1);
4784 nRow2 = static_cast<SCROW>(nR - 1);
4785 nTab2 = 0;
4787 break;
4788 default:
4789 SetError( errIllegalParameter);
4791 if ( nTab1 != nTab2 )
4793 SetError( errIllegalParameter);
4796 if (bSumExtraRange)
4798 // Take the range geometry of the 1st parameter and apply it to
4799 // the 3rd. If parts of the resulting range would point outside
4800 // the sheet, don't complain but silently ignore and simply cut
4801 // them away, this is what Xcl does :-/
4803 // For the cut-away part we also don't need to determine the
4804 // criteria match, so shrink the source range accordingly,
4805 // instead of the result range.
4806 SCCOL nColDelta = nCol2 - nCol1;
4807 SCROW nRowDelta = nRow2 - nRow1;
4808 SCCOL nMaxCol;
4809 SCROW nMaxRow;
4810 if (pSumExtraMatrix)
4812 SCSIZE nC, nR;
4813 pSumExtraMatrix->GetDimensions( nC, nR);
4814 nMaxCol = static_cast<SCCOL>(nC - 1);
4815 nMaxRow = static_cast<SCROW>(nR - 1);
4817 else
4819 nMaxCol = MAXCOL;
4820 nMaxRow = MAXROW;
4822 if (nCol3 + nColDelta > nMaxCol)
4824 SCCOL nNewDelta = nMaxCol - nCol3;
4825 nCol2 = nCol1 + nNewDelta;
4828 if (nRow3 + nRowDelta > nMaxRow)
4830 SCROW nNewDelta = nMaxRow - nRow3;
4831 nRow2 = nRow1 + nNewDelta;
4834 else
4836 nCol3 = nCol1;
4837 nRow3 = nRow1;
4838 nTab3 = nTab1;
4841 if (nGlobalError == 0)
4843 ScQueryParam rParam;
4844 rParam.nRow1 = nRow1;
4845 rParam.nRow2 = nRow2;
4847 ScQueryEntry& rEntry = rParam.GetEntry(0);
4848 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4849 rEntry.bDoQuery = true;
4850 if (!bIsString)
4852 rItem.meType = ScQueryEntry::ByValue;
4853 rItem.mfVal = fVal;
4854 rEntry.eOp = SC_EQUAL;
4856 else
4858 rParam.FillInExcelSyntax(pDok->GetSharedStringPool(), aString.getString(), 0, pFormatter);
4859 if (rItem.meType == ScQueryEntry::ByString)
4860 rParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
4862 ScAddress aAdr;
4863 aAdr.SetTab( nTab3 );
4864 rParam.nCol1 = nCol1;
4865 rParam.nCol2 = nCol2;
4866 rEntry.nField = nCol1;
4867 SCsCOL nColDiff = nCol3 - nCol1;
4868 SCsROW nRowDiff = nRow3 - nRow1;
4869 if (pQueryMatrix)
4871 // Never case-sensitive.
4872 sc::CompareOptions aOptions( pDok, rEntry, rParam.bRegExp);
4873 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
4874 if (nGlobalError || !pResultMatrix)
4876 SetError( errIllegalParameter);
4879 if (pSumExtraMatrix)
4881 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
4883 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
4885 if (pResultMatrix->IsValue( nCol, nRow) &&
4886 pResultMatrix->GetDouble( nCol, nRow))
4888 SCSIZE nC = nCol + nColDiff;
4889 SCSIZE nR = nRow + nRowDiff;
4890 if (pSumExtraMatrix->IsValue( nC, nR))
4892 fVal = pSumExtraMatrix->GetDouble( nC, nR);
4893 ++fCount;
4894 if ( bNull && fVal != 0.0 )
4896 bNull = false;
4897 fMem = fVal;
4899 else
4900 fSum += fVal;
4906 else
4908 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
4910 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
4912 if (pResultMatrix->GetDouble( nCol, nRow))
4914 aAdr.SetCol( nCol + nColDiff);
4915 aAdr.SetRow( nRow + nRowDiff);
4916 ScRefCellValue aCell;
4917 aCell.assign(*pDok, aAdr);
4918 if (aCell.hasNumeric())
4920 fVal = GetCellValue(aAdr, aCell);
4921 ++fCount;
4922 if ( bNull && fVal != 0.0 )
4924 bNull = false;
4925 fMem = fVal;
4927 else
4928 fSum += fVal;
4935 else
4937 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false);
4938 // Increment Entry.nField in iterator when switching to next column.
4939 aCellIter.SetAdvanceQueryParamEntryField( true );
4940 if ( aCellIter.GetFirst() )
4942 if (pSumExtraMatrix)
4946 SCSIZE nC = aCellIter.GetCol() + nColDiff;
4947 SCSIZE nR = aCellIter.GetRow() + nRowDiff;
4948 if (pSumExtraMatrix->IsValue( nC, nR))
4950 fVal = pSumExtraMatrix->GetDouble( nC, nR);
4951 ++fCount;
4952 if ( bNull && fVal != 0.0 )
4954 bNull = false;
4955 fMem = fVal;
4957 else
4958 fSum += fVal;
4960 } while ( aCellIter.GetNext() );
4962 else
4966 aAdr.SetCol( aCellIter.GetCol() + nColDiff);
4967 aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
4968 ScRefCellValue aCell;
4969 aCell.assign(*pDok, aAdr);
4970 if (aCell.hasNumeric())
4972 fVal = GetCellValue(aAdr, aCell);
4973 ++fCount;
4974 if ( bNull && fVal != 0.0 )
4976 bNull = false;
4977 fMem = fVal;
4979 else
4980 fSum += fVal;
4982 } while ( aCellIter.GetNext() );
4987 else
4989 SetError( errIllegalParameter);
4990 return 0;
4994 switch( eFunc )
4996 case ifSUMIF: fRes = ::rtl::math::approxAdd( fSum, fMem ); break;
4997 case ifAVERAGEIF: fRes = div( ::rtl::math::approxAdd( fSum, fMem ), fCount); break;
4999 return fRes;
5002 void ScInterpreter::ScSumIf()
5004 PushDouble( IterateParametersIf( ifSUMIF));
5007 void ScInterpreter::ScAverageIf()
5009 PushDouble( IterateParametersIf( ifAVERAGEIF));
5012 void ScInterpreter::ScCountIf()
5014 if ( MustHaveParamCount( GetByte(), 2 ) )
5016 svl::SharedString aString;
5017 double fVal = 0.0;
5018 bool bIsString = true;
5019 switch ( GetStackType() )
5021 case svDoubleRef :
5022 case svSingleRef :
5024 ScAddress aAdr;
5025 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5027 PushInt(0);
5028 return ;
5030 ScRefCellValue aCell;
5031 aCell.assign(*pDok, aAdr);
5032 switch (aCell.meType)
5034 case CELLTYPE_VALUE :
5035 fVal = GetCellValue(aAdr, aCell);
5036 bIsString = false;
5037 break;
5038 case CELLTYPE_FORMULA :
5039 if (aCell.mpFormula->IsValue())
5041 fVal = GetCellValue(aAdr, aCell);
5042 bIsString = false;
5044 else
5045 GetCellString(aString, aCell);
5046 break;
5047 case CELLTYPE_STRING :
5048 case CELLTYPE_EDIT :
5049 GetCellString(aString, aCell);
5050 break;
5051 default:
5052 fVal = 0.0;
5053 bIsString = false;
5056 break;
5057 case svMatrix:
5058 case svExternalSingleRef:
5059 case svExternalDoubleRef:
5061 ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString);
5062 bIsString = ScMatrix::IsNonValueType( nType);
5064 break;
5065 case svString:
5066 aString = GetString();
5067 break;
5068 default:
5070 fVal = GetDouble();
5071 bIsString = false;
5074 double fCount = 0.0;
5075 short nParam = 1;
5076 size_t nRefInList = 0;
5077 while (nParam-- > 0)
5079 SCCOL nCol1 = 0;
5080 SCROW nRow1 = 0;
5081 SCTAB nTab1 = 0;
5082 SCCOL nCol2 = 0;
5083 SCROW nRow2 = 0;
5084 SCTAB nTab2 = 0;
5085 ScMatrixRef pQueryMatrix;
5086 switch ( GetStackType() )
5088 case svDoubleRef :
5089 case svRefList :
5091 ScRange aRange;
5092 PopDoubleRef( aRange, nParam, nRefInList);
5093 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5095 break;
5096 case svSingleRef :
5097 PopSingleRef( nCol1, nRow1, nTab1 );
5098 nCol2 = nCol1;
5099 nRow2 = nRow1;
5100 nTab2 = nTab1;
5101 break;
5102 case svMatrix:
5103 case svExternalSingleRef:
5104 case svExternalDoubleRef:
5106 pQueryMatrix = GetMatrix();
5107 if (!pQueryMatrix)
5109 PushIllegalParameter();
5110 return;
5112 nCol1 = 0;
5113 nRow1 = 0;
5114 nTab1 = 0;
5115 SCSIZE nC, nR;
5116 pQueryMatrix->GetDimensions( nC, nR);
5117 nCol2 = static_cast<SCCOL>(nC - 1);
5118 nRow2 = static_cast<SCROW>(nR - 1);
5119 nTab2 = 0;
5121 break;
5122 default:
5123 PushIllegalParameter();
5124 return ;
5126 if ( nTab1 != nTab2 )
5128 PushIllegalParameter();
5129 return;
5131 if (nCol1 > nCol2)
5133 PushIllegalParameter();
5134 return;
5136 if (nGlobalError == 0)
5138 ScQueryParam rParam;
5139 rParam.nRow1 = nRow1;
5140 rParam.nRow2 = nRow2;
5142 ScQueryEntry& rEntry = rParam.GetEntry(0);
5143 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5144 rEntry.bDoQuery = true;
5145 if (!bIsString)
5147 rItem.meType = ScQueryEntry::ByValue;
5148 rItem.mfVal = fVal;
5149 rEntry.eOp = SC_EQUAL;
5151 else
5153 rParam.FillInExcelSyntax(pDok->GetSharedStringPool(), aString.getString(), 0, pFormatter);
5154 if (rItem.meType == ScQueryEntry::ByString)
5155 rParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
5157 rParam.nCol1 = nCol1;
5158 rParam.nCol2 = nCol2;
5159 rEntry.nField = nCol1;
5160 if (pQueryMatrix)
5162 // Never case-sensitive.
5163 sc::CompareOptions aOptions( pDok, rEntry, rParam.bRegExp);
5164 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5165 if (nGlobalError || !pResultMatrix)
5167 PushIllegalParameter();
5168 return;
5171 SCSIZE nSize = pResultMatrix->GetElementCount();
5172 for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
5174 if (pResultMatrix->IsValue( nIndex) &&
5175 pResultMatrix->GetDouble( nIndex))
5176 ++fCount;
5179 else
5181 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false);
5182 // Keep Entry.nField in iterator on column change
5183 aCellIter.SetAdvanceQueryParamEntryField( true );
5184 if ( aCellIter.GetFirst() )
5188 fCount++;
5189 } while ( aCellIter.GetNext() );
5193 else
5195 PushIllegalParameter();
5196 return;
5199 PushDouble(fCount);
5203 double ScInterpreter::IterateParametersIfs( ScIterFuncIfs eFunc )
5205 sal_uInt8 nParamCount = GetByte();
5206 sal_uInt8 nQueryCount = nParamCount / 2;
5208 bool bCheck;
5209 if ( eFunc == ifCOUNTIFS )
5210 bCheck = (nParamCount >= 2) && (nParamCount % 2 == 0);
5211 else
5212 bCheck = (nParamCount >= 3) && (nParamCount % 2 == 1);
5214 if ( !bCheck )
5216 SetError( errParameterExpected);
5218 else
5220 std::vector<sal_uInt8> aResArray;
5221 size_t nRowSize = 0;
5222 size_t nColSize = 0;
5223 double fVal = 0.0;
5224 double fSum = 0.0;
5225 double fMem = 0.0;
5226 double fRes = 0.0;
5227 double fCount = 0.0;
5228 short nParam = 1;
5229 size_t nRefInList = 0;
5230 SCCOL nDimensionCols = 0;
5231 SCROW nDimensionRows = 0;
5233 while (nParamCount > 1 && !nGlobalError)
5235 // take criteria
5236 svl::SharedString aString;
5237 fVal = 0.0;
5238 bool bIsString = true;
5239 switch ( GetStackType() )
5241 case svDoubleRef :
5242 case svSingleRef :
5244 ScAddress aAdr;
5245 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5246 return 0;
5248 ScRefCellValue aCell;
5249 aCell.assign(*pDok, aAdr);
5250 switch (aCell.meType)
5252 case CELLTYPE_VALUE :
5253 fVal = GetCellValue(aAdr, aCell);
5254 bIsString = false;
5255 break;
5256 case CELLTYPE_FORMULA :
5257 if (aCell.mpFormula->IsValue())
5259 fVal = GetCellValue(aAdr, aCell);
5260 bIsString = false;
5262 else
5263 GetCellString(aString, aCell);
5264 break;
5265 case CELLTYPE_STRING :
5266 case CELLTYPE_EDIT :
5267 GetCellString(aString, aCell);
5268 break;
5269 default:
5270 fVal = 0.0;
5271 bIsString = false;
5274 break;
5275 case svString:
5276 aString = GetString();
5277 break;
5278 case svMatrix :
5279 case svExternalDoubleRef:
5281 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5282 bIsString = ScMatrix::IsNonValueType( nType);
5284 break;
5285 case svExternalSingleRef:
5287 ScExternalRefCache::TokenRef pToken;
5288 PopExternalSingleRef(pToken);
5289 if (pToken)
5291 if (pToken->GetType() == svDouble)
5293 fVal = pToken->GetDouble();
5294 bIsString = false;
5296 else
5297 aString = pToken->GetString();
5300 break;
5301 default:
5303 fVal = GetDouble();
5304 bIsString = false;
5308 if (nGlobalError)
5309 return 0; // and bail out, no need to evaluate other arguments
5311 // take range
5312 nParam = 1;
5313 nRefInList = 0;
5314 SCCOL nCol1 = 0;
5315 SCROW nRow1 = 0;
5316 SCTAB nTab1 = 0;
5317 SCCOL nCol2 = 0;
5318 SCROW nRow2 = 0;
5319 SCTAB nTab2 = 0;
5320 ScMatrixRef pQueryMatrix;
5321 switch ( GetStackType() )
5323 case svRefList :
5325 ScRange aRange;
5326 PopDoubleRef( aRange, nParam, nRefInList);
5327 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5329 break;
5330 case svDoubleRef :
5331 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
5332 break;
5333 case svSingleRef :
5334 PopSingleRef( nCol1, nRow1, nTab1 );
5335 nCol2 = nCol1;
5336 nRow2 = nRow1;
5337 nTab2 = nTab1;
5338 break;
5339 case svMatrix:
5340 case svExternalSingleRef:
5341 case svExternalDoubleRef:
5343 pQueryMatrix = PopMatrix();
5344 if (!pQueryMatrix)
5346 SetError( errIllegalParameter);
5347 return 0;
5349 nCol1 = 0;
5350 nRow1 = 0;
5351 nTab1 = 0;
5352 SCSIZE nC, nR;
5353 pQueryMatrix->GetDimensions( nC, nR);
5354 nCol2 = static_cast<SCCOL>(nC - 1);
5355 nRow2 = static_cast<SCROW>(nR - 1);
5356 nTab2 = 0;
5358 break;
5359 default:
5360 SetError( errIllegalParameter);
5361 return 0;
5363 if ( nTab1 != nTab2 )
5365 SetError( errIllegalArgument);
5366 return 0;
5369 // All reference ranges must be of same dimension and size.
5370 if (!nDimensionCols)
5371 nDimensionCols = nCol2 - nCol1 + 1;
5372 if (!nDimensionRows)
5373 nDimensionRows = nRow2 - nRow1 + 1;
5374 if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1)))
5376 SetError ( errIllegalArgument);
5377 return 0;
5380 // recalculate matrix values
5381 if (nGlobalError)
5382 return 0;
5384 // initialize temporary result matrix
5385 if (aResArray.empty())
5387 nColSize = nCol2 - nCol1 + 1;
5388 nRowSize = nRow2 - nRow1 + 1;
5389 aResArray.resize(nColSize*nRowSize, 0);
5392 ScQueryParam rParam;
5393 rParam.nRow1 = nRow1;
5394 rParam.nRow2 = nRow2;
5396 ScQueryEntry& rEntry = rParam.GetEntry(0);
5397 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5398 rEntry.bDoQuery = true;
5399 if (!bIsString)
5401 rItem.meType = ScQueryEntry::ByValue;
5402 rItem.mfVal = fVal;
5403 rEntry.eOp = SC_EQUAL;
5405 else
5407 rParam.FillInExcelSyntax(pDok->GetSharedStringPool(), aString.getString(), 0, pFormatter);
5408 if (rItem.meType == ScQueryEntry::ByString)
5409 rParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
5411 ScAddress aAdr;
5412 aAdr.SetTab( nTab1 );
5413 rParam.nCol1 = nCol1;
5414 rParam.nCol2 = nCol2;
5415 rEntry.nField = nCol1;
5416 SCsCOL nColDiff = -nCol1;
5417 SCsROW nRowDiff = -nRow1;
5418 if (pQueryMatrix)
5420 // Never case-sensitive.
5421 sc::CompareOptions aOptions( pDok, rEntry, rParam.bRegExp);
5422 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5423 if (nGlobalError || !pResultMatrix)
5425 SetError( errIllegalParameter);
5426 return 0;
5429 // result matrix is filled with boolean values.
5430 std::vector<double> aResValues;
5431 pResultMatrix->GetDoubleArray(aResValues, true);
5432 if (aResArray.size() != aResValues.size())
5434 SetError( errIllegalParameter);
5435 return 0;
5438 std::vector<sal_uInt8>::iterator itRes = aResArray.begin(), itResEnd = aResArray.end();
5439 std::vector<double>::const_iterator itThisRes = aResValues.begin();
5440 for (; itRes != itResEnd; ++itRes, ++itThisRes)
5441 *itRes += *itThisRes;
5443 else
5445 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false);
5446 // Increment Entry.nField in iterator when switching to next column.
5447 aCellIter.SetAdvanceQueryParamEntryField( true );
5448 if ( aCellIter.GetFirst() )
5452 size_t nC = aCellIter.GetCol() + nColDiff;
5453 size_t nR = aCellIter.GetRow() + nRowDiff;
5454 ++aResArray[nC*nRowSize+nR];
5455 } while ( aCellIter.GetNext() );
5458 nParamCount -= 2;
5461 if (nGlobalError)
5462 return 0; // bail out
5464 // main range - only for AVERAGEIFS and SUMIFS
5465 if (nParamCount == 1)
5467 nParam = 1;
5468 nRefInList = 0;
5469 bool bNull = true;
5470 SCCOL nMainCol1 = 0;
5471 SCROW nMainRow1 = 0;
5472 SCTAB nMainTab1 = 0;
5473 SCCOL nMainCol2 = 0;
5474 SCROW nMainRow2 = 0;
5475 SCTAB nMainTab2 = 0;
5476 ScMatrixRef pMainMatrix;
5477 switch ( GetStackType() )
5479 case svRefList :
5481 ScRange aRange;
5482 PopDoubleRef( aRange, nParam, nRefInList);
5483 aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2);
5485 break;
5486 case svDoubleRef :
5487 PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 );
5488 break;
5489 case svSingleRef :
5490 PopSingleRef( nMainCol1, nMainRow1, nMainTab1 );
5491 nMainCol2 = nMainCol1;
5492 nMainRow2 = nMainRow1;
5493 nMainTab2 = nMainTab1;
5494 break;
5495 case svMatrix:
5496 case svExternalSingleRef:
5497 case svExternalDoubleRef:
5499 pMainMatrix = PopMatrix();
5500 if (!pMainMatrix)
5502 SetError( errIllegalParameter);
5503 return 0;
5505 nMainCol1 = 0;
5506 nMainRow1 = 0;
5507 nMainTab1 = 0;
5508 SCSIZE nC, nR;
5509 pMainMatrix->GetDimensions( nC, nR);
5510 nMainCol2 = static_cast<SCCOL>(nC - 1);
5511 nMainRow2 = static_cast<SCROW>(nR - 1);
5512 nMainTab2 = 0;
5514 break;
5515 default:
5516 SetError( errIllegalParameter);
5517 return 0;
5519 if ( nMainTab1 != nMainTab2 )
5521 SetError( errIllegalArgument);
5522 return 0;
5525 // All reference ranges must be of same dimension and size.
5526 if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1)))
5528 SetError ( errIllegalArgument);
5529 return 0;
5532 if (nGlobalError)
5533 return 0; // bail out
5535 // end-result calculation
5536 ScAddress aAdr;
5537 aAdr.SetTab( nMainTab1 );
5538 if (pMainMatrix)
5540 std::vector<double> aMainValues;
5541 pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's.
5542 if (aResArray.size() != aMainValues.size())
5544 SetError( errIllegalArgument);
5545 return 0;
5548 std::vector<sal_uInt8>::const_iterator itRes = aResArray.begin(), itResEnd = aResArray.end();
5549 std::vector<double>::const_iterator itMain = aMainValues.begin();
5550 for (; itRes != itResEnd; ++itRes, ++itMain)
5552 if (*itRes != nQueryCount)
5553 continue;
5555 fVal = *itMain;
5556 if (GetDoubleErrorValue(fVal) == errElementNaN)
5557 continue;
5559 ++fCount;
5560 if (bNull && fVal != 0.0)
5562 bNull = false;
5563 fMem = fVal;
5565 else
5566 fSum += fVal;
5569 else
5571 std::vector<sal_uInt8>::const_iterator itRes = aResArray.begin();
5572 for (size_t nCol = 0; nCol < nColSize; ++nCol)
5574 for (size_t nRow = 0; nRow < nRowSize; ++nRow, ++itRes)
5576 if (*itRes == nQueryCount)
5578 aAdr.SetCol( static_cast<SCCOL>(nCol) + nMainCol1);
5579 aAdr.SetRow( static_cast<SCROW>(nRow) + nMainRow1);
5580 ScRefCellValue aCell;
5581 aCell.assign(*pDok, aAdr);
5582 if (aCell.hasNumeric())
5584 fVal = GetCellValue(aAdr, aCell);
5585 ++fCount;
5586 if ( bNull && fVal != 0.0 )
5588 bNull = false;
5589 fMem = fVal;
5591 else
5592 fSum += fVal;
5599 else
5601 std::vector<sal_uInt8>::const_iterator itRes = aResArray.begin(), itResEnd = aResArray.end();
5602 for (; itRes != itResEnd; ++itRes)
5603 if (*itRes == nQueryCount)
5604 ++fCount;
5607 switch( eFunc )
5609 case ifSUMIFS: fRes = ::rtl::math::approxAdd( fSum, fMem ); break;
5610 case ifAVERAGEIFS: fRes = div( ::rtl::math::approxAdd( fSum, fMem ), fCount); break;
5611 case ifCOUNTIFS: fRes = fCount; break;
5612 default: ; // nothing
5614 return fRes;
5616 return 0;
5619 void ScInterpreter::ScSumIfs()
5621 PushDouble( IterateParametersIfs( ifSUMIFS));
5624 void ScInterpreter::ScAverageIfs()
5626 PushDouble( IterateParametersIfs( ifAVERAGEIFS));
5629 void ScInterpreter::ScCountIfs()
5631 PushDouble( IterateParametersIfs( ifCOUNTIFS));
5634 void ScInterpreter::ScLookup()
5636 sal_uInt8 nParamCount = GetByte();
5637 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5638 return ;
5640 ScMatrixRef pDataMat = NULL, pResMat = NULL;
5641 SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0;
5642 SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0;
5643 SCTAB nTab1 = 0, nResTab = 0;
5644 SCSIZE nLenMajor = 0; // length of major direction
5645 bool bVertical = true; // whether to lookup vertically or horizontally
5647 // The third parameter, result array, for double, string and single reference.
5648 double fResVal = 0.0;
5649 svl::SharedString aResStr;
5650 ScAddress aResAdr;
5651 StackVar eResArrayType = svUnknown;
5653 if (nParamCount == 3)
5655 eResArrayType = GetStackType();
5656 switch (eResArrayType)
5658 case svDoubleRef:
5660 SCTAB nTabJunk;
5661 PopDoubleRef(nResCol1, nResRow1, nResTab,
5662 nResCol2, nResRow2, nTabJunk);
5663 if (nResTab != nTabJunk ||
5664 ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0))
5666 // The result array must be a vector.
5667 PushIllegalParameter();
5668 return;
5671 break;
5672 case svMatrix:
5673 case svExternalSingleRef:
5674 case svExternalDoubleRef:
5676 pResMat = GetMatrix();
5677 if (!pResMat)
5679 PushIllegalParameter();
5680 return;
5682 SCSIZE nC, nR;
5683 pResMat->GetDimensions(nC, nR);
5684 if (nC != 1 && nR != 1)
5686 // Result matrix must be a vector.
5687 PushIllegalParameter();
5688 return;
5691 break;
5692 case svDouble:
5693 fResVal = GetDouble();
5694 break;
5695 case svString:
5696 aResStr = GetString();
5697 break;
5698 case svSingleRef:
5699 PopSingleRef( aResAdr );
5700 break;
5701 default:
5702 PushIllegalParameter();
5703 return;
5707 // For double, string and single reference.
5708 double fDataVal = 0.0;
5709 svl::SharedString aDataStr;
5710 ScAddress aDataAdr;
5711 bool bValueData = false;
5713 // Get the data-result range and also determine whether this is vertical
5714 // lookup or horizontal lookup.
5716 StackVar eDataArrayType = GetStackType();
5717 switch (eDataArrayType)
5719 case svDoubleRef:
5721 SCTAB nTabJunk;
5722 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk);
5723 if (nTab1 != nTabJunk)
5725 PushIllegalParameter();
5726 return;
5728 bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1);
5729 nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1;
5731 break;
5732 case svMatrix:
5733 case svExternalSingleRef:
5734 case svExternalDoubleRef:
5736 pDataMat = GetMatrix();
5737 if (!pDataMat)
5739 PushIllegalParameter();
5740 return;
5743 SCSIZE nC, nR;
5744 pDataMat->GetDimensions(nC, nR);
5745 bVertical = (nR >= nC);
5746 nLenMajor = bVertical ? nR : nC;
5748 break;
5749 case svDouble:
5751 fDataVal = GetDouble();
5752 bValueData = true;
5754 break;
5755 case svString:
5757 aDataStr = GetString();
5759 break;
5760 case svSingleRef:
5762 PopSingleRef( aDataAdr );
5763 ScRefCellValue aCell;
5764 aCell.assign(*pDok, aDataAdr);
5765 if (aCell.hasEmptyValue())
5767 // Empty cells aren't found anywhere, bail out early.
5768 SetError( NOTAVAILABLE);
5770 else if (aCell.hasNumeric())
5772 fDataVal = GetCellValue(aDataAdr, aCell);
5773 bValueData = true;
5775 else
5776 GetCellString(aDataStr, aCell);
5778 break;
5779 default:
5780 SetError( errIllegalParameter);
5783 if (nGlobalError)
5785 PushError( nGlobalError);
5786 return;
5789 // Get the lookup value.
5791 ScQueryParam aParam;
5792 ScQueryEntry& rEntry = aParam.GetEntry(0);
5793 if ( !FillEntry(rEntry) )
5794 return;
5796 if ( eDataArrayType == svDouble || eDataArrayType == svString ||
5797 eDataArrayType == svSingleRef )
5799 // Delta position for a single value is always 0.
5801 // Found if data <= query, but not if query is string and found data is
5802 // numeric or vice versa. This is how Excel does it but doesn't
5803 // document it.
5805 bool bFound = false;
5806 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5808 if ( bValueData )
5810 if (rItem.meType == ScQueryEntry::ByString)
5811 bFound = false;
5812 else
5813 bFound = (fDataVal <= rItem.mfVal);
5815 else
5817 if (rItem.meType != ScQueryEntry::ByString)
5818 bFound = false;
5819 else
5820 bFound = (ScGlobal::GetCollator()->compareString(aDataStr.getString(), rItem.maString.getString()) <= 0);
5823 if (!bFound)
5825 PushNA();
5826 return;
5829 if (pResMat)
5831 if (pResMat->IsValue( 0, 0 ))
5832 PushDouble(pResMat->GetDouble( 0, 0 ));
5833 else
5834 PushString(pResMat->GetString(0, 0));
5836 else if (nParamCount == 3)
5838 switch (eResArrayType)
5840 case svDouble:
5841 PushDouble( fResVal );
5842 break;
5843 case svString:
5844 PushString( aResStr );
5845 break;
5846 case svDoubleRef:
5847 aResAdr.Set( nResCol1, nResRow1, nResTab);
5848 // fallthru
5849 case svSingleRef:
5850 PushCellResultToken( true, aResAdr, NULL, NULL);
5851 break;
5852 default:
5853 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eResArrayType, single value data");
5856 else
5858 switch (eDataArrayType)
5860 case svDouble:
5861 PushDouble( fDataVal );
5862 break;
5863 case svString:
5864 PushString( aDataStr );
5865 break;
5866 case svSingleRef:
5867 PushCellResultToken( true, aDataAdr, NULL, NULL);
5868 break;
5869 default:
5870 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eDataArrayType, single value data");
5873 return;
5876 // Now, perform the search to compute the delta position (nDelta).
5878 if (pDataMat)
5880 // Data array is given as a matrix.
5881 rEntry.bDoQuery = true;
5882 rEntry.eOp = SC_LESS_EQUAL;
5883 bool bFound = false;
5885 SCSIZE nC, nR;
5886 pDataMat->GetDimensions(nC, nR);
5888 // In case of non-vector matrix, only search the first row or column.
5889 ScMatrixRef pDataMat2;
5890 if (bVertical)
5892 ScMatrixRef pTempMat(new ScMatrix(1, nR, 0.0));
5893 for (SCSIZE i = 0; i < nR; ++i)
5894 if (pDataMat->IsValue(0, i))
5895 pTempMat->PutDouble(pDataMat->GetDouble(0, i), 0, i);
5896 else
5897 pTempMat->PutString(pDataMat->GetString(0, i), 0, i);
5898 pDataMat2 = pTempMat;
5900 else
5902 ScMatrixRef pTempMat(new ScMatrix(nC, 1, 0.0));
5903 for (SCSIZE i = 0; i < nC; ++i)
5904 if (pDataMat->IsValue(i, 0))
5905 pTempMat->PutDouble(pDataMat->GetDouble(i, 0), i, 0);
5906 else
5907 pTempMat->PutString(pDataMat->GetString(i, 0), i, 0);
5908 pDataMat2 = pTempMat;
5911 VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical);
5913 // binary search for non-equality mode (the source data is
5914 // assumed to be sorted in ascending order).
5916 SCCOLROW nDelta = -1;
5918 SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0;
5919 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
5921 SCSIZE nMid = nFirst + nLen/2;
5922 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry);
5923 if (nCmp == 0)
5925 // exact match. find the last item with the same value.
5926 lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor, false);
5927 nDelta = nMid;
5928 bFound = true;
5929 break;
5932 if (nLen == 1) // first and last items are next to each other.
5934 nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1;
5935 // If already the 1st item is greater there's nothing found.
5936 bFound = (nDelta >= 0);
5937 break;
5940 if (nCmp < 0)
5941 nFirst = nMid;
5942 else
5943 nLast = nMid;
5946 if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item
5948 sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry);
5949 if (nCmp <= 0)
5951 // either the last item is an exact match or the real
5952 // hit is beyond the last item.
5953 nDelta += 1;
5954 bFound = true;
5957 else if (nDelta > 0) // valid hit must be 2nd item or higher
5959 // non-exact match
5960 bFound = true;
5963 // With 0-9 < A-Z, if query is numeric and data found is string, or
5964 // vice versa, the (yet another undocumented) Excel behavior is to
5965 // return #N/A instead.
5967 if (bFound)
5969 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
5970 SCCOLROW i = nDelta;
5971 SCSIZE n = aMatAcc.GetElementCount();
5972 if (static_cast<SCSIZE>(i) >= n)
5973 i = static_cast<SCCOLROW>(n);
5974 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
5975 if (bByString == aMatAcc.IsValue(i))
5976 bFound = false;
5979 if (!bFound)
5981 PushNA();
5982 return;
5985 // Now that we've found the delta, push the result back to the cell.
5987 if (pResMat)
5989 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
5990 // result array is matrix.
5991 if (static_cast<SCSIZE>(nDelta) >= aResMatAcc.GetElementCount())
5993 PushNA();
5994 return;
5996 if (aResMatAcc.IsValue(nDelta))
5997 PushDouble(aResMatAcc.GetDouble(nDelta));
5998 else
5999 PushString(aResMatAcc.GetString(nDelta));
6001 else if (nParamCount == 3)
6003 // result array is cell range.
6004 ScAddress aAdr;
6005 aAdr.SetTab(nResTab);
6006 bool bResVertical = (nResRow2 - nResRow1) > 0;
6007 if (bResVertical)
6009 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
6010 if (nTempRow > MAXROW)
6012 PushDouble(0);
6013 return;
6015 aAdr.SetCol(nResCol1);
6016 aAdr.SetRow(nTempRow);
6018 else
6020 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
6021 if (nTempCol > MAXCOL)
6023 PushDouble(0);
6024 return;
6026 aAdr.SetCol(nTempCol);
6027 aAdr.SetRow(nResRow1);
6029 PushCellResultToken(true, aAdr, NULL, NULL);
6031 else
6033 // no result array. Use the data array to get the final value from.
6034 if (bVertical)
6036 if (pDataMat->IsValue(nC-1, nDelta))
6037 PushDouble(pDataMat->GetDouble(nC-1, nDelta));
6038 else
6039 PushString(pDataMat->GetString(nC-1, nDelta));
6041 else
6043 if (pDataMat->IsValue(nDelta, nR-1))
6044 PushDouble(pDataMat->GetDouble(nDelta, nR-1));
6045 else
6046 PushString(pDataMat->GetString(nDelta, nR-1));
6050 return;
6053 // Perform cell range search.
6055 aParam.nCol1 = nCol1;
6056 aParam.nRow1 = nRow1;
6057 aParam.nCol2 = bVertical ? nCol1 : nCol2;
6058 aParam.nRow2 = bVertical ? nRow2 : nRow1;
6059 aParam.bByRow = bVertical;
6061 rEntry.bDoQuery = true;
6062 rEntry.eOp = SC_LESS_EQUAL;
6063 rEntry.nField = nCol1;
6064 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6065 if (rItem.meType == ScQueryEntry::ByString)
6066 aParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
6068 ScQueryCellIterator aCellIter(pDok, nTab1, aParam, false);
6069 SCCOL nC;
6070 SCROW nR;
6071 // Advance Entry.nField in iterator upon switching columns if
6072 // lookup in row.
6073 aCellIter.SetAdvanceQueryParamEntryField(!bVertical);
6074 if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) )
6076 PushNA();
6077 return;
6080 SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1);
6082 if (pResMat)
6084 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
6085 // Use the matrix result array.
6086 if (aResMatAcc.IsValue(nDelta))
6087 PushDouble(aResMatAcc.GetDouble(nDelta));
6088 else
6089 PushString(aResMatAcc.GetString(nDelta));
6091 else if (nParamCount == 3)
6093 switch (eResArrayType)
6095 case svDoubleRef:
6097 // Use the result array vector. Note that the result array is assumed
6098 // to be a vector (i.e. 1-dimensinoal array).
6100 ScAddress aAdr;
6101 aAdr.SetTab(nResTab);
6102 bool bResVertical = (nResRow2 - nResRow1) > 0;
6103 if (bResVertical)
6105 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
6106 if (nTempRow > MAXROW)
6108 PushDouble(0);
6109 return;
6111 aAdr.SetCol(nResCol1);
6112 aAdr.SetRow(nTempRow);
6114 else
6116 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
6117 if (nTempCol > MAXCOL)
6119 PushDouble(0);
6120 return;
6122 aAdr.SetCol(nTempCol);
6123 aAdr.SetRow(nResRow1);
6125 PushCellResultToken( true, aAdr, NULL, NULL);
6127 break;
6128 case svDouble:
6129 case svString:
6130 case svSingleRef:
6132 if (nDelta != 0)
6133 PushNA();
6134 else
6136 switch (eResArrayType)
6138 case svDouble:
6139 PushDouble( fResVal );
6140 break;
6141 case svString:
6142 PushString( aResStr );
6143 break;
6144 case svSingleRef:
6145 PushCellResultToken( true, aResAdr, NULL, NULL);
6146 break;
6147 default:
6148 ; // nothing
6152 break;
6153 default:
6154 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eResArrayType, range search");
6157 else
6159 // Regardless of whether or not the result array exists, the last
6160 // array is always used as the "result" array.
6162 ScAddress aAdr;
6163 aAdr.SetTab(nTab1);
6164 if (bVertical)
6166 SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta);
6167 if (nTempRow > MAXROW)
6169 PushDouble(0);
6170 return;
6172 aAdr.SetCol(nCol2);
6173 aAdr.SetRow(nTempRow);
6175 else
6177 SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta);
6178 if (nTempCol > MAXCOL)
6180 PushDouble(0);
6181 return;
6183 aAdr.SetCol(nTempCol);
6184 aAdr.SetRow(nRow2);
6186 PushCellResultToken(true, aAdr, NULL, NULL);
6190 void ScInterpreter::ScHLookup()
6192 CalculateLookup(true);
6195 void ScInterpreter::CalculateLookup(bool bHLookup)
6197 sal_uInt8 nParamCount = GetByte();
6198 if (!MustHaveParamCount(nParamCount, 3, 4))
6199 return;
6201 // Optional 4th argument to declare whether or not the range is sorted.
6202 bool bSorted = true;
6203 if (nParamCount == 4)
6204 bSorted = GetBool();
6206 // Index of column to search.
6207 double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0;
6209 ScMatrixRef pMat = NULL;
6210 SCSIZE nC = 0, nR = 0;
6211 SCCOL nCol1 = 0;
6212 SCROW nRow1 = 0;
6213 SCTAB nTab1 = 0;
6214 SCCOL nCol2 = 0;
6215 SCROW nRow2 = 0;
6216 SCTAB nTab2;
6217 StackVar eType = GetStackType();
6218 if (eType == svDoubleRef)
6220 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6221 if (nTab1 != nTab2)
6223 PushIllegalParameter();
6224 return;
6227 else if (eType == svSingleRef)
6229 PopSingleRef(nCol1, nRow1, nTab1);
6230 nCol2 = nCol1;
6231 nRow2 = nRow1;
6233 else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef)
6235 pMat = GetMatrix();
6237 if (pMat)
6238 pMat->GetDimensions(nC, nR);
6239 else
6241 PushIllegalParameter();
6242 return;
6245 else
6247 PushIllegalParameter();
6248 return;
6251 if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) )
6253 PushIllegalArgument();
6254 return;
6257 SCROW nZIndex = static_cast<SCROW>(fIndex);
6258 SCCOL nSpIndex = static_cast<SCCOL>(fIndex);
6260 if (!pMat)
6262 nZIndex += nRow1; // Wertzeile
6263 nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column
6266 if (nGlobalError)
6268 PushIllegalParameter();
6269 return;
6272 ScQueryParam aParam;
6273 aParam.nCol1 = nCol1;
6274 aParam.nRow1 = nRow1;
6275 if ( bHLookup )
6277 aParam.nCol2 = nCol2;
6278 aParam.nRow2 = nRow1; // nur in der ersten Zeile suchen
6279 aParam.bByRow = false;
6281 else
6283 aParam.nCol2 = nCol1; // nur in der ersten Spalte suchen
6284 aParam.nRow2 = nRow2;
6285 aParam.nTab = nTab1;
6288 ScQueryEntry& rEntry = aParam.GetEntry(0);
6289 rEntry.bDoQuery = true;
6290 if ( bSorted )
6291 rEntry.eOp = SC_LESS_EQUAL;
6292 if ( !FillEntry(rEntry) )
6293 return;
6295 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6296 if (rItem.meType == ScQueryEntry::ByString)
6297 aParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
6298 if (pMat)
6300 SCSIZE nMatCount = bHLookup ? nC : nR;
6301 SCSIZE nDelta = SCSIZE_MAX;
6302 if (rItem.meType == ScQueryEntry::ByString)
6304 //!!!!!!!
6305 //TODO: enable regex on matrix strings
6306 //!!!!!!!
6307 svl::SharedString aParamStr = rItem.maString;
6308 if ( bSorted )
6310 static CollatorWrapper* pCollator = ScGlobal::GetCollator();
6311 for (SCSIZE i = 0; i < nMatCount; i++)
6313 if (bHLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i))
6315 sal_Int32 nRes =
6316 pCollator->compareString(
6317 bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString());
6318 if (nRes <= 0)
6319 nDelta = i;
6320 else if (i>0) // #i2168# ignore first mismatch
6321 i = nMatCount+1;
6323 else
6324 nDelta = i;
6327 else
6329 if (bHLookup)
6331 for (SCSIZE i = 0; i < nMatCount; i++)
6333 if (pMat->IsString(i, 0))
6335 if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase())
6337 nDelta = i;
6338 i = nMatCount + 1;
6343 else
6345 nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0);
6349 else
6351 if ( bSorted )
6353 // #i2168# ignore strings
6354 for (SCSIZE i = 0; i < nMatCount; i++)
6356 if (!(bHLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i)))
6358 if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal)
6359 nDelta = i;
6360 else
6361 i = nMatCount+1;
6365 else
6367 if (bHLookup)
6369 for (SCSIZE i = 0; i < nMatCount; i++)
6371 if (! pMat->IsString(i, 0) )
6373 if ( pMat->GetDouble(i,0) == rItem.mfVal)
6375 nDelta = i;
6376 i = nMatCount + 1;
6381 else
6383 nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0);
6387 if ( nDelta != SCSIZE_MAX )
6389 SCSIZE nX = static_cast<SCSIZE>(nSpIndex);
6390 SCSIZE nY = nDelta;
6391 if ( bHLookup )
6393 nX = nDelta;
6394 nY = static_cast<SCSIZE>(nZIndex);
6396 if ( pMat->IsString( nX, nY) )
6397 PushString(pMat->GetString( nX,nY).getString());
6398 else
6399 PushDouble(pMat->GetDouble( nX,nY));
6401 else
6402 PushNA();
6404 else
6406 rEntry.nField = nCol1;
6407 bool bFound = false;
6408 SCCOL nCol = 0;
6409 SCROW nRow = 0;
6410 if ( bSorted )
6411 rEntry.eOp = SC_LESS_EQUAL;
6412 if ( bHLookup )
6414 ScQueryCellIterator aCellIter(pDok, nTab1, aParam, false);
6415 // advance Entry.nField in Iterator upon switching columns
6416 aCellIter.SetAdvanceQueryParamEntryField( true );
6417 if ( bSorted )
6419 SCROW nRow1_temp;
6420 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp );
6422 else if ( aCellIter.GetFirst() )
6424 bFound = true;
6425 nCol = aCellIter.GetCol();
6427 nRow = nZIndex;
6429 else
6431 ScAddress aResultPos( nCol1, nRow1, nTab1);
6432 bFound = LookupQueryWithCache( aResultPos, aParam);
6433 nRow = aResultPos.Row();
6434 nCol = nSpIndex;
6437 if ( bFound )
6439 ScAddress aAdr( nCol, nRow, nTab1 );
6440 PushCellResultToken( true, aAdr, NULL, NULL);
6442 else
6443 PushNA();
6447 bool ScInterpreter::FillEntry(ScQueryEntry& rEntry)
6449 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6450 switch ( GetStackType() )
6452 case svDouble:
6454 rItem.meType = ScQueryEntry::ByValue;
6455 rItem.mfVal = GetDouble();
6457 break;
6458 case svString:
6460 rItem.meType = ScQueryEntry::ByString;
6461 rItem.maString = GetString();
6463 break;
6464 case svDoubleRef :
6465 case svSingleRef :
6467 ScAddress aAdr;
6468 if ( !PopDoubleRefOrSingleRef( aAdr ) )
6470 PushInt(0);
6471 return false;
6473 ScRefCellValue aCell;
6474 aCell.assign(*pDok, aAdr);
6475 if (aCell.hasNumeric())
6477 rItem.meType = ScQueryEntry::ByValue;
6478 rItem.mfVal = GetCellValue(aAdr, aCell);
6480 else
6482 GetCellString(rItem.maString, aCell);
6483 rItem.meType = ScQueryEntry::ByString;
6486 break;
6487 case svMatrix :
6489 svl::SharedString aStr;
6490 const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr);
6491 rItem.maString = aStr;
6492 rItem.meType = ScMatrix::IsNonValueType(nType) ?
6493 ScQueryEntry::ByString : ScQueryEntry::ByValue;
6495 break;
6496 default:
6498 PushIllegalParameter();
6499 return false;
6501 } // switch ( GetStackType() )
6502 return true;
6504 void ScInterpreter::ScVLookup()
6506 CalculateLookup(false);
6509 void ScInterpreter::ScSubTotal()
6511 sal_uInt8 nParamCount = GetByte();
6512 if ( MustHaveParamCountMin( nParamCount, 2 ) )
6514 // We must fish the 1st parameter deep from the stack! And push it on top.
6515 const FormulaToken* p = pStack[ sp - nParamCount ];
6516 PushTempToken( *p );
6517 int nFunc = (int) ::rtl::math::approxFloor( GetDouble() );
6518 mnSubTotalFlags |= SUBTOTAL_IGN_NESTED_ST_AG | SUBTOTAL_IGN_FILTERED;
6519 if (nFunc > 100)
6521 // For opcodes 101 through 111, we need to skip hidden cells.
6522 // Other than that these opcodes are identical to 1 through 11.
6523 mnSubTotalFlags |= SUBTOTAL_IGN_HIDDEN;
6524 nFunc -= 100;
6527 if ( nFunc < 1 || nFunc > 11 )
6528 PushIllegalArgument(); // simulate return on stack, not SetError(...)
6529 else
6531 cPar = nParamCount - 1;
6532 switch( nFunc )
6534 case SUBTOTAL_FUNC_AVE : ScAverage(); break;
6535 case SUBTOTAL_FUNC_CNT : ScCount(); break;
6536 case SUBTOTAL_FUNC_CNT2 : ScCount2(); break;
6537 case SUBTOTAL_FUNC_MAX : ScMax(); break;
6538 case SUBTOTAL_FUNC_MIN : ScMin(); break;
6539 case SUBTOTAL_FUNC_PROD : ScProduct(); break;
6540 case SUBTOTAL_FUNC_STD : ScStDev(); break;
6541 case SUBTOTAL_FUNC_STDP : ScStDevP(); break;
6542 case SUBTOTAL_FUNC_SUM : ScSum(); break;
6543 case SUBTOTAL_FUNC_VAR : ScVar(); break;
6544 case SUBTOTAL_FUNC_VARP : ScVarP(); break;
6545 default : PushIllegalArgument(); break;
6548 mnSubTotalFlags = 0x00;
6549 // Get rid of the 1st (fished) parameter.
6550 double nVal = GetDouble();
6551 Pop();
6552 PushDouble( nVal );
6556 void ScInterpreter::ScAggregate()
6558 sal_uInt8 nParamCount = GetByte();
6559 if ( MustHaveParamCountMin( nParamCount, 3 ) )
6561 // fish the 1st parameter from the stack and push it on top.
6562 const FormulaToken* p = pStack[ sp - nParamCount ];
6563 PushTempToken( *p );
6564 int nFunc = ( int ) ::rtl::math::approxFloor( GetDouble() );
6565 // fish the 2nd parameter from the stack and push it on top.
6566 const FormulaToken* p2 = pStack[ sp - ( nParamCount - 1 ) ];
6567 PushTempToken( *p2 );
6568 int nOption = ( int ) ::rtl::math::approxFloor( GetDouble() );
6570 if ( nFunc < 1 || nFunc > 19 )
6571 PushIllegalArgument();
6572 else
6574 switch ( nOption)
6576 case 0 : // ignore nested SUBTOTAL and AGGREGATE functions
6577 mnSubTotalFlags = SUBTOTAL_IGN_NESTED_ST_AG;
6578 break;
6579 case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions
6580 mnSubTotalFlags = SUBTOTAL_IGN_HIDDEN | SUBTOTAL_IGN_NESTED_ST_AG;
6581 break;
6582 case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions
6583 mnSubTotalFlags = SUBTOTAL_IGN_ERR_VAL | SUBTOTAL_IGN_NESTED_ST_AG;
6584 break;
6585 case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions
6586 mnSubTotalFlags = SUBTOTAL_IGN_HIDDEN | SUBTOTAL_IGN_ERR_VAL | SUBTOTAL_IGN_NESTED_ST_AG;
6587 break;
6588 case 4 : // ignore nothing
6589 mnSubTotalFlags = 0x00;
6590 break;
6591 case 5 : // ignore hidden rows
6592 mnSubTotalFlags = SUBTOTAL_IGN_HIDDEN ;
6593 break;
6594 case 6 : // ignore error values
6595 mnSubTotalFlags = SUBTOTAL_IGN_ERR_VAL ;
6596 break;
6597 case 7 : // ignore hidden rows and error values
6598 mnSubTotalFlags = SUBTOTAL_IGN_HIDDEN | SUBTOTAL_IGN_ERR_VAL ;
6599 break;
6600 default :
6601 PushIllegalArgument();
6602 return;
6605 cPar = nParamCount - 2;
6606 switch ( nFunc )
6608 case SUBTOTAL_FUNC_AVE : ScAverage(); break;
6609 case SUBTOTAL_FUNC_CNT : ScCount(); break;
6610 case SUBTOTAL_FUNC_CNT2 : ScCount2(); break;
6611 case SUBTOTAL_FUNC_MAX : ScMax(); break;
6612 case SUBTOTAL_FUNC_MIN : ScMin(); break;
6613 case SUBTOTAL_FUNC_PROD : ScProduct(); break;
6614 case SUBTOTAL_FUNC_STD : ScStDev(); break;
6615 case SUBTOTAL_FUNC_STDP : ScStDevP(); break;
6616 case SUBTOTAL_FUNC_SUM : ScSum(); break;
6617 case SUBTOTAL_FUNC_VAR : ScVar(); break;
6618 case SUBTOTAL_FUNC_VARP : ScVarP(); break;
6619 case AGGREGATE_FUNC_MEDIAN : ScMedian(); break;
6620 case AGGREGATE_FUNC_MODSNGL : ScModalValue(); break;
6621 case AGGREGATE_FUNC_LARGE : ScLarge(); break;
6622 case AGGREGATE_FUNC_SMALL : ScSmall(); break;
6623 case AGGREGATE_FUNC_PERCINC : ScPercentile( true ); break;
6624 case AGGREGATE_FUNC_QRTINC : ScQuartile( true ); break;
6625 case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break;
6626 case AGGREGATE_FUNC_QRTEXC : ScQuartile( false ); break;
6627 default : PushIllegalArgument(); break;
6629 mnSubTotalFlags = 0x00;
6631 double nVal = GetDouble();
6632 // Get rid of the 1st and 2nd (fished) parameters.
6633 Pop();
6634 Pop();
6635 PushDouble( nVal );
6639 ScDBQueryParamBase* ScInterpreter::GetDBParams( bool& rMissingField )
6641 bool bAllowMissingField = false;
6642 if ( rMissingField )
6644 bAllowMissingField = true;
6645 rMissingField = false;
6647 if ( GetByte() == 3 )
6649 // First, get the query criteria range.
6650 ::std::unique_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() );
6651 if (!pQueryRef.get())
6652 return NULL;
6654 bool bByVal = true;
6655 double nVal = 0.0;
6656 svl::SharedString aStr;
6657 ScRange aMissingRange;
6658 bool bRangeFake = false;
6659 switch (GetStackType())
6661 case svDouble :
6662 nVal = ::rtl::math::approxFloor( GetDouble() );
6663 if ( bAllowMissingField && nVal == 0.0 )
6664 rMissingField = true; // fake missing parameter
6665 break;
6666 case svString :
6667 bByVal = false;
6668 aStr = GetString();
6669 break;
6670 case svSingleRef :
6672 ScAddress aAdr;
6673 PopSingleRef( aAdr );
6674 ScRefCellValue aCell;
6675 aCell.assign(*pDok, aAdr);
6676 if (aCell.hasNumeric())
6677 nVal = GetCellValue(aAdr, aCell);
6678 else
6680 bByVal = false;
6681 GetCellString(aStr, aCell);
6684 break;
6685 case svDoubleRef :
6686 if ( bAllowMissingField )
6687 { // fake missing parameter for old SO compatibility
6688 bRangeFake = true;
6689 PopDoubleRef( aMissingRange );
6691 else
6693 PopError();
6694 SetError( errIllegalParameter );
6696 break;
6697 case svMissing :
6698 PopError();
6699 if ( bAllowMissingField )
6700 rMissingField = true;
6701 else
6702 SetError( errIllegalParameter );
6703 break;
6704 default:
6705 PopError();
6706 SetError( errIllegalParameter );
6709 if (nGlobalError)
6710 return NULL;
6712 unique_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() );
6714 if (nGlobalError || !pDBRef.get())
6715 return NULL;
6717 if ( bRangeFake )
6719 // range parameter must match entire database range
6720 if (pDBRef->isRangeEqual(aMissingRange))
6721 rMissingField = true;
6722 else
6723 SetError( errIllegalParameter );
6726 if (nGlobalError)
6727 return NULL;
6729 SCCOL nField = pDBRef->getFirstFieldColumn();
6730 if (rMissingField)
6731 ; // special case
6732 else if (bByVal)
6733 nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal));
6734 else
6736 sal_uInt16 nErr = 0;
6737 nField = pDBRef->findFieldColumn(aStr.getString(), &nErr);
6738 SetError(nErr);
6741 if (!ValidCol(nField))
6742 return NULL;
6744 unique_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) );
6746 if (pParam.get())
6748 // An allowed missing field parameter sets the result field
6749 // to any of the query fields, just to be able to return
6750 // some cell from the iterator.
6751 if ( rMissingField )
6752 nField = static_cast<SCCOL>(pParam->GetEntry(0).nField);
6753 pParam->mnField = nField;
6755 SCSIZE nCount = pParam->GetEntryCount();
6756 for ( SCSIZE i=0; i < nCount; i++ )
6758 ScQueryEntry& rEntry = pParam->GetEntry(i);
6759 if (!rEntry.bDoQuery)
6760 break;
6762 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6763 sal_uInt32 nIndex = 0;
6764 OUString aQueryStr = rItem.maString.getString();
6765 bool bNumber = pFormatter->IsNumberFormat(
6766 aQueryStr, nIndex, rItem.mfVal);
6767 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
6769 if (!bNumber && !pParam->bRegExp)
6770 pParam->bRegExp = MayBeRegExp(aQueryStr, pDok);
6772 return pParam.release();
6775 return NULL;
6778 void ScInterpreter::DBIterator( ScIterFunc eFunc )
6780 double nErg = 0.0;
6781 double fMem = 0.0;
6782 bool bNull = true;
6783 sal_uLong nCount = 0;
6784 bool bMissingField = false;
6785 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
6786 if (pQueryParam.get())
6788 if (!pQueryParam->IsValidFieldIndex())
6790 SetError(errNoValue);
6791 return;
6793 ScDBQueryDataIterator aValIter(pDok, pQueryParam.release());
6794 ScDBQueryDataIterator::Value aValue;
6795 if ( aValIter.GetFirst(aValue) && !aValue.mnError )
6797 switch( eFunc )
6799 case ifPRODUCT: nErg = 1; break;
6800 case ifMAX: nErg = -MAXDOUBLE; break;
6801 case ifMIN: nErg = MAXDOUBLE; break;
6802 default: ; // nothing
6806 nCount++;
6807 switch( eFunc )
6809 case ifAVERAGE:
6810 case ifSUM:
6811 if ( bNull && aValue.mfValue != 0.0 )
6813 bNull = false;
6814 fMem = aValue.mfValue;
6816 else
6817 nErg += aValue.mfValue;
6818 break;
6819 case ifSUMSQ: nErg += aValue.mfValue * aValue.mfValue; break;
6820 case ifPRODUCT: nErg *= aValue.mfValue; break;
6821 case ifMAX: if( aValue.mfValue > nErg ) nErg = aValue.mfValue; break;
6822 case ifMIN: if( aValue.mfValue < nErg ) nErg = aValue.mfValue; break;
6823 default: ; // nothing
6826 while ( aValIter.GetNext(aValue) && !aValue.mnError );
6828 SetError(aValue.mnError);
6830 else
6831 SetError( errIllegalParameter);
6832 switch( eFunc )
6834 case ifCOUNT: nErg = nCount; break;
6835 case ifSUM: nErg = ::rtl::math::approxAdd( nErg, fMem ); break;
6836 case ifAVERAGE: nErg = div(::rtl::math::approxAdd(nErg, fMem), nCount); break;
6837 default: ; // nothing
6839 PushDouble( nErg );
6842 void ScInterpreter::ScDBSum()
6844 DBIterator( ifSUM );
6847 void ScInterpreter::ScDBCount()
6849 bool bMissingField = true;
6850 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
6851 if (pQueryParam.get())
6853 sal_uLong nCount = 0;
6854 if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL )
6855 { // count all matching records
6856 // TODO: currently the QueryIterators only return cell pointers of
6857 // existing cells, so if a query matches an empty cell there's
6858 // nothing returned, and therefore not counted!
6859 // Since this has ever been the case and this code here only came
6860 // into existence to fix #i6899 and it never worked before we'll
6861 // have to live with it until we reimplement the iterators to also
6862 // return empty cells, which would mean to adapt all callers of
6863 // iterators.
6864 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get());
6865 p->nCol2 = p->nCol1; // Don't forget to select only one column.
6866 SCTAB nTab = p->nTab;
6867 // ScQueryCellIterator doesn't make use of ScDBQueryParamBase::mnField,
6868 // so the source range has to be restricted, like before the introduction
6869 // of ScDBQueryParamBase.
6870 p->nCol1 = p->nCol2 = p->mnField;
6871 ScQueryCellIterator aCellIter( pDok, nTab, *p);
6872 if ( aCellIter.GetFirst() )
6876 nCount++;
6877 } while ( aCellIter.GetNext() );
6880 else
6881 { // count only matching records with a value in the "result" field
6882 if (!pQueryParam->IsValidFieldIndex())
6884 SetError(errNoValue);
6885 return;
6887 ScDBQueryDataIterator aValIter( pDok, pQueryParam.release());
6888 ScDBQueryDataIterator::Value aValue;
6889 if ( aValIter.GetFirst(aValue) && !aValue.mnError )
6893 nCount++;
6895 while ( aValIter.GetNext(aValue) && !aValue.mnError );
6897 SetError(aValue.mnError);
6899 PushDouble( nCount );
6901 else
6902 PushIllegalParameter();
6905 void ScInterpreter::ScDBCount2()
6907 bool bMissingField = true;
6908 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
6909 if (pQueryParam.get())
6911 if (!pQueryParam->IsValidFieldIndex())
6913 SetError(errNoValue);
6914 return;
6916 sal_uLong nCount = 0;
6917 pQueryParam->mbSkipString = false;
6918 ScDBQueryDataIterator aValIter( pDok, pQueryParam.release());
6919 ScDBQueryDataIterator::Value aValue;
6920 if ( aValIter.GetFirst(aValue) && !aValue.mnError )
6924 nCount++;
6926 while ( aValIter.GetNext(aValue) && !aValue.mnError );
6928 SetError(aValue.mnError);
6929 PushDouble( nCount );
6931 else
6932 PushIllegalParameter();
6935 void ScInterpreter::ScDBAverage()
6937 DBIterator( ifAVERAGE );
6940 void ScInterpreter::ScDBMax()
6942 DBIterator( ifMAX );
6945 void ScInterpreter::ScDBMin()
6947 DBIterator( ifMIN );
6950 void ScInterpreter::ScDBProduct()
6952 DBIterator( ifPRODUCT );
6955 void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount )
6957 std::vector<double> values;
6958 double vSum = 0.0;
6959 double vMean = 0.0;
6961 rValCount = 0.0;
6962 double fSum = 0.0;
6963 bool bMissingField = false;
6964 unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
6965 if (pQueryParam.get())
6967 if (!pQueryParam->IsValidFieldIndex())
6969 SetError(errNoValue);
6970 return;
6972 ScDBQueryDataIterator aValIter(pDok, pQueryParam.release());
6973 ScDBQueryDataIterator::Value aValue;
6974 if (aValIter.GetFirst(aValue) && !aValue.mnError)
6978 rValCount++;
6979 values.push_back(aValue.mfValue);
6980 fSum += aValue.mfValue;
6982 while ((aValue.mnError == 0) && aValIter.GetNext(aValue));
6984 SetError(aValue.mnError);
6986 else
6987 SetError( errIllegalParameter);
6989 vMean = fSum / values.size();
6991 for (size_t i = 0; i < values.size(); i++)
6992 vSum += (values[i] - vMean) * (values[i] - vMean);
6994 rVal = vSum;
6997 void ScInterpreter::ScDBStdDev()
6999 double fVal, fCount;
7000 GetDBStVarParams( fVal, fCount );
7001 PushDouble( sqrt(fVal/(fCount-1)));
7004 void ScInterpreter::ScDBStdDevP()
7006 double fVal, fCount;
7007 GetDBStVarParams( fVal, fCount );
7008 PushDouble( sqrt(fVal/fCount));
7011 void ScInterpreter::ScDBVar()
7013 double fVal, fCount;
7014 GetDBStVarParams( fVal, fCount );
7015 PushDouble(fVal/(fCount-1));
7018 void ScInterpreter::ScDBVarP()
7020 double fVal, fCount;
7021 GetDBStVarParams( fVal, fCount );
7022 PushDouble(fVal/fCount);
7025 void ScInterpreter::ScIndirect()
7027 sal_uInt8 nParamCount = GetByte();
7028 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7030 // Reference address syntax for INDIRECT is configurable.
7031 FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax;
7032 if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
7033 // Use the current address syntax if unspecified.
7034 eConv = pDok->GetAddressConvention();
7036 // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible
7037 // to determine which syntax to use during doc import
7038 bool bTryXlA1 = (eConv == FormulaGrammar::CONV_A1_XL_A1);
7040 if (nParamCount == 2 && 0.0 == ::rtl::math::approxFloor( GetDouble()))
7042 // Overwrite the config and try Excel R1C1.
7043 eConv = FormulaGrammar::CONV_XL_R1C1;
7044 bTryXlA1 = false;
7047 const ScAddress::Details aDetails( bTryXlA1 ? FormulaGrammar::CONV_OOO : eConv, aPos );
7048 const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos );
7049 SCTAB nTab = aPos.Tab();
7050 OUString sRefStr = GetString().getString();
7051 ScRefAddress aRefAd, aRefAd2;
7052 ScAddress::ExternalInfo aExtInfo;
7053 if ( ConvertDoubleRef(pDok, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) ||
7054 ( bTryXlA1 && ConvertDoubleRef(pDok, sRefStr, nTab, aRefAd,
7055 aRefAd2, aDetailsXlA1, &aExtInfo) ) )
7057 if (aExtInfo.mbExternal)
7059 PushExternalDoubleRef(
7060 aExtInfo.mnFileId, aExtInfo.maTabName,
7061 aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
7062 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab());
7064 else
7065 PushDoubleRef( aRefAd, aRefAd2);
7067 else if ( ConvertSingleRef(pDok, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) ||
7068 ( bTryXlA1 && ConvertSingleRef (pDok, sRefStr, nTab, aRefAd,
7069 aDetailsXlA1, &aExtInfo) ) )
7071 if (aExtInfo.mbExternal)
7073 PushExternalSingleRef(
7074 aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab());
7076 else
7077 PushSingleRef( aRefAd);
7079 else
7083 ScRangeData* pData = ScRangeStringConverter::GetRangeDataFromString(sRefStr, nTab, pDok);
7084 if (!pData)
7085 break;
7087 // We need this in order to obtain a good range.
7088 pData->ValidateTabRefs();
7090 ScRange aRange;
7092 // This is the usual way to treat named ranges containing
7093 // relative references.
7094 if (!pData->IsReference( aRange, aPos))
7095 break;
7097 if (aRange.aStart == aRange.aEnd)
7098 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
7099 aRange.aStart.Tab());
7100 else
7101 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
7102 aRange.aStart.Tab(), aRange.aEnd.Col(),
7103 aRange.aEnd.Row(), aRange.aEnd.Tab());
7105 // success!
7106 return;
7108 while (false);
7112 OUString aName( ScGlobal::pCharClass->uppercase( sRefStr));
7113 ScDBCollection::NamedDBs& rDBs = pDok->GetDBCollection()->getNamedDBs();
7114 const ScDBData* pData = rDBs.findByUpperName( aName);
7115 if (!pData)
7116 break;
7118 ScRange aRange;
7119 pData->GetArea( aRange);
7121 // In Excel, specifying a table name without [] resolves to the
7122 // same as with [], a range that excludes header and totals
7123 // rows and contains only data rows. Do the same.
7124 if (pData->HasHeader())
7125 aRange.aStart.IncRow();
7126 if (pData->HasTotals())
7127 aRange.aEnd.IncRow(-1);
7129 if (aRange.aStart.Row() > aRange.aEnd.Row())
7130 break;
7132 if (aRange.aStart == aRange.aEnd)
7133 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
7134 aRange.aStart.Tab());
7135 else
7136 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
7137 aRange.aStart.Tab(), aRange.aEnd.Col(),
7138 aRange.aEnd.Row(), aRange.aEnd.Tab());
7140 // success!
7141 return;
7143 while (false);
7145 // It may be even a TableRef.
7146 // Anything else that resolves to one reference could be added
7147 // here, but we don't want to compile every arbitrary string. This
7148 // is already nasty enough..
7149 sal_Int32 nIndex = 0;
7150 if ((nIndex = sRefStr.indexOf('[')) >= 0 && sRefStr.indexOf(']',nIndex+1) > nIndex)
7154 ScCompiler aComp( pDok, aPos);
7155 aComp.SetGrammar( pDok->GetGrammar());
7156 aComp.SetRefConvention( eConv); // must be after grammar
7157 boost::scoped_ptr<ScTokenArray> pArr( aComp.CompileString( sRefStr));
7159 // Whatever.. use only the specific case.
7160 if (!pArr->HasOpCode( ocTableRef))
7161 break;
7163 aComp.CompileTokenArray();
7165 // A syntactically valid reference will generate exactly
7166 // one RPN token, a reference or error. Discard everything
7167 // else as error.
7168 if (pArr->GetCodeLen() != 1)
7169 break;
7171 ScTokenRef xTok( pArr->FirstRPN());
7172 if (!xTok)
7173 break;
7175 switch (xTok->GetType())
7177 case svSingleRef:
7178 case svDoubleRef:
7179 case svError:
7180 PushTempToken( xTok.get());
7181 // success!
7182 return;
7183 default:
7184 ; // nothing
7187 while (false);
7190 PushError( errNoRef);
7195 void ScInterpreter::ScAddressFunc()
7197 OUString sTabStr;
7199 sal_uInt8 nParamCount = GetByte();
7200 if( !MustHaveParamCount( nParamCount, 2, 5 ) )
7201 return;
7203 if( nParamCount >= 5 )
7204 sTabStr = GetString().getString();
7206 FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default
7207 if( nParamCount >= 4 && 0.0 == ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0)))
7208 eConv = FormulaGrammar::CONV_XL_R1C1;
7210 sal_uInt16 nFlags = SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE; // default
7211 if( nParamCount >= 3 )
7213 sal_uInt16 n = (sal_uInt16) ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0));
7214 switch ( n )
7216 default :
7217 PushNoValue();
7218 return;
7220 case 5:
7221 case 1 : break; // default
7222 case 6:
7223 case 2 : nFlags = SCA_ROW_ABSOLUTE; break;
7224 case 7:
7225 case 3 : nFlags = SCA_COL_ABSOLUTE; break;
7226 case 8:
7227 case 4 : nFlags = 0; break; // both relative
7230 nFlags |= SCA_VALID | SCA_VALID_ROW | SCA_VALID_COL;
7232 SCCOL nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble());
7233 SCROW nRow = (SCROW) ::rtl::math::approxFloor(GetDouble());
7234 if( eConv == FormulaGrammar::CONV_XL_R1C1 )
7236 // YUCK! The XL interface actually treats rel R1C1 refs differently
7237 // than A1
7238 if( !(nFlags & SCA_COL_ABSOLUTE) )
7239 nCol += aPos.Col() + 1;
7240 if( !(nFlags & SCA_ROW_ABSOLUTE) )
7241 nRow += aPos.Row() + 1;
7244 --nCol;
7245 --nRow;
7246 if(!ValidCol( nCol) || !ValidRow( nRow))
7248 PushIllegalArgument();
7249 return;
7252 const ScAddress::Details aDetails( eConv, aPos );
7253 const ScAddress aAdr( nCol, nRow, 0);
7254 OUString aRefStr(aAdr.Format(nFlags, pDok, aDetails));
7256 if( nParamCount >= 5 && !sTabStr.isEmpty() )
7258 OUString aDoc;
7259 if (eConv == FormulaGrammar::CONV_OOO)
7261 // Isolate Tab from 'Doc'#Tab
7262 sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr);
7263 if (nPos != -1)
7265 if (sTabStr[nPos+1] == '$')
7266 ++nPos; // also split 'Doc'#$Tab
7267 aDoc = sTabStr.copy( 0, nPos+1);
7268 sTabStr = sTabStr.copy( nPos+1);
7271 /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
7272 * need some extra handling to isolate Tab from Doc. */
7273 if (sTabStr[0] != '\'' || !sTabStr.endsWith("'"))
7274 ScCompiler::CheckTabQuotes( sTabStr, eConv);
7275 if (!aDoc.isEmpty())
7276 sTabStr = aDoc + sTabStr;
7277 sTabStr += eConv == FormulaGrammar::CONV_XL_R1C1 ? OUString("!") : OUString(".");
7278 sTabStr += aRefStr;
7279 PushString( sTabStr );
7281 else
7282 PushString( aRefStr );
7285 void ScInterpreter::ScOffset()
7287 sal_uInt8 nParamCount = GetByte();
7288 if ( MustHaveParamCount( nParamCount, 3, 5 ) )
7290 long nColNew = -1, nRowNew = -1, nColPlus, nRowPlus;
7291 if (nParamCount == 5)
7292 nColNew = (long) ::rtl::math::approxFloor(GetDouble());
7293 if (nParamCount >= 4)
7294 nRowNew = (long) ::rtl::math::approxFloor(GetDoubleWithDefault( -1.0 ));
7295 nColPlus = (long) ::rtl::math::approxFloor(GetDouble());
7296 nRowPlus = (long) ::rtl::math::approxFloor(GetDouble());
7297 SCCOL nCol1(0);
7298 SCROW nRow1(0);
7299 SCTAB nTab1(0);
7300 SCCOL nCol2(0);
7301 SCROW nRow2(0);
7302 SCTAB nTab2(0);
7303 if (nColNew == 0 || nRowNew == 0)
7305 PushIllegalArgument();
7306 return;
7308 switch (GetStackType())
7310 case svSingleRef:
7312 PopSingleRef(nCol1, nRow1, nTab1);
7313 if (nParamCount == 3 || (nColNew < 0 && nRowNew < 0))
7315 nCol1 = (SCCOL)((long) nCol1 + nColPlus);
7316 nRow1 = (SCROW)((long) nRow1 + nRowPlus);
7317 if (!ValidCol(nCol1) || !ValidRow(nRow1))
7318 PushIllegalArgument();
7319 else
7320 PushSingleRef(nCol1, nRow1, nTab1);
7322 else
7324 if (nColNew < 0)
7325 nColNew = 1;
7326 if (nRowNew < 0)
7327 nRowNew = 1;
7328 nCol1 = (SCCOL)((long)nCol1+nColPlus);
7329 nRow1 = (SCROW)((long)nRow1+nRowPlus);
7330 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
7331 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
7332 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
7333 !ValidCol(nCol2) || !ValidRow(nRow2))
7334 PushIllegalArgument();
7335 else
7336 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
7338 break;
7340 case svExternalSingleRef:
7342 sal_uInt16 nFileId;
7343 OUString aTabName;
7344 ScSingleRefData aRef;
7345 PopExternalSingleRef(nFileId, aTabName, aRef);
7346 ScAddress aAbsRef = aRef.toAbs(aPos);
7347 nCol1 = aAbsRef.Col();
7348 nRow1 = aAbsRef.Row();
7349 nTab1 = aAbsRef.Tab();
7351 if (nParamCount == 3 || (nColNew < 0 && nRowNew < 0))
7353 nCol1 = (SCCOL)((long) nCol1 + nColPlus);
7354 nRow1 = (SCROW)((long) nRow1 + nRowPlus);
7355 if (!ValidCol(nCol1) || !ValidRow(nRow1))
7356 PushIllegalArgument();
7357 else
7358 PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1);
7360 else
7362 if (nColNew < 0)
7363 nColNew = 1;
7364 if (nRowNew < 0)
7365 nRowNew = 1;
7366 nCol1 = (SCCOL)((long)nCol1+nColPlus);
7367 nRow1 = (SCROW)((long)nRow1+nRowPlus);
7368 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
7369 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
7370 nTab2 = nTab1;
7371 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
7372 !ValidCol(nCol2) || !ValidRow(nRow2))
7373 PushIllegalArgument();
7374 else
7375 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7377 break;
7379 case svDoubleRef:
7381 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7382 if (nColNew < 0)
7383 nColNew = nCol2 - nCol1 + 1;
7384 if (nRowNew < 0)
7385 nRowNew = nRow2 - nRow1 + 1;
7386 nCol1 = (SCCOL)((long)nCol1+nColPlus);
7387 nRow1 = (SCROW)((long)nRow1+nRowPlus);
7388 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
7389 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
7390 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
7391 !ValidCol(nCol2) || !ValidRow(nRow2) || nTab1 != nTab2)
7392 PushIllegalArgument();
7393 else
7394 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
7395 break;
7397 case svExternalDoubleRef:
7399 sal_uInt16 nFileId;
7400 OUString aTabName;
7401 ScComplexRefData aRef;
7402 PopExternalDoubleRef(nFileId, aTabName, aRef);
7403 ScRange aAbs = aRef.toAbs(aPos);
7404 nCol1 = aAbs.aStart.Col();
7405 nRow1 = aAbs.aStart.Row();
7406 nTab1 = aAbs.aStart.Tab();
7407 nCol2 = aAbs.aEnd.Col();
7408 nRow2 = aAbs.aEnd.Row();
7409 nTab2 = aAbs.aEnd.Tab();
7410 if (nColNew < 0)
7411 nColNew = nCol2 - nCol1 + 1;
7412 if (nRowNew < 0)
7413 nRowNew = nRow2 - nRow1 + 1;
7414 nCol1 = (SCCOL)((long)nCol1+nColPlus);
7415 nRow1 = (SCROW)((long)nRow1+nRowPlus);
7416 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
7417 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
7418 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
7419 !ValidCol(nCol2) || !ValidRow(nRow2) || nTab1 != nTab2)
7420 PushIllegalArgument();
7421 else
7422 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7423 break;
7425 default:
7426 PushIllegalParameter();
7427 break;
7428 } // end switch
7432 void ScInterpreter::ScIndex()
7434 sal_uInt8 nParamCount = GetByte();
7435 if ( MustHaveParamCount( nParamCount, 1, 4 ) )
7437 long nArea;
7438 size_t nAreaCount;
7439 SCCOL nCol;
7440 SCROW nRow;
7441 if (nParamCount == 4)
7442 nArea = (long) ::rtl::math::approxFloor(GetDouble());
7443 else
7444 nArea = 1;
7445 if (nParamCount >= 3)
7446 nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble());
7447 else
7448 nCol = 0;
7449 if (nParamCount >= 2)
7450 nRow = (SCROW) ::rtl::math::approxFloor(GetDouble());
7451 else
7452 nRow = 0;
7453 if (GetStackType() == svRefList)
7454 nAreaCount = (sp ? pStack[sp-1]->GetRefList()->size() : 0);
7455 else
7456 nAreaCount = 1; // one reference or array or whatever
7457 if (nAreaCount == 0 || (size_t)nArea > nAreaCount)
7459 PushError( errNoRef);
7460 return;
7462 else if (nArea < 1 || nCol < 0 || nRow < 0)
7464 PushIllegalArgument();
7465 return;
7467 switch (GetStackType())
7469 case svMatrix:
7470 case svExternalSingleRef:
7471 case svExternalDoubleRef:
7473 if (nArea != 1)
7474 SetError(errIllegalArgument);
7475 sal_uInt16 nOldSp = sp;
7476 ScMatrixRef pMat = GetMatrix();
7477 if (pMat)
7479 SCSIZE nC, nR;
7480 pMat->GetDimensions(nC, nR);
7481 // Access one element of a vector independent of col/row
7482 // orientation?
7483 bool bVector = ((nCol == 0 || nRow == 0) && (nC == 1 || nR == 1));
7484 SCSIZE nElement = ::std::max( static_cast<SCSIZE>(nCol),
7485 static_cast<SCSIZE>(nRow));
7486 if (nC == 0 || nR == 0 ||
7487 (!bVector && (static_cast<SCSIZE>(nCol) > nC ||
7488 static_cast<SCSIZE>(nRow) > nR)) ||
7489 (bVector && nElement > nC * nR))
7490 PushIllegalArgument();
7491 else if (nCol == 0 && nRow == 0)
7492 sp = nOldSp;
7493 else if (bVector)
7495 --nElement;
7496 if (pMat->IsString( nElement))
7497 PushString( pMat->GetString(nElement).getString());
7498 else
7499 PushDouble( pMat->GetDouble( nElement));
7501 else if (nCol == 0)
7503 ScMatrixRef pResMat = GetNewMat(nC, 1);
7504 if (pResMat)
7506 SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1);
7507 for (SCSIZE i = 0; i < nC; i++)
7508 if (!pMat->IsString(i, nRowMinus1))
7509 pResMat->PutDouble(pMat->GetDouble(i,
7510 nRowMinus1), i, 0);
7511 else
7512 pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0);
7514 PushMatrix(pResMat);
7516 else
7517 PushIllegalArgument();
7519 else if (nRow == 0)
7521 ScMatrixRef pResMat = GetNewMat(1, nR);
7522 if (pResMat)
7524 SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1);
7525 for (SCSIZE i = 0; i < nR; i++)
7526 if (!pMat->IsString(nColMinus1, i))
7527 pResMat->PutDouble(pMat->GetDouble(nColMinus1,
7528 i), i);
7529 else
7530 pResMat->PutString(pMat->GetString(nColMinus1, i), i);
7531 PushMatrix(pResMat);
7533 else
7534 PushIllegalArgument();
7536 else
7538 if (!pMat->IsString( static_cast<SCSIZE>(nCol-1),
7539 static_cast<SCSIZE>(nRow-1)))
7540 PushDouble( pMat->GetDouble(
7541 static_cast<SCSIZE>(nCol-1),
7542 static_cast<SCSIZE>(nRow-1)));
7543 else
7544 PushString( pMat->GetString(
7545 static_cast<SCSIZE>(nCol-1),
7546 static_cast<SCSIZE>(nRow-1)).getString());
7550 break;
7551 case svSingleRef:
7553 SCCOL nCol1 = 0;
7554 SCROW nRow1 = 0;
7555 SCTAB nTab1 = 0;
7556 PopSingleRef( nCol1, nRow1, nTab1);
7557 if (nCol > 1 || nRow > 1)
7558 PushIllegalArgument();
7559 else
7560 PushSingleRef( nCol1, nRow1, nTab1);
7562 break;
7563 case svDoubleRef:
7564 case svRefList:
7566 SCCOL nCol1 = 0;
7567 SCROW nRow1 = 0;
7568 SCTAB nTab1 = 0;
7569 SCCOL nCol2 = 0;
7570 SCROW nRow2 = 0;
7571 SCTAB nTab2 = 0;
7572 bool bRowArray = false;
7573 if (GetStackType() == svRefList)
7575 FormulaTokenRef xRef = PopToken();
7576 if (nGlobalError || !xRef)
7578 PushIllegalParameter();
7579 return;
7581 ScRange aRange( ScAddress::UNINITIALIZED);
7582 DoubleRefToRange( (*(xRef.get()->GetRefList()))[nArea-1], aRange);
7583 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7584 if ( nParamCount == 2 && nRow1 == nRow2 )
7585 bRowArray = true;
7587 else
7589 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7590 if ( nParamCount == 2 && nRow1 == nRow2 )
7591 bRowArray = true;
7593 if ( nTab1 != nTab2 ||
7594 (nCol > 0 && nCol1+nCol-1 > nCol2) ||
7595 (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) ||
7596 ( nRow > nCol2 - nCol1 + 1 && bRowArray ))
7597 PushIllegalArgument();
7598 else if (nCol == 0 && nRow == 0)
7600 if ( nCol1 == nCol2 && nRow1 == nRow2 )
7601 PushSingleRef( nCol1, nRow1, nTab1 );
7602 else
7603 PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 );
7605 else if (nRow == 0)
7607 if ( nRow1 == nRow2 )
7608 PushSingleRef( nCol1+nCol-1, nRow1, nTab1 );
7609 else
7610 PushDoubleRef( nCol1+nCol-1, nRow1, nTab1,
7611 nCol1+nCol-1, nRow2, nTab1 );
7613 else if (nCol == 0)
7615 if ( nCol1 == nCol2 )
7616 PushSingleRef( nCol1, nRow1+nRow-1, nTab1 );
7617 else if ( bRowArray )
7619 nCol =(SCCOL) nRow;
7620 nRow = 1;
7621 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
7623 else
7624 PushDoubleRef( nCol1, nRow1+nRow-1, nTab1,
7625 nCol2, nRow1+nRow-1, nTab1);
7627 else
7628 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
7630 break;
7631 default:
7632 PushIllegalParameter();
7637 void ScInterpreter::ScMultiArea()
7639 // Legacy support, convert to RefList
7640 sal_uInt8 nParamCount = GetByte();
7641 if (MustHaveParamCountMin( nParamCount, 1))
7643 while (!nGlobalError && nParamCount-- > 1)
7645 ScUnionFunc();
7650 void ScInterpreter::ScAreas()
7652 sal_uInt8 nParamCount = GetByte();
7653 if (MustHaveParamCount( nParamCount, 1))
7655 size_t nCount = 0;
7656 switch (GetStackType())
7658 case svSingleRef:
7660 FormulaTokenRef xT = PopToken();
7661 ValidateRef( *xT.get()->GetSingleRef());
7662 ++nCount;
7664 break;
7665 case svDoubleRef:
7667 FormulaTokenRef xT = PopToken();
7668 ValidateRef( *xT.get()->GetDoubleRef());
7669 ++nCount;
7671 break;
7672 case svRefList:
7674 FormulaTokenRef xT = PopToken();
7675 ValidateRef( *(xT.get()->GetRefList()));
7676 nCount += xT.get()->GetRefList()->size();
7678 break;
7679 default:
7680 SetError( errIllegalParameter);
7682 PushDouble( double(nCount));
7686 void ScInterpreter::ScCurrency()
7688 sal_uInt8 nParamCount = GetByte();
7689 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7691 OUString aStr;
7692 double fDec;
7693 if (nParamCount == 2)
7695 fDec = ::rtl::math::approxFloor(GetDouble());
7696 if (fDec < -15.0 || fDec > 15.0)
7698 PushIllegalArgument();
7699 return;
7702 else
7703 fDec = 2.0;
7704 double fVal = GetDouble();
7705 double fFac;
7706 if ( fDec != 0.0 )
7707 fFac = pow( (double)10, fDec );
7708 else
7709 fFac = 1.0;
7710 if (fVal < 0.0)
7711 fVal = ceil(fVal*fFac-0.5)/fFac;
7712 else
7713 fVal = floor(fVal*fFac+0.5)/fFac;
7714 Color* pColor = NULL;
7715 if ( fDec < 0.0 )
7716 fDec = 0.0;
7717 sal_uLong nIndex = pFormatter->GetStandardFormat(
7718 css::util::NumberFormat::CURRENCY,
7719 ScGlobal::eLnge);
7720 if ( (sal_uInt16) fDec != pFormatter->GetFormatPrecision( nIndex ) )
7722 OUString sFormatString = pFormatter->GenerateFormat(
7723 nIndex,
7724 ScGlobal::eLnge,
7725 true, // mit Tausenderpunkt
7726 false, // nicht rot
7727 (sal_uInt16) fDec,// Nachkommastellen
7728 1); // 1 Vorkommanull
7729 if (!pFormatter->GetPreviewString(sFormatString,
7730 fVal,
7731 aStr,
7732 &pColor,
7733 ScGlobal::eLnge))
7734 SetError(errIllegalArgument);
7736 else
7738 pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor);
7740 PushString(aStr);
7744 void ScInterpreter::ScReplace()
7746 if ( MustHaveParamCount( GetByte(), 4 ) )
7748 OUString aNewStr = GetString().getString();
7749 double fCount = GetStringPositionArgument();
7750 double fPos = GetStringPositionArgument();
7751 OUString aOldStr = GetString().getString();
7752 if (fPos < 1.0 || fCount < 0.0)
7753 PushIllegalArgument();
7754 else
7756 sal_Int32 nCount = static_cast<sal_Int32>(fCount);
7757 sal_Int32 nPos = static_cast<sal_Int32>(fPos);
7758 sal_Int32 nLen = aOldStr.getLength();
7759 if (nPos > nLen + 1)
7760 nPos = nLen + 1;
7761 if (nCount > nLen - nPos + 1)
7762 nCount = nLen - nPos + 1;
7763 aOldStr = aOldStr.replaceAt( nPos-1, nCount, "" );
7764 if ( CheckStringResultLen( aOldStr, aNewStr ) )
7765 aOldStr = aOldStr.replaceAt( nPos-1, 0, aNewStr );
7766 PushString( aOldStr );
7771 void ScInterpreter::ScFixed()
7773 sal_uInt8 nParamCount = GetByte();
7774 if ( MustHaveParamCount( nParamCount, 1, 3 ) )
7776 OUString aStr;
7777 double fDec;
7778 bool bThousand;
7779 if (nParamCount == 3)
7780 bThousand = !GetBool(); // Param TRUE: keine Tausenderpunkte
7781 else
7782 bThousand = true;
7783 if (nParamCount >= 2)
7785 fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
7786 if (fDec < -15.0 || fDec > 15.0)
7788 PushIllegalArgument();
7789 return;
7792 else
7793 fDec = 2.0;
7794 double fVal = GetDouble();
7795 double fFac;
7796 if ( fDec != 0.0 )
7797 fFac = pow( (double)10, fDec );
7798 else
7799 fFac = 1.0;
7800 if (fVal < 0.0)
7801 fVal = ceil(fVal*fFac-0.5)/fFac;
7802 else
7803 fVal = floor(fVal*fFac+0.5)/fFac;
7804 Color* pColor = NULL;
7805 if (fDec < 0.0)
7806 fDec = 0.0;
7807 sal_uLong nIndex = pFormatter->GetStandardFormat(
7808 css::util::NumberFormat::NUMBER,
7809 ScGlobal::eLnge);
7810 OUString sFormatString = pFormatter->GenerateFormat(
7811 nIndex,
7812 ScGlobal::eLnge,
7813 bThousand, // mit Tausenderpunkt
7814 false, // nicht rot
7815 (sal_uInt16) fDec,// Nachkommastellen
7816 1); // 1 Vorkommanull
7817 if (!pFormatter->GetPreviewString(sFormatString,
7818 fVal,
7819 aStr,
7820 &pColor,
7821 ScGlobal::eLnge))
7822 PushIllegalArgument();
7823 else
7824 PushString(aStr);
7828 void ScInterpreter::ScFind()
7830 sal_uInt8 nParamCount = GetByte();
7831 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
7833 sal_Int32 nAnz;
7834 if (nParamCount == 3)
7835 nAnz = GetDouble();
7836 else
7837 nAnz = 1;
7838 OUString sStr = GetString().getString();
7839 if (nAnz < 1 || nAnz > sStr.getLength())
7840 PushNoValue();
7841 else
7843 sal_Int32 nPos = sStr.indexOf(GetString().getString(), nAnz - 1);
7844 if (nPos == -1)
7845 PushNoValue();
7846 else
7847 PushDouble((double)(nPos + 1));
7852 void ScInterpreter::ScExact()
7854 nFuncFmtType = css::util::NumberFormat::LOGICAL;
7855 if ( MustHaveParamCount( GetByte(), 2 ) )
7857 svl::SharedString s1 = GetString();
7858 svl::SharedString s2 = GetString();
7859 PushInt( int(s1.getData() == s2.getData()) );
7863 void ScInterpreter::ScLeft()
7865 sal_uInt8 nParamCount = GetByte();
7866 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7868 sal_Int32 n;
7869 if (nParamCount == 2)
7871 double nVal = GetStringPositionArgument();
7872 if (nVal < 0.0)
7874 PushIllegalArgument();
7875 return ;
7877 else
7878 n = (sal_Int32) nVal;
7880 else
7881 n = 1;
7882 OUString aStr = GetString().getString();
7883 n = std::min(n, aStr.getLength());
7884 aStr = aStr.copy( 0, n );
7885 PushString( aStr );
7889 typedef struct {
7890 UBlockCode from;
7891 UBlockCode to;
7892 } UBlockScript;
7894 static const UBlockScript scriptList[] = {
7895 {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO},
7896 {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES},
7897 {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT },
7898 {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS},
7899 {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS},
7900 {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS},
7901 {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT},
7902 {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES}
7904 #define scriptListCount sizeof (scriptList) / sizeof (UBlockScript)
7905 bool SAL_CALL lcl_getScriptClass(sal_uInt32 currentChar)
7907 // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
7908 if( (currentChar == 0x005c || currentChar == 0x20ac) &&
7909 (MsLangId::getSystemLanguage() == LANGUAGE_JAPANESE) )
7910 return true;
7911 sal_uInt16 i;
7912 static bool nRet = false;
7913 UBlockCode block = (UBlockCode)ublock_getCode((sal_uInt32)currentChar);
7914 for ( i = 0; i < scriptListCount; i++) {
7915 if (block <= scriptList[i].to) break;
7917 nRet = (i < scriptListCount && block >= scriptList[i].from);
7918 return nRet;
7920 bool IsDBCS(sal_Unicode ch)
7922 return lcl_getScriptClass(ch);
7924 sal_Int32 getLengthB(const OUString &str)
7926 if(str.isEmpty())
7927 return 0;
7928 sal_Int32 index = 0;
7929 sal_Int32 length = 0;
7930 while(index < str.getLength()){
7931 if(IsDBCS(str[index]))
7932 length += 2;
7933 else
7934 length++;
7935 index++;
7937 return length;
7939 void ScInterpreter::ScLenB()
7941 PushDouble( getLengthB(GetString().getString()) );
7943 OUString lcl_RightB(const OUString &rStr, sal_Int32 n)
7945 if( n < getLengthB(rStr) )
7947 OUStringBuffer aBuf(rStr);
7948 sal_Int32 index = aBuf.getLength();
7949 while(index-- >= 0)
7951 if(0 == n)
7953 aBuf.remove( 0, index + 1);
7954 break;
7956 if(-1 == n)
7958 aBuf.remove( 0, index + 2 );
7959 aBuf.insert( 0, " ");
7960 break;
7962 if(IsDBCS(aBuf[index]))
7963 n -= 2;
7964 else
7965 n--;
7967 return aBuf.makeStringAndClear();
7969 return rStr;
7971 void ScInterpreter::ScRightB()
7973 sal_uInt8 nParamCount = GetByte();
7974 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7976 sal_Int32 n;
7977 if (nParamCount == 2)
7979 double nVal = GetStringPositionArgument();
7980 if ( nVal < 0.0 )
7982 PushIllegalArgument();
7983 return ;
7985 else
7986 n = (sal_Int32) nVal;
7988 else
7989 n = 1;
7990 OUString aStr(lcl_RightB(GetString().getString(), n));
7991 PushString( aStr );
7994 OUString lcl_LeftB(const OUString &rStr, sal_Int32 n)
7996 if( n < getLengthB(rStr) )
7998 OUStringBuffer aBuf(rStr);
7999 sal_Int32 index = -1;
8000 while(index++ < aBuf.getLength())
8002 if(0 == n)
8004 aBuf.truncate(index);
8005 break;
8007 if(-1 == n)
8009 aBuf.truncate( index - 1 );
8010 aBuf.append(" ");
8011 break;
8013 if(IsDBCS(aBuf[index]))
8014 n -= 2;
8015 else
8016 n--;
8018 return aBuf.makeStringAndClear();
8020 return rStr;
8022 void ScInterpreter::ScLeftB()
8024 sal_uInt8 nParamCount = GetByte();
8025 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
8027 sal_Int32 n;
8028 if (nParamCount == 2)
8030 double nVal = GetStringPositionArgument();
8031 if ( nVal < 0.0 )
8033 PushIllegalArgument();
8034 return ;
8036 else
8037 n = (sal_Int32) nVal;
8039 else
8040 n = 1;
8041 OUString aStr(lcl_LeftB(GetString().getString(), n));
8042 PushString( aStr );
8045 void ScInterpreter::ScMidB()
8047 if ( MustHaveParamCount( GetByte(), 3 ) )
8049 double fAnz = GetStringPositionArgument();
8050 double fAnfang = GetStringPositionArgument();
8051 OUString aStr = GetString().getString();
8052 if (fAnfang < 1.0 || fAnz < 0.0)
8053 PushIllegalArgument();
8054 else
8057 aStr = lcl_LeftB(aStr, (sal_Int32)fAnfang + (sal_Int32)fAnz - 1);
8058 sal_Int32 nCnt = getLengthB(aStr) - (sal_Int32)fAnfang + 1;
8059 aStr = lcl_RightB(aStr, nCnt>0 ? nCnt:0);
8060 PushString(aStr);
8065 void ScInterpreter::ScRight()
8067 sal_uInt8 nParamCount = GetByte();
8068 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
8070 sal_Int32 n;
8071 if (nParamCount == 2)
8073 double nVal = GetStringPositionArgument();
8074 if (nVal < 0.0)
8076 PushIllegalArgument();
8077 return ;
8079 else
8080 n = (sal_Int32) nVal;
8082 else
8083 n = 1;
8084 OUString aStr = GetString().getString();
8085 if( n < aStr.getLength() )
8086 aStr = aStr.copy( aStr.getLength() - n );
8087 PushString( aStr );
8091 void ScInterpreter::ScSearch()
8093 sal_uInt8 nParamCount = GetByte();
8094 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
8096 double fAnz;
8097 if (nParamCount == 3)
8099 // This should use GetStringPositionArgument() but old versions up
8100 // to LibreOffice 4.2.5 allowed and ignored 0 and negative values.
8101 // It is unnecessary to break existing documents that "rely" on
8102 // that behavior. Though ODFF constrains Start to be >=1.
8103 /* TODO: fix this and possibly break those broken documents? */
8104 fAnz = rtl::math::approxFloor( GetDouble());
8105 if (fAnz < 1.0)
8106 fAnz = 1.0;
8107 else if (!CheckStringPositionArgument( fAnz))
8109 PushIllegalArgument();
8110 return;
8113 else
8114 fAnz = 1.0;
8115 OUString sStr = GetString().getString();
8116 OUString SearchStr = GetString().getString();
8117 sal_Int32 nPos = fAnz - 1;
8118 sal_Int32 nEndPos = sStr.getLength();
8119 if( nPos >= nEndPos )
8120 PushNoValue();
8121 else
8123 utl::SearchParam::SearchType eSearchType =
8124 (MayBeRegExp( SearchStr, pDok ) ?
8125 utl::SearchParam::SRCH_REGEXP : utl::SearchParam::SRCH_NORMAL);
8126 utl::SearchParam sPar(SearchStr, eSearchType, false, false, false);
8127 utl::TextSearch sT( sPar, *ScGlobal::pCharClass );
8128 bool nBool = sT.SearchForward(sStr, &nPos, &nEndPos);
8129 if (!nBool)
8130 PushNoValue();
8131 else
8132 PushDouble((double)(nPos) + 1);
8137 void ScInterpreter::ScMid()
8139 if ( MustHaveParamCount( GetByte(), 3 ) )
8141 double fAnz = GetStringPositionArgument();
8142 double fAnfang = GetStringPositionArgument();
8143 OUString aStr = GetString().getString();
8144 if (fAnfang < 1.0 || fAnz < 0.0)
8145 PushIllegalArgument();
8146 else
8148 sal_Int32 nCharacters = std::min<sal_Int32>(static_cast<sal_Int32>(fAnz), aStr.getLength() - fAnfang + 1);
8149 OUString sRes;
8150 if (nCharacters > 0)
8151 sRes = aStr.copy(static_cast<sal_Int32>(fAnfang-1), nCharacters);
8152 PushString(sRes);
8157 void ScInterpreter::ScText()
8159 if ( MustHaveParamCount( GetByte(), 2 ) )
8161 OUString sFormatString = GetString().getString();
8162 svl::SharedString aStr;
8163 bool bString = false;
8164 double fVal = 0.0;
8165 switch (GetStackType())
8167 case svError:
8168 PopError();
8169 break;
8170 case svDouble:
8171 fVal = PopDouble();
8172 break;
8173 default:
8175 FormulaTokenRef xTok( PopToken());
8176 if (!nGlobalError)
8178 PushTempToken( xTok.get());
8179 // Temporarily override the ConvertStringToValue()
8180 // error for GetCellValue() / GetCellValueOrZero()
8181 sal_uInt16 nSErr = mnStringNoValueError;
8182 mnStringNoValueError = errNotNumericString;
8183 fVal = GetDouble();
8184 mnStringNoValueError = nSErr;
8185 if (nGlobalError == errNotNumericString)
8187 // Not numeric.
8188 nGlobalError = 0;
8189 PushTempToken( xTok.get());
8190 aStr = GetString();
8191 bString = true;
8196 if (nGlobalError)
8197 PushError( nGlobalError);
8198 else
8200 OUString aResult;
8201 Color* pColor = NULL;
8202 LanguageType eCellLang;
8203 const ScPatternAttr* pPattern = pDok->GetPattern(
8204 aPos.Col(), aPos.Row(), aPos.Tab() );
8205 if ( pPattern )
8206 eCellLang = static_cast<const SvxLanguageItem&>(
8207 pPattern->GetItem( ATTR_LANGUAGE_FORMAT )).GetValue();
8208 else
8209 eCellLang = ScGlobal::eLnge;
8210 if (bString)
8212 if (!pFormatter->GetPreviewString( sFormatString, aStr.getString(),
8213 aResult, &pColor, eCellLang))
8214 PushIllegalArgument();
8215 else
8216 PushString( aResult);
8218 else
8220 if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal,
8221 aResult, &pColor, eCellLang))
8222 PushIllegalArgument();
8223 else
8224 PushString( aResult);
8230 void ScInterpreter::ScSubstitute()
8232 sal_uInt8 nParamCount = GetByte();
8233 if ( MustHaveParamCount( nParamCount, 3, 4 ) )
8235 sal_Int32 nAnz;
8236 if (nParamCount == 4)
8238 double fAnz = GetStringPositionArgument();
8239 if( fAnz < 1 )
8241 PushIllegalArgument();
8242 return;
8244 else
8245 nAnz = (sal_Int32) fAnz;
8247 else
8248 nAnz = 0;
8249 OUString sNewStr = GetString().getString();
8250 OUString sOldStr = GetString().getString();
8251 OUString sStr = GetString().getString();
8252 sal_Int32 nPos = 0;
8253 sal_Int32 nCount = 0;
8254 sal_Int32 nNewLen = sNewStr.getLength();
8255 sal_Int32 nOldLen = sOldStr.getLength();
8256 while( true )
8258 nPos = sStr.indexOf( sOldStr, nPos );
8259 if (nPos != -1)
8261 nCount++;
8262 if( !nAnz || nCount == nAnz )
8264 sStr = sStr.replaceAt(nPos,nOldLen, "");
8265 if ( CheckStringResultLen( sStr, sNewStr ) )
8267 sStr = sStr.replaceAt(nPos, 0, sNewStr);
8268 nPos = sal::static_int_cast<sal_Int32>( nPos + nNewLen );
8270 else
8271 break;
8273 else
8274 nPos++;
8276 else
8277 break;
8279 PushString( sStr );
8283 void ScInterpreter::ScRept()
8285 if ( MustHaveParamCount( GetByte(), 2 ) )
8287 double fAnz = GetStringPositionArgument();
8288 OUString aStr = GetString().getString();
8289 if ( fAnz < 0.0 )
8290 PushIllegalArgument();
8291 else if ( fAnz * aStr.getLength() > SAL_MAX_UINT16 )
8293 PushError( errStringOverflow );
8295 else if ( fAnz == 0.0 )
8296 PushString( EMPTY_OUSTRING );
8297 else
8299 const sal_Int32 nLen = aStr.getLength();
8300 sal_Int32 n = (sal_Int32) fAnz;
8301 OUStringBuffer aRes(n*nLen);
8302 while( n-- )
8303 aRes.append(aStr);
8304 PushString( aRes.makeStringAndClear() );
8309 void ScInterpreter::ScConcat()
8311 sal_uInt8 nParamCount = GetByte();
8312 OUString aRes;
8313 while( nParamCount-- > 0)
8315 OUString aStr = GetString().getString();
8316 aRes = aStr + aRes;
8318 PushString( aRes );
8321 sal_uInt16 ScInterpreter::GetErrorType()
8323 sal_uInt16 nErr;
8324 sal_uInt16 nOldError = nGlobalError;
8325 nGlobalError = 0;
8326 switch ( GetStackType() )
8328 case svRefList :
8330 FormulaTokenRef x = PopToken();
8331 if (nGlobalError)
8332 nErr = nGlobalError;
8333 else
8335 const ScRefList* pRefList = x.get()->GetRefList();
8336 size_t n = pRefList->size();
8337 if (!n)
8338 nErr = errNoRef;
8339 else if (n > 1)
8340 nErr = errNoValue;
8341 else
8343 ScRange aRange;
8344 DoubleRefToRange( (*pRefList)[0], aRange);
8345 if (nGlobalError)
8346 nErr = nGlobalError;
8347 else
8349 ScAddress aAdr;
8350 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
8351 nErr = pDok->GetErrCode( aAdr );
8352 else
8353 nErr = nGlobalError;
8358 break;
8359 case svDoubleRef :
8361 ScRange aRange;
8362 PopDoubleRef( aRange );
8363 if ( nGlobalError )
8364 nErr = nGlobalError;
8365 else
8367 ScAddress aAdr;
8368 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
8369 nErr = pDok->GetErrCode( aAdr );
8370 else
8371 nErr = nGlobalError;
8374 break;
8375 case svSingleRef :
8377 ScAddress aAdr;
8378 PopSingleRef( aAdr );
8379 if ( nGlobalError )
8380 nErr = nGlobalError;
8381 else
8382 nErr = pDok->GetErrCode( aAdr );
8384 break;
8385 default:
8386 PopError();
8387 nErr = nGlobalError;
8389 nGlobalError = nOldError;
8390 return nErr;
8393 void ScInterpreter::ScErrorType()
8395 sal_uInt16 nErr = GetErrorType();
8396 if ( nErr )
8398 nGlobalError = 0;
8399 PushDouble( nErr );
8401 else
8403 PushNA();
8407 void ScInterpreter::ScErrorType_ODF()
8409 sal_uInt16 nErr = GetErrorType();
8410 sal_uInt16 nErrType;
8412 switch ( nErr )
8414 case errParameterExpected : // #NULL!
8415 nErrType = 1;
8416 break;
8417 case errDivisionByZero : // #DIV/0!
8418 nErrType = 2;
8419 break;
8420 case errNoValue : // #VALUE!
8421 nErrType = 3;
8422 break;
8423 case errNoRef : // #REF!
8424 nErrType = 4;
8425 break;
8426 case errNoName : // #NAME?
8427 nErrType = 5;
8428 break;
8429 case errIllegalFPOperation : // #NUM!
8430 nErrType = 6;
8431 break;
8432 case NOTAVAILABLE : // #N/A
8433 nErrType = 7;
8434 break;
8436 #GETTING_DATA is a message that can appear in Excel when a large or
8437 complex worksheet is being calculated. In Excel 2007 and newer,
8438 operations are grouped so more complicated cells may finish after
8439 earlier ones do. While the calculations are still processing, the
8440 unfinished cells may display #GETTING_DATA.
8441 Because the message is temporary and disappears when the calculations
8442 complete, this isn’t a true error.
8443 No calc error code known (yet).
8445 case : // GETTING_DATA
8446 nErrType = 8;
8447 break;
8449 default :
8450 nErrType = 0;
8451 break;
8454 if ( nErrType )
8456 nGlobalError = 0;
8457 PushDouble( nErrType );
8459 else
8460 PushNA();
8463 bool ScInterpreter::MayBeRegExp( const OUString& rStr, const ScDocument* pDoc )
8465 if ( pDoc && !pDoc->GetDocOptions().IsFormulaRegexEnabled() )
8466 return false;
8467 if ( rStr.isEmpty() || (rStr.getLength() == 1 && !rStr.startsWith(".")) )
8468 return false; // single meta characters can not be a regexp
8469 static const sal_Unicode cre[] = { '.','*','+','?','[',']','^','$','\\','<','>','(',')','|', 0 };
8470 const sal_Unicode* p1 = rStr.getStr();
8471 sal_Unicode c1;
8472 while ( ( c1 = *p1++ ) != 0 )
8474 const sal_Unicode* p2 = cre;
8475 while ( *p2 )
8477 if ( c1 == *p2++ )
8478 return true;
8481 return false;
8484 static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument * pDoc,
8485 const ScQueryParam & rParam, const ScQueryEntry & rEntry )
8487 bool bFound = false;
8488 ScQueryCellIterator aCellIter( pDoc, rParam.nTab, rParam, false);
8489 if (rEntry.eOp != SC_EQUAL)
8491 // range lookup <= or >=
8492 SCCOL nCol;
8493 SCROW nRow;
8494 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow);
8495 if (bFound)
8497 o_rResultPos.SetCol( nCol);
8498 o_rResultPos.SetRow( nRow);
8501 else if (aCellIter.GetFirst())
8503 // EQUAL
8504 bFound = true;
8505 o_rResultPos.SetCol( aCellIter.GetCol());
8506 o_rResultPos.SetRow( aCellIter.GetRow());
8508 return bFound;
8511 bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos,
8512 const ScQueryParam & rParam ) const
8514 bool bFound = false;
8515 const ScQueryEntry& rEntry = rParam.GetEntry(0);
8516 bool bColumnsMatch = (rParam.nCol1 == rEntry.nField);
8517 OSL_ENSURE( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match");
8518 // At least all volatile functions that generate indirect references have
8519 // to force non-cached lookup.
8520 /* TODO: We could further classify volatile functions into reference
8521 * generating and not reference generating functions to have to force less
8522 * direct lookups here. We could even further attribute volatility per
8523 * parameter so it would affect only the lookup range parameter. */
8524 if (!bColumnsMatch || GetVolatileType() != NOT_VOLATILE)
8525 bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry);
8526 else
8528 ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab,
8529 rParam.nCol2, rParam.nRow2, rParam.nTab);
8530 ScLookupCache& rCache = pDok->GetLookupCache( aLookupRange);
8531 ScLookupCache::QueryCriteria aCriteria( rEntry);
8532 ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos,
8533 aCriteria, aPos);
8534 switch (eCacheResult)
8536 case ScLookupCache::NOT_CACHED :
8537 case ScLookupCache::CRITERIA_DIFFERENT :
8538 bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry);
8539 if (eCacheResult == ScLookupCache::NOT_CACHED)
8540 rCache.insert( o_rResultPos, aCriteria, aPos, bFound);
8541 break;
8542 case ScLookupCache::FOUND :
8543 bFound = true;
8544 break;
8545 case ScLookupCache::NOT_AVAILABLE :
8546 ; // nothing, bFound remains FALSE
8547 break;
8550 return bFound;
8553 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */