Stop leaking all ScPostIt instances.
[LibreOffice.git] / sc / source / core / tool / interpr1.cxx
blob2a048ae90a0ae9952dda2bb32845db42f8f403ce
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 <osl/thread.h>
26 #include <svx/algitem.hxx>
27 #include <unotools/textsearch.hxx>
28 #include <svl/zforlist.hxx>
29 #include <svl/zformat.hxx>
30 #include <tools/urlobj.hxx>
31 #include <unotools/charclass.hxx>
32 #include <sfx2/docfile.hxx>
33 #include <sfx2/printer.hxx>
34 #include <unotools/collatorwrapper.hxx>
35 #include <unotools/transliterationwrapper.hxx>
36 #include <rtl/ustring.hxx>
37 #include <unicode/uchar.h>
39 #include "patattr.hxx"
40 #include "global.hxx"
41 #include "document.hxx"
42 #include "dociter.hxx"
43 #include "formulacell.hxx"
44 #include "scmatrix.hxx"
45 #include "docoptio.hxx"
46 #include "globstr.hrc"
47 #include "attrib.hxx"
48 #include "jumpmatrix.hxx"
49 #include "random.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/string.hxx>
65 #include "svl/sharedstringpool.hxx"
67 #include <stdlib.h>
68 #include <string.h>
69 #include <math.h>
70 #include <vector>
71 #include <memory>
72 #include <limits>
74 static const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656); // 2^48
76 IMPL_FIXEDMEMPOOL_NEWDEL( ScTokenStack )
77 IMPL_FIXEDMEMPOOL_NEWDEL( ScInterpreter )
79 ScCalcConfig ScInterpreter::maGlobalConfig;
80 ScTokenStack* ScInterpreter::pGlobalStack = NULL;
81 bool ScInterpreter::bGlobalStackInUse = false;
83 using namespace formula;
84 using ::std::auto_ptr;
86 void ScInterpreter::ScIfJump()
88 const short* pJump = pCur->GetJump();
89 short nJumpCount = pJump[ 0 ];
90 MatrixDoubleRefToMatrix();
91 switch ( GetStackType() )
93 case svMatrix:
95 ScMatrixRef pMat = PopMatrix();
96 if ( !pMat )
97 PushIllegalParameter();
98 else
100 FormulaTokenRef xNew;
101 ScTokenMatrixMap::const_iterator aMapIter;
102 // DoubleError handled by JumpMatrix
103 pMat->SetErrorInterpreter( NULL);
104 SCSIZE nCols, nRows;
105 pMat->GetDimensions( nCols, nRows );
106 if ( nCols == 0 || nRows == 0 )
108 PushIllegalArgument();
109 return;
111 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
112 pCur)) != pTokenMatrixMap->end()))
113 xNew = (*aMapIter).second;
114 else
116 ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
117 for ( SCSIZE nC=0; nC < nCols; ++nC )
119 for ( SCSIZE nR=0; nR < nRows; ++nR )
121 double fVal;
122 bool bTrue;
123 bool bIsValue = pMat->IsValue(nC, nR);
124 if (bIsValue)
126 fVal = pMat->GetDouble(nC, nR);
127 bIsValue = ::rtl::math::isFinite(fVal);
128 bTrue = bIsValue && (fVal != 0.0);
129 if (bTrue)
130 fVal = 1.0;
132 else
134 // Treat empty and empty path as 0, but string
135 // as error.
136 bIsValue = (!pMat->IsString(nC, nR) || pMat->IsEmpty(nC, nR));
137 bTrue = false;
138 fVal = (bIsValue ? 0.0 : CreateDoubleError( errNoValue));
140 if ( bTrue )
141 { // TRUE
142 if( nJumpCount >= 2 )
143 { // THEN path
144 pJumpMat->SetJump( nC, nR, fVal,
145 pJump[ 1 ],
146 pJump[ nJumpCount ]);
148 else
149 { // no parameter given for THEN
150 pJumpMat->SetJump( nC, nR, fVal,
151 pJump[ nJumpCount ],
152 pJump[ nJumpCount ]);
155 else
156 { // FALSE
157 if( nJumpCount == 3 && bIsValue )
158 { // ELSE path
159 pJumpMat->SetJump( nC, nR, fVal,
160 pJump[ 2 ],
161 pJump[ nJumpCount ]);
163 else
164 { // no parameter given for ELSE,
165 // or DoubleError
166 pJumpMat->SetJump( nC, nR, fVal,
167 pJump[ nJumpCount ],
168 pJump[ nJumpCount ]);
173 xNew = new ScJumpMatrixToken( pJumpMat );
174 GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type(pCur, xNew));
176 PushTempToken( xNew.get());
177 // set endpoint of path for main code line
178 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
181 break;
182 default:
184 if ( GetBool() )
185 { // TRUE
186 if( nJumpCount >= 2 )
187 { // THEN path
188 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
190 else
191 { // no parameter given for THEN
192 nFuncFmtType = NUMBERFORMAT_LOGICAL;
193 PushInt(1);
194 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
197 else
198 { // FALSE
199 if( nJumpCount == 3 )
200 { // ELSE path
201 aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
203 else
204 { // no parameter given for ELSE
205 nFuncFmtType = NUMBERFORMAT_LOGICAL;
206 PushInt(0);
207 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
215 /** Store a matrix value in another matrix in the context of that other matrix
216 is the result matrix of a jump matrix. All arguments must be valid and are
217 not checked. */
218 static void lcl_storeJumpMatResult(
219 const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR )
221 if ( pMat->IsValue( nC, nR ) )
223 double fVal = pMat->GetDouble( nC, nR );
224 pJumpMat->PutResultDouble( fVal, nC, nR );
226 else if ( pMat->IsEmpty( nC, nR ) )
228 pJumpMat->PutResultEmpty( nC, nR );
230 else
232 pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR);
237 void ScInterpreter::ScIfError( bool bNAonly )
239 const short* pJump = pCur->GetJump();
240 short nJumpCount = pJump[ 0 ];
241 if (!sp)
243 PushError( errUnknownStackVariable);
244 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
245 return;
248 FormulaTokenRef xToken( pStack[ sp - 1 ] );
249 bool bError = false;
250 sal_uInt16 nOldGlobalError = nGlobalError;
251 nGlobalError = 0;
253 MatrixDoubleRefToMatrix();
254 switch (GetStackType())
256 default:
257 Pop();
258 break;
259 case svError:
260 PopError();
261 bError = true;
262 break;
263 case svDoubleRef:
264 case svSingleRef:
266 ScAddress aAdr;
267 if (!PopDoubleRefOrSingleRef( aAdr))
268 bError = true;
269 else
272 ScRefCellValue aCell;
273 aCell.assign(*pDok, aAdr);
274 nGlobalError = GetCellErrCode(aCell);
275 if (nGlobalError)
276 bError = true;
279 break;
280 case svExternalSingleRef:
281 case svExternalDoubleRef:
283 double fVal;
284 svl::SharedString aStr;
285 // Handles also existing jump matrix case and sets error on
286 // elements.
287 GetDoubleOrStringFromMatrix( fVal, aStr);
288 if (nGlobalError)
289 bError = true;
291 break;
292 case svMatrix:
294 const ScMatrixRef pMat = PopMatrix();
295 if (!pMat || (nGlobalError && (!bNAonly || nGlobalError == NOTAVAILABLE)))
297 bError = true;
298 break; // switch
300 // If the matrix has no queried error at all we can simply use
301 // it as result and don't need to bother with jump matrix.
302 SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(),
303 nErrorRow = ::std::numeric_limits<SCSIZE>::max();
304 SCSIZE nCols, nRows;
305 pMat->GetDimensions( nCols, nRows );
306 if (nCols == 0 || nRows == 0)
308 bError = true;
309 break; // switch
311 for (SCSIZE nC=0; nC < nCols && !bError; ++nC)
313 for (SCSIZE nR=0; nR < nRows && !bError; ++nR)
315 sal_uInt16 nErr = pMat->GetError( nC, nR );
316 if (nErr && (!bNAonly || nErr == NOTAVAILABLE))
318 bError = true;
319 nErrorCol = nC;
320 nErrorRow = nR;
324 if (!bError)
325 break; // switch, we're done and have the result
327 FormulaTokenRef xNew;
328 ScTokenMatrixMap::const_iterator aMapIter;
329 if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end()))
331 xNew = (*aMapIter).second;
333 else
335 const ScMatrix* pMatPtr = pMat.get();
336 ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
337 // Init all jumps to no error to save single calls. Error
338 // is the exceptional condition.
339 const double fFlagResult = CreateDoubleError( errJumpMatHasResult);
340 pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] );
341 // Up to first error position simply store results, no need
342 // to evaluate error conditions again.
343 SCSIZE nC = 0, nR = 0;
344 for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ )
346 for ( ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR)
348 lcl_storeJumpMatResult(pMatPtr, pJumpMat, nC, nR);
350 if (nC != nErrorCol || nR != nErrorRow)
351 ++nC;
353 // Now the mixed cases.
354 for ( ; nC < nCols; ++nC)
356 for ( ; nR < nRows; ++nR)
358 sal_uInt16 nErr = pMat->GetError( nC, nR );
359 if (nErr && (!bNAonly || nErr == NOTAVAILABLE))
360 { // TRUE, THEN path
361 pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] );
363 else
364 { // FALSE, EMPTY path, store result instead
365 lcl_storeJumpMatResult(pMatPtr, pJumpMat, nC, nR);
369 xNew = new ScJumpMatrixToken( pJumpMat );
370 GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type( pCur, xNew ));
372 nGlobalError = nOldGlobalError;
373 PushTempToken( xNew.get() );
374 // set endpoint of path for main code line
375 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
376 return;
378 break;
381 if (bError && (!bNAonly || nGlobalError == NOTAVAILABLE))
383 // error, calculate 2nd argument
384 nGlobalError = 0;
385 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
387 else
389 // no error, push 1st argument and continue
390 nGlobalError = nOldGlobalError;
391 PushTempToken( xToken.get());
392 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
397 void ScInterpreter::ScChoseJump()
399 // We have to set a jump, if there was none chosen because of an error set
400 // it to endpoint.
401 bool bHaveJump = false;
402 const short* pJump = pCur->GetJump();
403 short nJumpCount = pJump[ 0 ];
404 MatrixDoubleRefToMatrix();
405 switch ( GetStackType() )
407 case svMatrix:
409 ScMatrixRef pMat = PopMatrix();
410 if ( !pMat )
411 PushIllegalParameter();
412 else
414 FormulaTokenRef xNew;
415 ScTokenMatrixMap::const_iterator aMapIter;
416 // DoubleError handled by JumpMatrix
417 pMat->SetErrorInterpreter( NULL);
418 SCSIZE nCols, nRows;
419 pMat->GetDimensions( nCols, nRows );
420 if ( nCols == 0 || nRows == 0 )
421 PushIllegalParameter();
422 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
423 pCur)) != pTokenMatrixMap->end()))
424 xNew = (*aMapIter).second;
425 else
427 ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
428 for ( SCSIZE nC=0; nC < nCols; ++nC )
430 for ( SCSIZE nR=0; nR < nRows; ++nR )
432 double fVal;
433 bool bIsValue = pMat->IsValue(nC, nR);
434 if ( bIsValue )
436 fVal = pMat->GetDouble(nC, nR);
437 bIsValue = ::rtl::math::isFinite( fVal );
438 if ( bIsValue )
440 fVal = ::rtl::math::approxFloor( fVal);
441 if ( (fVal < 1) || (fVal >= nJumpCount))
443 bIsValue = false;
444 fVal = CreateDoubleError(
445 errIllegalArgument);
449 else
451 fVal = CreateDoubleError( errNoValue);
453 if ( bIsValue )
455 pJumpMat->SetJump( nC, nR, fVal,
456 pJump[ (short)fVal ],
457 pJump[ nJumpCount ]);
459 else
461 pJumpMat->SetJump( nC, nR, fVal,
462 pJump[ nJumpCount ],
463 pJump[ nJumpCount ]);
467 xNew = new ScJumpMatrixToken( pJumpMat );
468 GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type(
469 pCur, xNew));
471 PushTempToken( xNew.get());
472 // set endpoint of path for main code line
473 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
474 bHaveJump = true;
477 break;
478 default:
480 double nJumpIndex = ::rtl::math::approxFloor( GetDouble() );
481 if (!nGlobalError && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
483 aCode.Jump( pJump[ (short) nJumpIndex ], pJump[ nJumpCount ] );
484 bHaveJump = true;
486 else
487 PushIllegalArgument();
490 if (!bHaveJump)
491 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
494 static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows )
496 SCSIZE nJumpCols, nJumpRows;
497 SCSIZE nResCols, nResRows;
498 SCSIZE nAdjustCols, nAdjustRows;
499 pJumpM->GetDimensions( nJumpCols, nJumpRows );
500 pJumpM->GetResMatDimensions( nResCols, nResRows );
501 if (( nJumpCols == 1 && nParmCols > nResCols ) ||
502 ( nJumpRows == 1 && nParmRows > nResRows ))
504 if ( nJumpCols == 1 && nJumpRows == 1 )
506 nAdjustCols = nParmCols > nResCols ? nParmCols : nResCols;
507 nAdjustRows = nParmRows > nResRows ? nParmRows : nResRows;
509 else if ( nJumpCols == 1 )
511 nAdjustCols = nParmCols;
512 nAdjustRows = nResRows;
514 else
516 nAdjustCols = nResCols;
517 nAdjustRows = nParmRows;
519 pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
523 bool ScInterpreter::JumpMatrix( short nStackLevel )
525 pJumpMatrix = static_cast<ScToken*>(pStack[sp-nStackLevel])->GetJumpMatrix();
526 bool bHasResMat = pJumpMatrix->HasResultMatrix();
527 SCSIZE nC, nR;
528 if ( nStackLevel == 2 )
530 if ( aCode.HasStacked() )
531 aCode.Pop(); // pop what Jump() pushed
532 else
534 OSL_FAIL( "ScInterpreter::JumpMatrix: pop goes the weasel" );
537 if ( !bHasResMat )
539 Pop();
540 SetError( errUnknownStackVariable );
542 else
544 pJumpMatrix->GetPos( nC, nR );
545 switch ( GetStackType() )
547 case svDouble:
549 double fVal = GetDouble();
550 if ( nGlobalError )
552 fVal = CreateDoubleError( nGlobalError );
553 nGlobalError = 0;
555 pJumpMatrix->PutResultDouble( fVal, nC, nR );
557 break;
558 case svString:
560 svl::SharedString aStr = GetString();
561 if ( nGlobalError )
563 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
564 nC, nR);
565 nGlobalError = 0;
567 else
568 pJumpMatrix->PutResultString(aStr, nC, nR);
570 break;
571 case svSingleRef:
573 ScAddress aAdr;
574 PopSingleRef( aAdr );
575 if ( nGlobalError )
577 pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError),
578 nC, nR);
579 nGlobalError = 0;
581 else
583 ScRefCellValue aCell;
584 aCell.assign(*pDok, aAdr);
585 if (aCell.hasEmptyValue())
586 pJumpMatrix->PutResultEmpty( nC, nR );
587 else if (aCell.hasNumeric())
589 double fVal = GetCellValue(aAdr, aCell);
590 if ( nGlobalError )
592 fVal = CreateDoubleError(
593 nGlobalError);
594 nGlobalError = 0;
596 pJumpMatrix->PutResultDouble( fVal, nC, nR );
598 else
600 svl::SharedString aStr;
601 GetCellString(aStr, aCell);
602 if ( nGlobalError )
604 pJumpMatrix->PutResultDouble( CreateDoubleError(
605 nGlobalError), nC, nR);
606 nGlobalError = 0;
608 else
609 pJumpMatrix->PutResultString(aStr, nC, nR);
613 break;
614 case svDoubleRef:
615 { // upper left plus offset within matrix
616 double fVal;
617 ScRange aRange;
618 PopDoubleRef( aRange );
619 if ( nGlobalError )
621 fVal = CreateDoubleError( nGlobalError );
622 nGlobalError = 0;
623 pJumpMatrix->PutResultDouble( fVal, nC, nR );
625 else
627 // Do not modify the original range because we use it
628 // to adjust the size of the result matrix if necessary.
629 ScAddress aAdr( aRange.aStart);
630 sal_uLong nCol = (sal_uLong)aAdr.Col() + nC;
631 sal_uLong nRow = (sal_uLong)aAdr.Row() + nR;
632 if ((nCol > static_cast<sal_uLong>(aRange.aEnd.Col()) &&
633 aRange.aEnd.Col() != aRange.aStart.Col())
634 || (nRow > static_cast<sal_uLong>(aRange.aEnd.Row()) &&
635 aRange.aEnd.Row() != aRange.aStart.Row()))
637 fVal = CreateDoubleError( NOTAVAILABLE );
638 pJumpMatrix->PutResultDouble( fVal, nC, nR );
640 else
642 // Replicate column and/or row of a vector if it is
643 // one. Note that this could be a range reference
644 // that in fact consists of only one cell, e.g. A1:A1
645 if (aRange.aEnd.Col() == aRange.aStart.Col())
646 nCol = aRange.aStart.Col();
647 if (aRange.aEnd.Row() == aRange.aStart.Row())
648 nRow = aRange.aStart.Row();
649 aAdr.SetCol( static_cast<SCCOL>(nCol) );
650 aAdr.SetRow( static_cast<SCROW>(nRow) );
651 ScRefCellValue aCell;
652 aCell.assign(*pDok, aAdr);
653 if (aCell.hasEmptyValue())
654 pJumpMatrix->PutResultEmpty( nC, nR );
655 else if (aCell.hasNumeric())
657 double fCellVal = GetCellValue(aAdr, aCell);
658 if ( nGlobalError )
660 fCellVal = CreateDoubleError(
661 nGlobalError);
662 nGlobalError = 0;
664 pJumpMatrix->PutResultDouble( fCellVal, nC, nR );
666 else
668 svl::SharedString aStr;
669 GetCellString(aStr, aCell);
670 if ( nGlobalError )
672 pJumpMatrix->PutResultDouble( CreateDoubleError(
673 nGlobalError), nC, nR);
674 nGlobalError = 0;
676 else
677 pJumpMatrix->PutResultString(aStr, nC, nR);
680 SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
681 SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
682 lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows );
685 break;
686 case svMatrix:
687 { // match matrix offsets
688 double fVal;
689 ScMatrixRef pMat = PopMatrix();
690 if ( nGlobalError )
692 fVal = CreateDoubleError( nGlobalError );
693 nGlobalError = 0;
694 pJumpMatrix->PutResultDouble( fVal, nC, nR );
696 else if ( !pMat )
698 fVal = CreateDoubleError( errUnknownVariable );
699 pJumpMatrix->PutResultDouble( fVal, nC, nR );
701 else
703 SCSIZE nCols, nRows;
704 pMat->GetDimensions( nCols, nRows );
705 if ((nCols <= nC && nCols != 1) ||
706 (nRows <= nR && nRows != 1))
708 fVal = CreateDoubleError( NOTAVAILABLE );
709 pJumpMatrix->PutResultDouble( fVal, nC, nR );
711 else
713 lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR);
715 lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows );
718 break;
719 case svError:
721 PopError();
722 double fVal = CreateDoubleError( nGlobalError);
723 nGlobalError = 0;
724 pJumpMatrix->PutResultDouble( fVal, nC, nR );
726 break;
727 default:
729 Pop();
730 double fVal = CreateDoubleError( errIllegalArgument);
731 pJumpMatrix->PutResultDouble( fVal, nC, nR );
736 bool bCont = pJumpMatrix->Next( nC, nR );
737 if ( bCont )
739 double fBool;
740 short nStart, nNext, nStop;
741 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
742 while ( bCont && nStart == nNext )
743 { // push all results that have no jump path
744 if ( bHasResMat && (GetDoubleErrorValue( fBool) != errJumpMatHasResult) )
746 // a false without path results in an empty path value
747 if ( fBool == 0.0 )
748 pJumpMatrix->PutResultEmptyPath( nC, nR );
749 else
750 pJumpMatrix->PutResultDouble( fBool, nC, nR );
752 bCont = pJumpMatrix->Next( nC, nR );
753 if ( bCont )
754 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
756 if ( bCont && nStart != nNext )
758 const ScTokenVec* pParams = pJumpMatrix->GetJumpParameters();
759 if ( pParams )
761 for ( ScTokenVec::const_iterator i = pParams->begin();
762 i != pParams->end(); ++i )
764 // This is not the current state of the interpreter, so
765 // push without error, and elements' errors are coded into
766 // double.
767 PushWithoutError( *(*i));
770 aCode.Jump( nStart, nNext, nStop );
773 if ( !bCont )
774 { // we're done with it, throw away jump matrix, keep result
775 ScMatrix* pResMat = pJumpMatrix->GetResultMatrix();
776 pJumpMatrix = NULL;
777 Pop();
778 PushMatrix( pResMat );
779 // Remove jump matrix from map and remember result matrix in case it
780 // could be reused in another path of the same condition.
781 if (pTokenMatrixMap)
783 pTokenMatrixMap->erase( pCur);
784 pTokenMatrixMap->insert( ScTokenMatrixMap::value_type( pCur,
785 pStack[sp-1]));
787 return true;
789 return false;
792 double ScInterpreter::Compare()
794 sc::Compare aComp;
795 aComp.mbIgnoreCase = pDok->GetDocOptions().IsIgnoreCase();
796 for( short i = 1; i >= 0; i-- )
798 sc::Compare::Cell& rCell = aComp.maCells[i];
800 switch ( GetRawStackType() )
802 case svEmptyCell:
803 Pop();
804 rCell.mbEmpty = true;
805 break;
806 case svMissing:
807 case svDouble:
808 rCell.mfValue = GetDouble();
809 rCell.mbValue = true;
810 break;
811 case svString:
812 rCell.maStr = GetString();
813 rCell.mbValue = false;
814 break;
815 case svDoubleRef :
816 case svSingleRef :
818 ScAddress aAdr;
819 if ( !PopDoubleRefOrSingleRef( aAdr ) )
820 break;
821 ScRefCellValue aCell;
822 aCell.assign(*pDok, aAdr);
823 if (aCell.hasEmptyValue())
824 rCell.mbEmpty = true;
825 else if (aCell.hasString())
827 svl::SharedString aStr;
828 GetCellString(aStr, aCell);
829 rCell.maStr = aStr;
830 rCell.mbValue = false;
832 else
834 rCell.mfValue = GetCellValue(aAdr, aCell);
835 rCell.mbValue = true;
838 break;
839 case svExternalSingleRef:
841 ScMatrixRef pMat = GetMatrix();
842 if (!pMat)
844 SetError( errIllegalParameter);
845 break;
848 SCSIZE nC, nR;
849 pMat->GetDimensions(nC, nR);
850 if (!nC || !nR)
852 SetError( errIllegalParameter);
853 break;
855 if (pMat->IsEmpty(0, 0))
856 rCell.mbEmpty = true;
857 else if (pMat->IsString(0, 0))
859 rCell.maStr = pMat->GetString(0, 0);
860 rCell.mbValue = false;
862 else
864 rCell.mfValue = pMat->GetDouble(0, 0);
865 rCell.mbValue = true;
868 break;
869 case svExternalDoubleRef:
870 // TODO: Find out how to handle this...
871 default:
872 SetError( errIllegalParameter);
873 break;
876 if( nGlobalError )
877 return 0;
878 nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL;
879 return sc::CompareFunc(aComp.maCells[0], aComp.maCells[1], aComp.mbIgnoreCase);
883 sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions )
885 sc::Compare aComp;
886 aComp.meOp = eOp;
887 aComp.mbIgnoreCase = pDok->GetDocOptions().IsIgnoreCase();
888 sc::RangeMatrix aMat[2];
889 ScAddress aAdr;
890 for( short i = 1; i >= 0; i-- )
892 sc::Compare::Cell& rCell = aComp.maCells[i];
894 switch (GetRawStackType())
896 case svEmptyCell:
897 Pop();
898 rCell.mbEmpty = true;
899 break;
900 case svMissing:
901 case svDouble:
902 rCell.mfValue = GetDouble();
903 rCell.mbValue = true;
904 break;
905 case svString:
906 rCell.maStr = GetString();
907 rCell.mbValue = false;
908 break;
909 case svSingleRef:
911 PopSingleRef( aAdr );
912 ScRefCellValue aCell;
913 aCell.assign(*pDok, aAdr);
914 if (aCell.hasEmptyValue())
915 rCell.mbEmpty = true;
916 else if (aCell.hasString())
918 svl::SharedString aStr;
919 GetCellString(aStr, aCell);
920 rCell.maStr = aStr;
921 rCell.mbValue = false;
923 else
925 rCell.mfValue = GetCellValue(aAdr, aCell);
926 rCell.mbValue = true;
929 break;
930 case svDoubleRef:
931 case svMatrix:
932 aMat[i] = GetRangeMatrix();
933 if (!aMat[i].mpMat)
934 SetError( errIllegalParameter);
935 else
936 aMat[i].mpMat->SetErrorInterpreter(NULL);
937 // errors are transported as DoubleError inside matrix
938 break;
939 default:
940 SetError( errIllegalParameter);
941 break;
945 sc::RangeMatrix aRes;
947 if (nGlobalError)
949 nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL;
950 return aRes;
953 if (aMat[0].mpMat && aMat[1].mpMat)
955 SCSIZE nC0, nC1;
956 SCSIZE nR0, nR1;
957 aMat[0].mpMat->GetDimensions(nC0, nR0);
958 aMat[1].mpMat->GetDimensions(nC1, nR1);
959 SCSIZE nC = std::max( nC0, nC1 );
960 SCSIZE nR = std::max( nR0, nR1 );
961 aRes.mpMat = GetNewMat( nC, nR);
962 if (!aRes.mpMat)
963 return aRes;
964 for ( SCSIZE j=0; j<nC; j++ )
966 for ( SCSIZE k=0; k<nR; k++ )
968 SCSIZE nCol = j, nRow = k;
969 if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) &&
970 aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow))
972 for ( short i=1; i>=0; i-- )
974 sc::Compare::Cell& rCell = aComp.maCells[i];
976 if (aMat[i].mpMat->IsString(j, k))
978 rCell.mbValue = false;
979 rCell.maStr = aMat[i].mpMat->GetString(j, k);
980 rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k);
982 else
984 rCell.mbValue = true;
985 rCell.mfValue = aMat[i].mpMat->GetDouble(j, k);
986 rCell.mbEmpty = false;
989 aRes.mpMat->PutDouble(
990 sc::CompareFunc(aComp.maCells[0], aComp.maCells[1], aComp.mbIgnoreCase, pOptions), j, k);
992 else
993 aRes.mpMat->PutString(mrStrPool.intern(ScGlobal::GetRscString(STR_NO_VALUE)), j, k);
997 switch (eOp)
999 case SC_EQUAL:
1000 aRes.mpMat->CompareEqual();
1001 break;
1002 case SC_LESS:
1003 aRes.mpMat->CompareLess();
1004 break;
1005 case SC_GREATER:
1006 aRes.mpMat->CompareGreater();
1007 break;
1008 case SC_LESS_EQUAL:
1009 aRes.mpMat->CompareLessEqual();
1010 break;
1011 case SC_GREATER_EQUAL:
1012 aRes.mpMat->CompareGreaterEqual();
1013 break;
1014 case SC_NOT_EQUAL:
1015 aRes.mpMat->CompareNotEqual();
1016 break;
1017 default:
1018 OSL_TRACE( "ScInterpreter::QueryMat: unhandled comparison operator: %d", (int)eOp);
1019 aRes.mpMat.reset();
1020 return aRes;
1023 else if (aMat[0].mpMat || aMat[1].mpMat)
1025 size_t i = ( aMat[0].mpMat ? 0 : 1);
1027 aRes.mnCol1 = aMat[i].mnCol1;
1028 aRes.mnRow1 = aMat[i].mnRow1;
1029 aRes.mnTab1 = aMat[i].mnTab1;
1030 aRes.mnCol2 = aMat[i].mnCol2;
1031 aRes.mnRow2 = aMat[i].mnRow2;
1032 aRes.mnTab2 = aMat[i].mnTab2;
1034 ScMatrix& rMat = *aMat[i].mpMat;
1035 aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions);
1036 if (!aRes.mpMat)
1037 return aRes;
1040 nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL;
1041 return aRes;
1045 ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions )
1047 short nSaveCurFmtType = nCurFmtType;
1048 short nSaveFuncFmtType = nFuncFmtType;
1049 PushMatrix( pMat);
1050 const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem();
1051 if (rItem.meType == ScQueryEntry::ByString)
1052 PushString(rItem.maString.getString());
1053 else
1054 PushDouble(rItem.mfVal);
1055 ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat;
1056 nCurFmtType = nSaveCurFmtType;
1057 nFuncFmtType = nSaveFuncFmtType;
1058 if (nGlobalError || !pResultMatrix)
1060 SetError( errIllegalParameter);
1061 return pResultMatrix;
1064 return pResultMatrix;
1067 void ScInterpreter::ScEqual()
1069 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1071 sc::RangeMatrix aMat = CompareMat(SC_EQUAL);
1072 if (!aMat.mpMat)
1074 PushIllegalParameter();
1075 return;
1078 PushMatrix(aMat);
1080 else
1081 PushInt( Compare() == 0 );
1085 void ScInterpreter::ScNotEqual()
1087 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1089 sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL);
1090 if (!aMat.mpMat)
1092 PushIllegalParameter();
1093 return;
1096 PushMatrix(aMat);
1098 else
1099 PushInt( Compare() != 0 );
1103 void ScInterpreter::ScLess()
1105 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1107 sc::RangeMatrix aMat = CompareMat(SC_LESS);
1108 if (!aMat.mpMat)
1110 PushIllegalParameter();
1111 return;
1114 PushMatrix(aMat);
1116 else
1117 PushInt( Compare() < 0 );
1121 void ScInterpreter::ScGreater()
1123 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1125 sc::RangeMatrix aMat = CompareMat(SC_GREATER);
1126 if (!aMat.mpMat)
1128 PushIllegalParameter();
1129 return;
1132 PushMatrix(aMat);
1134 else
1135 PushInt( Compare() > 0 );
1139 void ScInterpreter::ScLessEqual()
1141 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1143 sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL);
1144 if (!aMat.mpMat)
1146 PushIllegalParameter();
1147 return;
1150 PushMatrix(aMat);
1152 else
1153 PushInt( Compare() <= 0 );
1157 void ScInterpreter::ScGreaterEqual()
1159 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1161 sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL);
1162 if (!aMat.mpMat)
1164 PushIllegalParameter();
1165 return;
1168 PushMatrix(aMat);
1170 else
1171 PushInt( Compare() >= 0 );
1175 void ScInterpreter::ScAnd()
1177 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1178 short nParamCount = GetByte();
1179 if ( MustHaveParamCountMin( nParamCount, 1 ) )
1181 bool bHaveValue = false;
1182 short nRes = true;
1183 size_t nRefInList = 0;
1184 while( nParamCount-- > 0)
1186 if ( !nGlobalError )
1188 switch ( GetStackType() )
1190 case svDouble :
1191 bHaveValue = true;
1192 nRes &= ( PopDouble() != 0.0 );
1193 break;
1194 case svString :
1195 Pop();
1196 SetError( errNoValue );
1197 break;
1198 case svSingleRef :
1200 ScAddress aAdr;
1201 PopSingleRef( aAdr );
1202 if ( !nGlobalError )
1204 ScRefCellValue aCell;
1205 aCell.assign(*pDok, aAdr);
1206 if (aCell.hasNumeric())
1208 bHaveValue = true;
1209 nRes &= ( GetCellValue(aAdr, aCell) != 0.0 );
1211 // else: Xcl raises no error here
1214 break;
1215 case svDoubleRef:
1216 case svRefList:
1218 ScRange aRange;
1219 PopDoubleRef( aRange, nParamCount, nRefInList);
1220 if ( !nGlobalError )
1222 double fVal;
1223 sal_uInt16 nErr = 0;
1224 ScValueIterator aValIter( pDok, aRange );
1225 if ( aValIter.GetFirst( fVal, nErr ) )
1227 bHaveValue = true;
1230 nRes &= ( fVal != 0.0 );
1231 } while ( (nErr == 0) &&
1232 aValIter.GetNext( fVal, nErr ) );
1234 SetError( nErr );
1237 break;
1238 case svExternalSingleRef:
1239 case svExternalDoubleRef:
1240 case svMatrix:
1242 ScMatrixRef pMat = GetMatrix();
1243 if ( pMat )
1245 bHaveValue = true;
1246 double fVal = pMat->And();
1247 sal_uInt16 nErr = GetDoubleErrorValue( fVal );
1248 if ( nErr )
1250 SetError( nErr );
1251 nRes = false;
1253 else
1254 nRes &= (fVal != 0.0);
1256 // else: GetMatrix did set errIllegalParameter
1258 break;
1259 default:
1260 Pop();
1261 SetError( errIllegalParameter);
1264 else
1265 Pop();
1267 if ( bHaveValue )
1268 PushInt( nRes );
1269 else
1270 PushNoValue();
1275 void ScInterpreter::ScOr()
1277 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1278 short nParamCount = GetByte();
1279 if ( MustHaveParamCountMin( nParamCount, 1 ) )
1281 bool bHaveValue = false;
1282 short nRes = false;
1283 size_t nRefInList = 0;
1284 while( nParamCount-- > 0)
1286 if ( !nGlobalError )
1288 switch ( GetStackType() )
1290 case svDouble :
1291 bHaveValue = true;
1292 nRes |= ( PopDouble() != 0.0 );
1293 break;
1294 case svString :
1295 Pop();
1296 SetError( errNoValue );
1297 break;
1298 case svSingleRef :
1300 ScAddress aAdr;
1301 PopSingleRef( aAdr );
1302 if ( !nGlobalError )
1304 ScRefCellValue aCell;
1305 aCell.assign(*pDok, aAdr);
1306 if (aCell.hasNumeric())
1308 bHaveValue = true;
1309 nRes |= ( GetCellValue(aAdr, aCell) != 0.0 );
1311 // else: Xcl raises no error here
1314 break;
1315 case svDoubleRef:
1316 case svRefList:
1318 ScRange aRange;
1319 PopDoubleRef( aRange, nParamCount, nRefInList);
1320 if ( !nGlobalError )
1322 double fVal;
1323 sal_uInt16 nErr = 0;
1324 ScValueIterator aValIter( pDok, aRange );
1325 if ( aValIter.GetFirst( fVal, nErr ) )
1327 bHaveValue = true;
1330 nRes |= ( fVal != 0.0 );
1331 } while ( (nErr == 0) &&
1332 aValIter.GetNext( fVal, nErr ) );
1334 SetError( nErr );
1337 break;
1338 case svExternalSingleRef:
1339 case svExternalDoubleRef:
1340 case svMatrix:
1342 bHaveValue = true;
1343 ScMatrixRef pMat = GetMatrix();
1344 if ( pMat )
1346 bHaveValue = true;
1347 double fVal = pMat->Or();
1348 sal_uInt16 nErr = GetDoubleErrorValue( fVal );
1349 if ( nErr )
1351 SetError( nErr );
1352 nRes = false;
1354 else
1355 nRes |= (fVal != 0.0);
1357 // else: GetMatrix did set errIllegalParameter
1359 break;
1360 default:
1361 Pop();
1362 SetError( errIllegalParameter);
1365 else
1366 Pop();
1368 if ( bHaveValue )
1369 PushInt( nRes );
1370 else
1371 PushNoValue();
1376 void ScInterpreter::ScXor()
1379 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1380 short nParamCount = GetByte();
1381 if ( MustHaveParamCountMin( nParamCount, 1 ) )
1383 bool bHaveValue = false;
1384 short nRes = false;
1385 size_t nRefInList = 0;
1386 while( nParamCount-- > 0)
1388 if ( !nGlobalError )
1390 switch ( GetStackType() )
1392 case svDouble :
1393 bHaveValue = true;
1394 nRes ^= ( PopDouble() != 0.0 );
1395 break;
1396 case svString :
1397 Pop();
1398 SetError( errNoValue );
1399 break;
1400 case svSingleRef :
1402 ScAddress aAdr;
1403 PopSingleRef( aAdr );
1404 if ( !nGlobalError )
1406 ScRefCellValue aCell;
1407 aCell.assign(*pDok, aAdr);
1408 if (aCell.hasNumeric())
1410 bHaveValue = true;
1411 nRes ^= ( GetCellValue(aAdr, aCell) != 0.0 );
1413 /* TODO: set error? Excel doesn't have XOR, but
1414 * doesn't set an error in this case for AND and
1415 * OR. */
1418 break;
1419 case svDoubleRef:
1420 case svRefList:
1422 ScRange aRange;
1423 PopDoubleRef( aRange, nParamCount, nRefInList);
1424 if ( !nGlobalError )
1426 double fVal;
1427 sal_uInt16 nErr = 0;
1428 ScValueIterator aValIter( pDok, aRange );
1429 if ( aValIter.GetFirst( fVal, nErr ) )
1431 bHaveValue = true;
1434 nRes ^= ( fVal != 0.0 );
1435 } while ( (nErr == 0) &&
1436 aValIter.GetNext( fVal, nErr ) );
1438 SetError( nErr );
1441 break;
1442 case svExternalSingleRef:
1443 case svExternalDoubleRef:
1444 case svMatrix:
1446 bHaveValue = true;
1447 ScMatrixRef pMat = GetMatrix();
1448 if ( pMat )
1450 bHaveValue = true;
1451 double fVal = pMat->Xor();
1452 sal_uInt16 nErr = GetDoubleErrorValue( fVal );
1453 if ( nErr )
1455 SetError( nErr );
1456 nRes = 0;
1458 else
1459 nRes ^= ( fVal != 0.0 );
1461 // else: GetMatrix did set errIllegalParameter
1463 break;
1464 default:
1465 Pop();
1466 SetError( errIllegalParameter);
1469 else
1470 Pop();
1472 if ( bHaveValue )
1473 PushInt( nRes );
1474 else
1475 PushNoValue();
1480 void ScInterpreter::ScNeg()
1482 // Simple negation doesn't change current format type to number, keep
1483 // current type.
1484 nFuncFmtType = nCurFmtType;
1485 switch ( GetStackType() )
1487 case svMatrix :
1489 ScMatrixRef pMat = GetMatrix();
1490 if ( !pMat )
1491 PushIllegalParameter();
1492 else
1494 SCSIZE nC, nR;
1495 pMat->GetDimensions( nC, nR );
1496 ScMatrixRef pResMat = GetNewMat( nC, nR);
1497 if ( !pResMat )
1498 PushIllegalArgument();
1499 else
1501 for (SCSIZE i = 0; i < nC; ++i)
1503 for (SCSIZE j = 0; j < nR; ++j)
1505 if ( pMat->IsValueOrEmpty(i,j) )
1506 pResMat->PutDouble( -pMat->GetDouble(i,j), i, j );
1507 else
1508 pResMat->PutString(
1509 mrStrPool.intern(ScGlobal::GetRscString(STR_NO_VALUE)), i, j);
1512 PushMatrix( pResMat );
1516 break;
1517 default:
1518 PushDouble( -GetDouble() );
1523 void ScInterpreter::ScPercentSign()
1525 nFuncFmtType = NUMBERFORMAT_PERCENT;
1526 const FormulaToken* pSaveCur = pCur;
1527 sal_uInt8 nSavePar = cPar;
1528 PushInt( 100 );
1529 cPar = 2;
1530 FormulaByteToken aDivOp( ocDiv, cPar );
1531 pCur = &aDivOp;
1532 ScDiv();
1533 pCur = pSaveCur;
1534 cPar = nSavePar;
1538 void ScInterpreter::ScNot()
1540 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1541 switch ( GetStackType() )
1543 case svMatrix :
1545 ScMatrixRef pMat = GetMatrix();
1546 if ( !pMat )
1547 PushIllegalParameter();
1548 else
1550 SCSIZE nC, nR;
1551 pMat->GetDimensions( nC, nR );
1552 ScMatrixRef pResMat = GetNewMat( nC, nR);
1553 if ( !pResMat )
1554 PushIllegalArgument();
1555 else
1557 for (SCSIZE i = 0; i < nC; ++i)
1559 for (SCSIZE j = 0; j < nR; ++j)
1561 if ( pMat->IsValueOrEmpty(i,j) )
1562 pResMat->PutDouble( (pMat->GetDouble(i,j) == 0.0), i, j );
1563 else
1564 pResMat->PutString(
1565 mrStrPool.intern(ScGlobal::GetRscString(STR_NO_VALUE)), i, j);
1568 PushMatrix( pResMat );
1572 break;
1573 default:
1574 PushInt( GetDouble() == 0.0 );
1579 void ScInterpreter::ScBitAnd()
1582 if ( !MustHaveParamCount( GetByte(), 2 ) )
1583 return;
1585 double num1 = ::rtl::math::approxFloor( GetDouble());
1586 double num2 = ::rtl::math::approxFloor( GetDouble());
1587 if ( (num1 >= n2power48) || (num1 < 0) ||
1588 (num2 >= n2power48) || (num2 < 0))
1589 PushIllegalArgument();
1590 else
1591 PushDouble ((sal_uInt64) num1 & (sal_uInt64) num2);
1595 void ScInterpreter::ScBitOr()
1598 if ( !MustHaveParamCount( GetByte(), 2 ) )
1599 return;
1601 double num1 = ::rtl::math::approxFloor( GetDouble());
1602 double num2 = ::rtl::math::approxFloor( GetDouble());
1603 if ( (num1 >= n2power48) || (num1 < 0) ||
1604 (num2 >= n2power48) || (num2 < 0))
1605 PushIllegalArgument();
1606 else
1607 PushDouble ((sal_uInt64) num1 | (sal_uInt64) num2);
1611 void ScInterpreter::ScBitXor()
1614 if ( !MustHaveParamCount( GetByte(), 2 ) )
1615 return;
1617 double num1 = ::rtl::math::approxFloor( GetDouble());
1618 double num2 = ::rtl::math::approxFloor( GetDouble());
1619 if ( (num1 >= n2power48) || (num1 < 0) ||
1620 (num2 >= n2power48) || (num2 < 0))
1621 PushIllegalArgument();
1622 else
1623 PushDouble ((sal_uInt64) num1 ^ (sal_uInt64) num2);
1627 void ScInterpreter::ScBitLshift()
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 = ::rtl::math::approxFloor( num / pow( 2.0, -fShift));
1642 else if (fShift == 0)
1643 fRes = num;
1644 else
1645 fRes = num * pow( 2.0, fShift);
1646 PushDouble( fRes);
1651 void ScInterpreter::ScBitRshift()
1654 if ( !MustHaveParamCount( GetByte(), 2 ) )
1655 return;
1657 double fShift = ::rtl::math::approxFloor( GetDouble());
1658 double num = ::rtl::math::approxFloor( GetDouble());
1659 if ((num >= n2power48) || (num < 0))
1660 PushIllegalArgument();
1661 else
1663 double fRes;
1664 if (fShift < 0)
1665 fRes = num * pow( 2.0, -fShift);
1666 else if (fShift == 0)
1667 fRes = num;
1668 else
1669 fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift));
1670 PushDouble( fRes);
1675 void ScInterpreter::ScPi()
1677 PushDouble(F_PI);
1681 void ScInterpreter::ScRandom()
1683 PushDouble(sc::rng::uniform());
1687 void ScInterpreter::ScTrue()
1689 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1690 PushInt(1);
1694 void ScInterpreter::ScFalse()
1696 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1697 PushInt(0);
1701 void ScInterpreter::ScDeg()
1703 PushDouble((GetDouble() / F_PI) * 180.0);
1707 void ScInterpreter::ScRad()
1709 PushDouble(GetDouble() * (F_PI / 180));
1713 void ScInterpreter::ScSin()
1715 PushDouble(::rtl::math::sin(GetDouble()));
1719 void ScInterpreter::ScCos()
1721 PushDouble(::rtl::math::cos(GetDouble()));
1725 void ScInterpreter::ScTan()
1727 PushDouble(::rtl::math::tan(GetDouble()));
1731 void ScInterpreter::ScCot()
1733 PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1737 void ScInterpreter::ScArcSin()
1739 PushDouble(asin(GetDouble()));
1743 void ScInterpreter::ScArcCos()
1745 PushDouble(acos(GetDouble()));
1749 void ScInterpreter::ScArcTan()
1751 PushDouble(atan(GetDouble()));
1755 void ScInterpreter::ScArcCot()
1757 PushDouble((F_PI2) - atan(GetDouble()));
1761 void ScInterpreter::ScSinHyp()
1763 PushDouble(sinh(GetDouble()));
1767 void ScInterpreter::ScCosHyp()
1769 PushDouble(cosh(GetDouble()));
1773 void ScInterpreter::ScTanHyp()
1775 PushDouble(tanh(GetDouble()));
1779 void ScInterpreter::ScCotHyp()
1781 PushDouble(1.0 / tanh(GetDouble()));
1785 void ScInterpreter::ScArcSinHyp()
1787 PushDouble( ::rtl::math::asinh( GetDouble()));
1790 void ScInterpreter::ScArcCosHyp()
1792 double fVal = GetDouble();
1793 if (fVal < 1.0)
1794 PushIllegalArgument();
1795 else
1796 PushDouble( ::rtl::math::acosh( fVal));
1799 void ScInterpreter::ScArcTanHyp()
1801 double fVal = GetDouble();
1802 if (fabs(fVal) >= 1.0)
1803 PushIllegalArgument();
1804 else
1805 PushDouble( ::rtl::math::atanh( fVal));
1809 void ScInterpreter::ScArcCotHyp()
1811 double nVal = GetDouble();
1812 if (fabs(nVal) <= 1.0)
1813 PushIllegalArgument();
1814 else
1815 PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
1818 void ScInterpreter::ScCosecant()
1820 PushDouble(1.0 / ::rtl::math::sin(GetDouble()));
1823 void ScInterpreter::ScSecant()
1825 PushDouble(1.0 / ::rtl::math::cos(GetDouble()));
1828 void ScInterpreter::ScCosecantHyp()
1830 PushDouble(1.0 / sinh(GetDouble()));
1833 void ScInterpreter::ScSecantHyp()
1835 PushDouble(1.0 / cosh(GetDouble()));
1839 void ScInterpreter::ScExp()
1841 PushDouble(exp(GetDouble()));
1845 void ScInterpreter::ScSqrt()
1847 double fVal = GetDouble();
1848 if (fVal >= 0.0)
1849 PushDouble(sqrt(fVal));
1850 else
1851 PushIllegalArgument();
1855 void ScInterpreter::ScIsEmpty()
1857 short nRes = 0;
1858 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1859 switch ( GetRawStackType() )
1861 case svEmptyCell:
1863 FormulaTokenRef p = PopToken();
1864 if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
1865 nRes = 1;
1867 break;
1868 case svDoubleRef :
1869 case svSingleRef :
1871 ScAddress aAdr;
1872 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1873 break;
1874 // NOTE: this could test also on inherited emptiness, but then the
1875 // cell tested wouldn't be empty. Must correspond with
1876 // ScCountEmptyCells().
1877 ScRefCellValue aCell;
1878 aCell.assign(*pDok, aAdr);
1879 if (aCell.meType == CELLTYPE_NONE)
1880 nRes = 1;
1882 break;
1883 case svExternalSingleRef:
1884 case svExternalDoubleRef:
1885 case svMatrix:
1887 ScMatrixRef pMat = GetMatrix();
1888 if ( !pMat )
1889 ; // nothing
1890 else if ( !pJumpMatrix )
1891 nRes = pMat->IsEmpty( 0, 0);
1892 else
1894 SCSIZE nCols, nRows, nC, nR;
1895 pMat->GetDimensions( nCols, nRows);
1896 pJumpMatrix->GetPos( nC, nR);
1897 if ( nC < nCols && nR < nRows )
1898 nRes = pMat->IsEmpty( nC, nR);
1899 // else: false, not empty (which is what Xcl does)
1902 break;
1903 default:
1904 Pop();
1906 nGlobalError = 0;
1907 PushInt( nRes );
1911 short ScInterpreter::IsString()
1913 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1914 short nRes = 0;
1915 switch ( GetRawStackType() )
1917 case svString:
1918 Pop();
1919 nRes = 1;
1920 break;
1921 case svDoubleRef :
1922 case svSingleRef :
1924 ScAddress aAdr;
1925 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1926 break;
1928 ScRefCellValue aCell;
1929 aCell.assign(*pDok, aAdr);
1930 if (GetCellErrCode(aCell) == 0)
1932 switch (aCell.meType)
1934 case CELLTYPE_STRING :
1935 case CELLTYPE_EDIT :
1936 nRes = 1;
1937 break;
1938 case CELLTYPE_FORMULA :
1939 nRes = (!aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
1940 break;
1941 default:
1942 ; // nothing
1946 break;
1947 case svMatrix:
1949 ScMatrixRef pMat = PopMatrix();
1950 if ( !pMat )
1951 ; // nothing
1952 else if ( !pJumpMatrix )
1953 nRes = pMat->IsString(0, 0) && !pMat->IsEmpty(0, 0);
1954 else
1956 SCSIZE nCols, nRows, nC, nR;
1957 pMat->GetDimensions( nCols, nRows);
1958 pJumpMatrix->GetPos( nC, nR);
1959 if ( nC < nCols && nR < nRows )
1960 nRes = pMat->IsString( nC, nR) && !pMat->IsEmpty( nC, nR);
1963 break;
1964 default:
1965 Pop();
1967 nGlobalError = 0;
1968 return nRes;
1972 void ScInterpreter::ScIsString()
1974 PushInt( IsString() );
1978 void ScInterpreter::ScIsNonString()
1980 PushInt( !IsString() );
1984 void ScInterpreter::ScIsLogical()
1986 short nRes = 0;
1987 switch ( GetStackType() )
1989 case svDoubleRef :
1990 case svSingleRef :
1992 ScAddress aAdr;
1993 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1994 break;
1996 ScRefCellValue aCell;
1997 aCell.assign(*pDok, aAdr);
1998 if (GetCellErrCode(aCell) == 0)
2000 if (aCell.hasNumeric())
2002 sal_uLong nFormat = GetCellNumberFormat(aAdr, aCell);
2003 nRes = (pFormatter->GetType(nFormat) == NUMBERFORMAT_LOGICAL);
2007 break;
2008 case svMatrix:
2009 // TODO: we don't have type information for arrays except
2010 // numerical/string.
2011 // Fall thru
2012 default:
2013 PopError();
2014 if ( !nGlobalError )
2015 nRes = ( nCurFmtType == NUMBERFORMAT_LOGICAL );
2017 nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL;
2018 nGlobalError = 0;
2019 PushInt( nRes );
2023 void ScInterpreter::ScType()
2025 short nType = 0;
2026 switch ( GetStackType() )
2028 case svDoubleRef :
2029 case svSingleRef :
2031 ScAddress aAdr;
2032 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2033 break;
2035 ScRefCellValue aCell;
2036 aCell.assign(*pDok, aAdr);
2037 if (GetCellErrCode(aCell) == 0)
2039 switch (aCell.meType)
2041 // NOTE: this is Xcl nonsense!
2042 case CELLTYPE_STRING :
2043 case CELLTYPE_EDIT :
2044 nType = 2;
2045 break;
2046 case CELLTYPE_VALUE :
2048 sal_uLong nFormat = GetCellNumberFormat(aAdr, aCell);
2049 if (pFormatter->GetType(nFormat) == NUMBERFORMAT_LOGICAL)
2050 nType = 4;
2051 else
2052 nType = 1;
2054 break;
2055 case CELLTYPE_FORMULA :
2056 nType = 8;
2057 break;
2058 default:
2059 PushIllegalArgument();
2062 else
2063 nType = 16;
2065 break;
2066 case svString:
2067 PopError();
2068 if ( nGlobalError )
2070 nType = 16;
2071 nGlobalError = 0;
2073 else
2074 nType = 2;
2075 break;
2076 case svMatrix:
2077 PopMatrix();
2078 if ( nGlobalError )
2080 nType = 16;
2081 nGlobalError = 0;
2083 else
2084 nType = 64;
2085 // we could return the type of one element if in JumpMatrix or
2086 // ForceArray mode, but Xcl doesn't ...
2087 break;
2088 default:
2089 PopError();
2090 if ( nGlobalError )
2092 nType = 16;
2093 nGlobalError = 0;
2095 else
2096 nType = 1;
2098 PushInt( nType );
2102 static inline bool lcl_FormatHasNegColor( const SvNumberformat* pFormat )
2104 return pFormat && pFormat->GetColor( 1 );
2108 static inline bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
2110 return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1);
2113 namespace {
2115 void getFormatString(SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr)
2117 bool bAppendPrec = true;
2118 sal_uInt16 nPrec, nLeading;
2119 bool bThousand, bIsRed;
2120 pFormatter->GetFormatSpecialInfo( nFormat, bThousand, bIsRed, nPrec, nLeading );
2122 switch( pFormatter->GetType( nFormat ) )
2124 case NUMBERFORMAT_NUMBER: if(bThousand) rFmtStr = ","; else rFmtStr = "F"; break;
2125 case NUMBERFORMAT_CURRENCY: rFmtStr = "C"; break;
2126 case NUMBERFORMAT_SCIENTIFIC: rFmtStr = "S"; break;
2127 case NUMBERFORMAT_PERCENT: rFmtStr = "P"; break;
2128 default:
2130 bAppendPrec = false;
2131 switch( pFormatter->GetIndexTableOffset( nFormat ) )
2133 case NF_DATE_SYSTEM_SHORT:
2134 case NF_DATE_SYS_DMMMYY:
2135 case NF_DATE_SYS_DDMMYY:
2136 case NF_DATE_SYS_DDMMYYYY:
2137 case NF_DATE_SYS_DMMMYYYY:
2138 case NF_DATE_DIN_DMMMYYYY:
2139 case NF_DATE_SYS_DMMMMYYYY:
2140 case NF_DATE_DIN_DMMMMYYYY: rFmtStr = "D1"; break;
2141 case NF_DATE_SYS_DDMMM: rFmtStr = "D2"; break;
2142 case NF_DATE_SYS_MMYY: rFmtStr = "D3"; break;
2143 case NF_DATETIME_SYSTEM_SHORT_HHMM:
2144 case NF_DATETIME_SYS_DDMMYYYY_HHMMSS:
2145 rFmtStr = "D4"; break;
2146 case NF_DATE_DIN_MMDD: rFmtStr = "D5"; break;
2147 case NF_TIME_HHMMSSAMPM: rFmtStr = "D6"; break;
2148 case NF_TIME_HHMMAMPM: rFmtStr = "D7"; break;
2149 case NF_TIME_HHMMSS: rFmtStr = "D8"; break;
2150 case NF_TIME_HHMM: rFmtStr = "D9"; break;
2151 default: rFmtStr = "G";
2155 if( bAppendPrec )
2156 rFmtStr += OUString::number(nPrec);
2157 const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat );
2158 if( lcl_FormatHasNegColor( pFormat ) )
2159 rFmtStr += "-";
2160 if( lcl_FormatHasOpenPar( pFormat ) )
2161 rFmtStr += "()";
2166 void ScInterpreter::ScCell()
2167 { // ATTRIBUTE ; [REF]
2168 sal_uInt8 nParamCount = GetByte();
2169 if( MustHaveParamCount( nParamCount, 1, 2 ) )
2171 ScAddress aCellPos( aPos );
2172 bool bError = false;
2173 if( nParamCount == 2 )
2175 switch (GetStackType())
2177 case svExternalSingleRef:
2178 case svExternalDoubleRef:
2180 // Let's handle external reference separately...
2181 ScCellExternal();
2182 return;
2184 default:
2187 bError = !PopDoubleRefOrSingleRef( aCellPos );
2189 OUString aInfoType = GetString().getString();
2190 if( bError || nGlobalError )
2191 PushIllegalParameter();
2192 else
2194 ScRefCellValue aCell;
2195 aCell.assign(*pDok, aCellPos);
2197 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell);
2199 // *** ADDRESS INFO ***
2200 if( aInfoType.equalsAscii( "COL" ) )
2201 { // column number (1-based)
2202 PushInt( aCellPos.Col() + 1 );
2204 else if( aInfoType.equalsAscii( "ROW" ) )
2205 { // row number (1-based)
2206 PushInt( aCellPos.Row() + 1 );
2208 else if( aInfoType.equalsAscii( "SHEET" ) )
2209 { // table number (1-based)
2210 PushInt( aCellPos.Tab() + 1 );
2212 else if( aInfoType.equalsAscii( "ADDRESS" ) )
2213 { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
2214 sal_uInt16 nFlags = (aCellPos.Tab() == aPos.Tab()) ? (SCA_ABS) : (SCA_ABS_3D);
2215 OUString aStr(aCellPos.Format(nFlags, pDok, pDok->GetAddressConvention()));
2216 PushString(aStr);
2218 else if( aInfoType.equalsAscii( "FILENAME" ) )
2219 { // file name and table name: 'FILENAME'#$TABLE
2220 SCTAB nTab = aCellPos.Tab();
2221 OUString aFuncResult;
2222 if( nTab < pDok->GetTableCount() )
2224 if( pDok->GetLinkMode( nTab ) == SC_LINK_VALUE )
2225 pDok->GetName( nTab, aFuncResult );
2226 else
2228 SfxObjectShell* pShell = pDok->GetDocumentShell();
2229 if( pShell && pShell->GetMedium() )
2231 OUStringBuffer aBuf;
2232 aBuf.append('\'');
2233 const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
2234 aBuf.append(rURLObj.GetMainURL(INetURLObject::DECODE_UNAMBIGUOUS));
2235 aBuf.appendAscii("'#$");
2236 OUString aTabName;
2237 pDok->GetName( nTab, aTabName );
2238 aBuf.append(aTabName);
2239 aFuncResult = aBuf.makeStringAndClear();
2243 PushString( aFuncResult );
2245 else if( aInfoType.equalsAscii( "COORD" ) )
2246 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
2247 // Yes, passing tab as col is intentional!
2248 OUStringBuffer aFuncResult;
2249 OUString aCellStr =
2250 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
2251 (SCA_COL_ABSOLUTE|SCA_VALID_COL), NULL, pDok->GetAddressConvention() );
2252 aFuncResult.append(aCellStr);
2253 aFuncResult.append(':');
2254 aCellStr = aCellPos.Format((SCA_COL_ABSOLUTE|SCA_VALID_COL|SCA_ROW_ABSOLUTE|SCA_VALID_ROW),
2255 NULL, pDok->GetAddressConvention());
2256 aFuncResult.append(aCellStr);
2257 PushString( aFuncResult.makeStringAndClear() );
2260 // *** CELL PROPERTIES ***
2261 else if( aInfoType.equalsAscii( "CONTENTS" ) )
2262 { // contents of the cell, no formatting
2263 if (aCell.hasString())
2265 svl::SharedString aStr;
2266 GetCellString(aStr, aCell);
2267 PushString( aStr );
2269 else
2270 PushDouble(GetCellValue(aCellPos, aCell));
2272 else if( aInfoType.equalsAscii( "TYPE" ) )
2273 { // b = blank; l = string (label); v = otherwise (value)
2274 sal_Unicode c;
2275 if (aCell.hasString())
2276 c = 'l';
2277 else
2278 c = aCell.hasNumeric() ? 'v' : 'b';
2279 PushString( OUString(c) );
2281 else if( aInfoType.equalsAscii( "WIDTH" ) )
2282 { // column width (rounded off as count of zero characters in standard font and size)
2283 Printer* pPrinter = pDok->GetPrinter();
2284 MapMode aOldMode( pPrinter->GetMapMode() );
2285 Font aOldFont( pPrinter->GetFont() );
2286 Font aDefFont;
2288 pPrinter->SetMapMode( MAP_TWIP );
2289 // font color doesn't matter here
2290 pDok->GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter );
2291 pPrinter->SetFont( aDefFont );
2292 long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) );
2293 pPrinter->SetFont( aOldFont );
2294 pPrinter->SetMapMode( aOldMode );
2295 int nZeroCount = (int)(pDok->GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
2296 PushInt( nZeroCount );
2298 else if( aInfoType.equalsAscii( "PREFIX" ) )
2299 { // ' = left; " = right; ^ = centered
2300 sal_Unicode c = 0;
2301 if (aCell.hasString())
2303 const SvxHorJustifyItem* pJustAttr = (const SvxHorJustifyItem*)
2304 pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_HOR_JUSTIFY );
2305 switch( pJustAttr->GetValue() )
2307 case SVX_HOR_JUSTIFY_STANDARD:
2308 case SVX_HOR_JUSTIFY_LEFT:
2309 case SVX_HOR_JUSTIFY_BLOCK: c = '\''; break;
2310 case SVX_HOR_JUSTIFY_CENTER: c = '^'; break;
2311 case SVX_HOR_JUSTIFY_RIGHT: c = '"'; break;
2312 case SVX_HOR_JUSTIFY_REPEAT: c = '\\'; break;
2315 PushString( OUString(c) );
2317 else if( aInfoType.equalsAscii( "PROTECT" ) )
2318 { // 1 = cell locked
2319 const ScProtectionAttr* pProtAttr = (const ScProtectionAttr*)
2320 pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_PROTECTION );
2321 PushInt( pProtAttr->GetProtection() ? 1 : 0 );
2324 // *** FORMATTING ***
2325 else if( aInfoType.equalsAscii( "FORMAT" ) )
2326 { // specific format code for standard formats
2327 OUString aFuncResult;
2328 sal_uLong nFormat = pDok->GetNumberFormat( aCellPos );
2329 getFormatString(pFormatter, nFormat, aFuncResult);
2330 PushString( aFuncResult );
2332 else if( aInfoType.equalsAscii( "COLOR" ) )
2333 { // 1 = negative values are colored, otherwise 0
2334 const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) );
2335 PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2337 else if( aInfoType.equalsAscii( "PARENTHESES" ) )
2338 { // 1 = format string contains a '(' character, otherwise 0
2339 const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) );
2340 PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2342 else
2343 PushIllegalArgument();
2348 void ScInterpreter::ScCellExternal()
2350 sal_uInt16 nFileId;
2351 OUString aTabName;
2352 ScSingleRefData aRef;
2353 ScExternalRefCache::TokenRef pToken;
2354 ScExternalRefCache::CellFormat aFmt;
2355 PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt);
2356 if (nGlobalError)
2358 PushIllegalParameter();
2359 return;
2362 OUString aInfoType = GetString().getString();
2363 if (nGlobalError)
2365 PushIllegalParameter();
2366 return;
2369 SCCOL nCol;
2370 SCROW nRow;
2371 SCTAB nTab;
2372 aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like.
2373 SingleRefToVars(aRef, nCol, nRow, nTab);
2374 if (nGlobalError)
2376 PushIllegalParameter();
2377 return;
2379 aRef.SetAbsTab(-1); // revert the value.
2381 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell);
2382 ScExternalRefManager* pRefMgr = pDok->GetExternalRefManager();
2384 if ( aInfoType == "COL" )
2385 PushInt(nCol + 1);
2386 else if ( aInfoType == "ROW" )
2387 PushInt(nRow + 1);
2388 else if ( aInfoType == "SHEET" )
2390 // For SHEET, No idea what number we should set, but let's always set
2391 // 1 if the external sheet exists, no matter what sheet. Excel does
2392 // the same.
2393 if (pRefMgr->getCacheTable(nFileId, aTabName, false, NULL).get())
2394 PushInt(1);
2395 else
2396 SetError(errNoName);
2398 else if ( aInfoType == "ADDRESS" )
2400 // ODF 1.2 says we need to always display address using the ODF A1 grammar.
2401 ScTokenArray aArray;
2402 aArray.AddExternalSingleReference(nFileId, aTabName, aRef);
2403 ScCompiler aComp(pDok, aPos, aArray);
2404 aComp.SetGrammar(formula::FormulaGrammar::GRAM_ODFF_A1);
2405 OUString aStr;
2406 aComp.CreateStringFromTokenArray(aStr);
2407 PushString(aStr);
2409 else if ( aInfoType == "FILENAME" )
2411 // 'file URI'#$SheetName
2413 const OUString* p = pRefMgr->getExternalFileName(nFileId);
2414 if (!p)
2416 // In theory this should never happen...
2417 SetError(errNoName);
2418 return;
2421 OUStringBuffer aBuf;
2422 aBuf.append('\'');
2423 aBuf.append(*p);
2424 aBuf.appendAscii("'#$");
2425 aBuf.append(aTabName);
2426 PushString(aBuf.makeStringAndClear());
2428 else if ( aInfoType == "CONTENTS" )
2430 switch (pToken->GetType())
2432 case svString:
2433 PushString(pToken->GetString());
2434 break;
2435 case svDouble:
2436 PushString(OUString::number(pToken->GetDouble()));
2437 break;
2438 case svError:
2439 PushString(ScGlobal::GetErrorString(pToken->GetError()));
2440 break;
2441 default:
2442 PushString(ScGlobal::GetEmptyOUString());
2445 else if ( aInfoType == "TYPE" )
2447 sal_Unicode c = 'v';
2448 switch (pToken->GetType())
2450 case svString:
2451 c = 'l';
2452 break;
2453 case svEmptyCell:
2454 c = 'b';
2455 break;
2456 default:
2459 PushString(OUString(c));
2461 else if ( aInfoType == "FORMAT" )
2463 OUString aFmtStr;
2464 sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0;
2465 getFormatString(pFormatter, nFmt, aFmtStr);
2466 PushString(aFmtStr);
2468 else if ( aInfoType == "COLOR" )
2470 // 1 = negative values are colored, otherwise 0
2471 int nVal = 0;
2472 if (aFmt.mbIsSet)
2474 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2475 nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0;
2477 PushInt(nVal);
2479 else if ( aInfoType == "PARENTHESES" )
2481 // 1 = format string contains a '(' character, otherwise 0
2482 int nVal = 0;
2483 if (aFmt.mbIsSet)
2485 const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex);
2486 nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0;
2488 PushInt(nVal);
2490 else
2491 PushIllegalParameter();
2494 void ScInterpreter::ScIsRef()
2496 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2497 short nRes = 0;
2498 switch ( GetStackType() )
2500 case svSingleRef :
2502 ScAddress aAdr;
2503 PopSingleRef( aAdr );
2504 if ( !nGlobalError )
2505 nRes = 1;
2507 break;
2508 case svDoubleRef :
2510 ScRange aRange;
2511 PopDoubleRef( aRange );
2512 if ( !nGlobalError )
2513 nRes = 1;
2515 break;
2516 case svRefList :
2518 FormulaTokenRef x = PopToken();
2519 if ( !nGlobalError )
2520 nRes = !static_cast<ScToken*>(x.get())->GetRefList()->empty();
2522 break;
2523 default:
2524 Pop();
2526 nGlobalError = 0;
2527 PushInt( nRes );
2531 void ScInterpreter::ScIsValue()
2533 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2534 short nRes = 0;
2535 switch ( GetRawStackType() )
2537 case svDouble:
2538 Pop();
2539 nRes = 1;
2540 break;
2541 case svDoubleRef :
2542 case svSingleRef :
2544 ScAddress aAdr;
2545 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2546 break;
2548 ScRefCellValue aCell;
2549 aCell.assign(*pDok, aAdr);
2550 if (GetCellErrCode(aCell) == 0)
2552 switch (aCell.meType)
2554 case CELLTYPE_VALUE :
2555 nRes = 1;
2556 break;
2557 case CELLTYPE_FORMULA :
2558 nRes = (aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty());
2559 break;
2560 default:
2561 ; // nothing
2565 break;
2566 case svMatrix:
2568 ScMatrixRef pMat = PopMatrix();
2569 if ( !pMat )
2570 ; // nothing
2571 else if ( !pJumpMatrix )
2573 if (pMat->GetErrorIfNotString( 0, 0) == 0)
2574 nRes = pMat->IsValue( 0, 0);
2576 else
2578 SCSIZE nCols, nRows, nC, nR;
2579 pMat->GetDimensions( nCols, nRows);
2580 pJumpMatrix->GetPos( nC, nR);
2581 if ( nC < nCols && nR < nRows )
2582 if (pMat->GetErrorIfNotString( nC, nR) == 0)
2583 nRes = pMat->IsValue( nC, nR);
2586 break;
2587 default:
2588 Pop();
2590 nGlobalError = 0;
2591 PushInt( nRes );
2595 void ScInterpreter::ScIsFormula()
2597 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2598 short nRes = 0;
2599 switch ( GetStackType() )
2601 case svDoubleRef :
2602 case svSingleRef :
2604 ScAddress aAdr;
2605 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2606 break;
2608 nRes = (pDok->GetCellType(aAdr) == CELLTYPE_FORMULA);
2610 break;
2611 default:
2612 Pop();
2614 nGlobalError = 0;
2615 PushInt( nRes );
2619 void ScInterpreter::ScFormula()
2621 OUString aFormula;
2622 switch ( GetStackType() )
2624 case svDoubleRef :
2625 case svSingleRef :
2627 ScAddress aAdr;
2628 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2629 break;
2631 ScRefCellValue aCell;
2632 aCell.assign(*pDok, aAdr);
2633 switch (aCell.meType)
2635 case CELLTYPE_FORMULA :
2636 aCell.mpFormula->GetFormula(aFormula);
2637 break;
2638 default:
2639 SetError( NOTAVAILABLE );
2642 break;
2643 default:
2644 Pop();
2645 SetError( NOTAVAILABLE );
2647 PushString( aFormula );
2652 void ScInterpreter::ScIsNV()
2654 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2655 short nRes = 0;
2656 switch ( GetStackType() )
2658 case svDoubleRef :
2659 case svSingleRef :
2661 ScAddress aAdr;
2662 PopDoubleRefOrSingleRef( aAdr );
2663 if ( nGlobalError == NOTAVAILABLE )
2664 nRes = 1;
2665 else
2667 ScRefCellValue aCell;
2668 aCell.assign(*pDok, aAdr);
2669 sal_uInt16 nErr = GetCellErrCode(aCell);
2670 nRes = (nErr == NOTAVAILABLE);
2673 break;
2674 case svMatrix:
2676 ScMatrixRef pMat = PopMatrix();
2677 if ( !pMat )
2678 ; // nothing
2679 else if ( !pJumpMatrix )
2680 nRes = (pMat->GetErrorIfNotString( 0, 0) == NOTAVAILABLE);
2681 else
2683 SCSIZE nCols, nRows, nC, nR;
2684 pMat->GetDimensions( nCols, nRows);
2685 pJumpMatrix->GetPos( nC, nR);
2686 if ( nC < nCols && nR < nRows )
2687 nRes = (pMat->GetErrorIfNotString( nC, nR) == NOTAVAILABLE);
2690 break;
2691 default:
2692 PopError();
2693 if ( nGlobalError == NOTAVAILABLE )
2694 nRes = 1;
2696 nGlobalError = 0;
2697 PushInt( nRes );
2701 void ScInterpreter::ScIsErr()
2703 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2704 short nRes = 0;
2705 switch ( GetStackType() )
2707 case svDoubleRef :
2708 case svSingleRef :
2710 ScAddress aAdr;
2711 PopDoubleRefOrSingleRef( aAdr );
2712 if ( nGlobalError && nGlobalError != NOTAVAILABLE )
2713 nRes = 1;
2714 else
2716 ScRefCellValue aCell;
2717 aCell.assign(*pDok, aAdr);
2718 sal_uInt16 nErr = GetCellErrCode(aCell);
2719 nRes = (nErr && nErr != NOTAVAILABLE);
2722 break;
2723 case svMatrix:
2725 ScMatrixRef pMat = PopMatrix();
2726 if ( nGlobalError || !pMat )
2727 nRes = ((nGlobalError && nGlobalError != NOTAVAILABLE) || !pMat);
2728 else if ( !pJumpMatrix )
2730 sal_uInt16 nErr = pMat->GetErrorIfNotString( 0, 0);
2731 nRes = (nErr && nErr != NOTAVAILABLE);
2733 else
2735 SCSIZE nCols, nRows, nC, nR;
2736 pMat->GetDimensions( nCols, nRows);
2737 pJumpMatrix->GetPos( nC, nR);
2738 if ( nC < nCols && nR < nRows )
2740 sal_uInt16 nErr = pMat->GetErrorIfNotString( nC, nR);
2741 nRes = (nErr && nErr != NOTAVAILABLE);
2745 break;
2746 default:
2747 PopError();
2748 if ( nGlobalError && nGlobalError != NOTAVAILABLE )
2749 nRes = 1;
2751 nGlobalError = 0;
2752 PushInt( nRes );
2756 void ScInterpreter::ScIsError()
2758 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2759 short nRes = 0;
2760 switch ( GetStackType() )
2762 case svDoubleRef :
2763 case svSingleRef :
2765 ScAddress aAdr;
2766 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2768 nRes = 1;
2769 break;
2771 if ( nGlobalError )
2772 nRes = 1;
2773 else
2775 ScRefCellValue aCell;
2776 aCell.assign(*pDok, aAdr);
2777 nRes = (GetCellErrCode(aCell) != 0);
2780 break;
2781 case svMatrix:
2783 ScMatrixRef pMat = PopMatrix();
2784 if ( nGlobalError || !pMat )
2785 nRes = 1;
2786 else if ( !pJumpMatrix )
2787 nRes = (pMat->GetErrorIfNotString( 0, 0) != 0);
2788 else
2790 SCSIZE nCols, nRows, nC, nR;
2791 pMat->GetDimensions( nCols, nRows);
2792 pJumpMatrix->GetPos( nC, nR);
2793 if ( nC < nCols && nR < nRows )
2794 nRes = (pMat->GetErrorIfNotString( nC, nR) != 0);
2797 break;
2798 default:
2799 PopError();
2800 if ( nGlobalError )
2801 nRes = 1;
2803 nGlobalError = 0;
2804 PushInt( nRes );
2808 short ScInterpreter::IsEven()
2810 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2811 short nRes = 0;
2812 double fVal = 0.0;
2813 switch ( GetStackType() )
2815 case svDoubleRef :
2816 case svSingleRef :
2818 ScAddress aAdr;
2819 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2820 break;
2822 ScRefCellValue aCell;
2823 aCell.assign(*pDok, aAdr);
2824 sal_uInt16 nErr = GetCellErrCode(aCell);
2825 if (nErr != 0)
2826 SetError(nErr);
2827 else
2829 switch (aCell.meType)
2831 case CELLTYPE_VALUE :
2832 fVal = GetCellValue(aAdr, aCell);
2833 nRes = 1;
2834 break;
2835 case CELLTYPE_FORMULA :
2836 if (aCell.mpFormula->IsValue())
2838 fVal = GetCellValue(aAdr, aCell);
2839 nRes = 1;
2841 break;
2842 default:
2843 ; // nothing
2847 break;
2848 case svDouble:
2850 fVal = PopDouble();
2851 nRes = 1;
2853 break;
2854 case svMatrix:
2856 ScMatrixRef pMat = PopMatrix();
2857 if ( !pMat )
2858 ; // nothing
2859 else if ( !pJumpMatrix )
2861 nRes = pMat->IsValue( 0, 0);
2862 if ( nRes )
2863 fVal = pMat->GetDouble( 0, 0);
2865 else
2867 SCSIZE nCols, nRows, nC, nR;
2868 pMat->GetDimensions( nCols, nRows);
2869 pJumpMatrix->GetPos( nC, nR);
2870 if ( nC < nCols && nR < nRows )
2872 nRes = pMat->IsValue( nC, nR);
2873 if ( nRes )
2874 fVal = pMat->GetDouble( nC, nR);
2876 else
2877 SetError( errNoValue);
2880 break;
2881 default:
2882 ; // nothing
2884 if ( !nRes )
2885 SetError( errIllegalParameter);
2886 else
2887 nRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
2888 return nRes;
2892 void ScInterpreter::ScIsEven()
2894 PushInt( IsEven() );
2898 void ScInterpreter::ScIsOdd()
2900 PushInt( !IsEven() );
2903 void ScInterpreter::ScN()
2905 sal_uInt16 nErr = nGlobalError;
2906 nGlobalError = 0;
2907 // Temporarily override the ConvertStringToValue() error for
2908 // GetCellValue() / GetCellValueOrZero()
2909 sal_uInt16 nSErr = mnStringNoValueError;
2910 mnStringNoValueError = errCellNoValue;
2911 double fVal = GetDouble();
2912 mnStringNoValueError = nSErr;
2913 if (nErr)
2914 nGlobalError = nErr; // preserve previous error if any
2915 else if (nGlobalError == errCellNoValue)
2916 nGlobalError = 0; // reset temporary detection error
2917 PushDouble(fVal);
2920 void ScInterpreter::ScTrim()
2922 // Doesn't only trim but also removes duplicated blanks within!
2923 OUString aVal = comphelper::string::strip(GetString().getString(), ' ');
2924 OUStringBuffer aStr;
2925 const sal_Unicode* p = aVal.getStr();
2926 const sal_Unicode* const pEnd = p + aVal.getLength();
2927 while ( p < pEnd )
2929 if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine
2930 aStr.append(*p);
2931 p++;
2933 PushString(aStr.makeStringAndClear());
2937 void ScInterpreter::ScUpper()
2939 OUString aString = ScGlobal::pCharClass->uppercase(GetString().getString());
2940 PushString(aString);
2944 void ScInterpreter::ScPropper()
2946 //2do: what to do with I18N-CJK ?!?
2947 OUStringBuffer aStr(GetString().getString());
2948 const sal_Int32 nLen = aStr.getLength();
2949 if ( nLen > 0 )
2951 OUString aUpr(ScGlobal::pCharClass->uppercase(aStr.toString()));
2952 OUString aLwr(ScGlobal::pCharClass->lowercase(aStr.toString()));
2953 aStr[0] = aUpr[0];
2954 sal_Int32 nPos = 1;
2955 while( nPos < nLen )
2957 OUString aTmpStr( aStr[nPos-1] );
2958 if ( !ScGlobal::pCharClass->isLetter( aTmpStr, 0 ) )
2959 aStr[nPos] = aUpr[nPos];
2960 else
2961 aStr[nPos] = aLwr[nPos];
2962 ++nPos;
2965 PushString(aStr.makeStringAndClear());
2969 void ScInterpreter::ScLower()
2971 OUString aString = ScGlobal::pCharClass->lowercase(GetString().getString());
2972 PushString(aString);
2976 void ScInterpreter::ScLen()
2978 PushDouble(GetString().getLength());
2982 void ScInterpreter::ScT()
2984 switch ( GetStackType() )
2986 case svDoubleRef :
2987 case svSingleRef :
2989 ScAddress aAdr;
2990 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2992 PushInt(0);
2993 return ;
2995 bool bValue = false;
2996 ScRefCellValue aCell;
2997 aCell.assign(*pDok, aAdr);
2998 if (GetCellErrCode(aCell) == 0)
3000 switch (aCell.meType)
3002 case CELLTYPE_VALUE :
3003 bValue = true;
3004 break;
3005 case CELLTYPE_FORMULA :
3006 bValue = aCell.mpFormula->IsValue();
3007 break;
3008 default:
3009 ; // nothing
3012 if ( bValue )
3013 PushString(EMPTY_OUSTRING);
3014 else
3016 // like GetString()
3017 svl::SharedString aStr;
3018 GetCellString(aStr, aCell);
3019 PushString(aStr);
3022 break;
3023 case svMatrix:
3024 case svExternalSingleRef:
3025 case svExternalDoubleRef:
3027 double fVal;
3028 svl::SharedString aStr;
3029 ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr);
3030 if (ScMatrix::IsValueType( nMatValType))
3031 PushString(svl::SharedString::getEmptyString());
3032 else
3033 PushString( aStr);
3035 break;
3036 case svDouble :
3038 PopError();
3039 PushString( EMPTY_OUSTRING );
3041 break;
3042 case svString :
3043 ; // leave on stack
3044 break;
3045 default :
3046 PushError( errUnknownOpCode);
3051 void ScInterpreter::ScValue()
3053 OUString aInputString;
3054 double fVal;
3056 switch ( GetRawStackType() )
3058 case svMissing:
3059 case svEmptyCell:
3060 Pop();
3061 PushInt(0);
3062 return;
3063 case svDouble:
3064 return; // leave on stack
3065 //break;
3067 case svSingleRef:
3068 case svDoubleRef:
3070 ScAddress aAdr;
3071 if ( !PopDoubleRefOrSingleRef( aAdr ) )
3073 PushInt(0);
3074 return;
3076 ScRefCellValue aCell;
3077 aCell.assign(*pDok, aAdr);
3078 if (aCell.hasString())
3080 svl::SharedString aSS;
3081 GetCellString(aSS, aCell);
3082 aInputString = aSS.getString();
3084 else if (aCell.hasNumeric())
3086 PushDouble( GetCellValue(aAdr, aCell) );
3087 return;
3089 else
3091 PushDouble(0.0);
3092 return;
3095 break;
3096 case svMatrix:
3098 svl::SharedString aSS;
3099 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
3100 aSS);
3101 aInputString = aSS.getString();
3102 switch (nType)
3104 case SC_MATVAL_EMPTY:
3105 fVal = 0.0;
3106 // fallthru
3107 case SC_MATVAL_VALUE:
3108 case SC_MATVAL_BOOLEAN:
3109 PushDouble( fVal);
3110 return;
3111 //break;
3112 case SC_MATVAL_STRING:
3113 // evaluated below
3114 break;
3115 default:
3116 PushIllegalArgument();
3119 break;
3120 default:
3121 aInputString = GetString().getString();
3122 break;
3125 sal_uInt32 nFIndex = 0; // 0 for default locale
3126 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
3127 PushDouble(fVal);
3128 else
3129 PushIllegalArgument();
3133 // fdo#57180
3134 void ScInterpreter::ScNumberValue()
3137 sal_uInt8 nParamCount = GetByte();
3138 if ( !MustHaveParamCount( nParamCount, 1, 3 ) )
3139 return;
3141 OUString aInputString;
3142 OUString aDecimalSeparator, aGroupSeparator;
3143 sal_Unicode cDecimalSeparator = 0;
3145 if ( nParamCount == 3 )
3146 aGroupSeparator = GetString().getString();
3148 if ( nParamCount >= 2 )
3150 aDecimalSeparator = GetString().getString();
3151 if ( aDecimalSeparator.getLength() == 1 )
3152 cDecimalSeparator = aDecimalSeparator[ 0 ];
3153 else
3155 PushIllegalArgument(); //if given, separator length must be 1
3156 return;
3160 if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 )
3162 PushIllegalArgument(); //decimal separator cannot appear in group separator
3163 return;
3166 switch (GetStackType())
3168 case svDouble:
3169 return; // leave on stack
3170 default:
3171 aInputString = GetString().getString();
3173 if ( nGlobalError )
3175 PushError( nGlobalError );
3176 return;
3178 if ( aInputString.isEmpty() )
3180 if ( GetGlobalConfig().mbEmptyStringAsZero )
3181 PushDouble( 0.0 );
3182 else
3183 PushNoValue();
3184 return;
3187 sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator );
3188 if ( nDecSep != 0 )
3190 OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString );
3191 sal_Int32 nIndex = 0;
3192 while (nIndex < aGroupSeparator.getLength())
3194 sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex );
3195 aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" );
3197 if ( nDecSep >= 0 )
3198 aInputString = aTemporary + aInputString.copy( nDecSep );
3199 else
3200 aInputString = aTemporary;
3203 for ( sal_Int32 i = aInputString.getLength(); --i >= 0; )
3205 sal_Unicode c = aInputString[ i ];
3206 if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D )
3207 aInputString = aInputString.replaceAt( i, 1, "" ); // remove spaces etc.
3209 sal_Int32 nPercentCount = 0;
3210 for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- )
3212 aInputString = aInputString.replaceAt( i, 1, "" ); // remove and count trailing '%'
3213 nPercentCount++;
3216 rtl_math_ConversionStatus eStatus;
3217 sal_Int32 nParseEnd;
3218 double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd );
3219 if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() )
3221 if (nPercentCount)
3222 fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string
3223 PushDouble(fVal);
3224 return;
3226 PushNoValue();
3230 //2do: this should be a proper unicode string method
3231 static inline bool lcl_ScInterpreter_IsPrintable( sal_Unicode c )
3233 return 0x20 <= c && c != 0x7f;
3236 void ScInterpreter::ScClean()
3238 OUString aStr = GetString().getString();
3239 for ( sal_Int32 i = 0; i < aStr.getLength(); i++ )
3241 if ( !lcl_ScInterpreter_IsPrintable( aStr[i] ) )
3242 aStr = aStr.replaceAt(i,1,"");
3244 PushString(aStr);
3248 void ScInterpreter::ScCode()
3250 //2do: make it full range unicode?
3251 OUString aStr = GetString().getString();
3252 //"classic" ByteString conversion flags
3253 const sal_uInt32 convertFlags =
3254 RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE |
3255 RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE |
3256 RTL_UNICODETOTEXT_FLAGS_FLUSH |
3257 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT |
3258 RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT |
3259 RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE;
3260 PushInt( (sal_uChar) OUStringToOString(OUString(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar() );
3264 void ScInterpreter::ScChar()
3266 //2do: make it full range unicode?
3267 double fVal = GetDouble();
3268 if (fVal < 0.0 || fVal >= 256.0)
3269 PushIllegalArgument();
3270 else
3272 //"classic" ByteString conversion flags
3273 const sal_uInt32 convertFlags =
3274 RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT |
3275 RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT |
3276 RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT;
3278 sal_Char cEncodedChar = static_cast<sal_Char>(fVal);
3279 OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags);
3280 PushString(aStr);
3285 /* #i70213# fullwidth/halfwidth conversion provided by
3286 * Takashi Nakamoto <bluedwarf@ooo>
3287 * erAck: added Excel compatibility conversions as seen in issue's test case. */
3289 static OUString lcl_convertIntoHalfWidth( const OUString & rStr )
3291 static bool bFirstASCCall = true;
3292 static utl::TransliterationWrapper aTrans( ::comphelper::getProcessComponentContext(), 0 );
3294 if( bFirstASCCall )
3296 aTrans.loadModuleByImplName( OUString( "FULLWIDTH_HALFWIDTH_LIKE_ASC" ), LANGUAGE_SYSTEM );
3297 bFirstASCCall = false;
3300 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ), NULL );
3304 static OUString lcl_convertIntoFullWidth( const OUString & rStr )
3306 static bool bFirstJISCall = true;
3307 static utl::TransliterationWrapper aTrans( ::comphelper::getProcessComponentContext(), 0 );
3309 if( bFirstJISCall )
3311 aTrans.loadModuleByImplName( OUString( "HALFWIDTH_FULLWIDTH_LIKE_JIS" ), LANGUAGE_SYSTEM );
3312 bFirstJISCall = false;
3315 return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ), NULL );
3319 /* ODFF:
3320 * Summary: Converts half-width to full-width ASCII and katakana characters.
3321 * Semantics: Conversion is done for half-width ASCII and katakana characters,
3322 * other characters are simply copied from T to the result. This is the
3323 * complementary function to ASC.
3324 * For references regarding halfwidth and fullwidth characters see
3325 * http://www.unicode.org/reports/tr11/
3326 * http://www.unicode.org/charts/charindex2.html#H
3327 * http://www.unicode.org/charts/charindex2.html#F
3329 void ScInterpreter::ScJis()
3331 if (MustHaveParamCount( GetByte(), 1))
3332 PushString( lcl_convertIntoFullWidth( GetString().getString()));
3336 /* ODFF:
3337 * Summary: Converts full-width to half-width ASCII and katakana characters.
3338 * Semantics: Conversion is done for full-width ASCII and katakana characters,
3339 * other characters are simply copied from T to the result. This is the
3340 * complementary function to JIS.
3342 void ScInterpreter::ScAsc()
3344 if (MustHaveParamCount( GetByte(), 1))
3345 PushString( lcl_convertIntoHalfWidth( GetString().getString()));
3348 void ScInterpreter::ScUnicode()
3350 if ( MustHaveParamCount( GetByte(), 1 ) )
3352 OUString aStr = GetString().getString();
3353 if (aStr.isEmpty())
3354 PushIllegalParameter();
3355 else
3357 sal_Int32 i = 0;
3358 PushDouble(aStr.iterateCodePoints(&i));
3363 void ScInterpreter::ScUnichar()
3365 if ( MustHaveParamCount( GetByte(), 1 ) )
3367 double dVal = ::rtl::math::approxFloor( GetDouble() );
3368 if ((dVal < 0x000000) || (dVal > 0x10FFFF))
3369 PushIllegalArgument();
3370 else
3372 sal_uInt32 nCodePoint = static_cast<sal_uInt32>( dVal );
3373 OUString aStr( &nCodePoint, 1 );
3374 PushString( aStr );
3380 void ScInterpreter::ScMin( bool bTextAsZero )
3382 short nParamCount = GetByte();
3383 if (!MustHaveParamCountMin( nParamCount, 1))
3384 return;
3385 double nMin = ::std::numeric_limits<double>::max();
3386 double nVal = 0.0;
3387 ScAddress aAdr;
3388 ScRange aRange;
3389 size_t nRefInList = 0;
3390 while (nParamCount-- > 0)
3392 switch (GetStackType())
3394 case svDouble :
3396 nVal = GetDouble();
3397 if (nMin > nVal) nMin = nVal;
3398 nFuncFmtType = NUMBERFORMAT_NUMBER;
3400 break;
3401 case svSingleRef :
3403 PopSingleRef( aAdr );
3404 ScRefCellValue aCell;
3405 aCell.assign(*pDok, aAdr);
3406 if (aCell.hasNumeric())
3408 nVal = GetCellValue(aAdr, aCell);
3409 CurFmtToFuncFmt();
3410 if (nMin > nVal) nMin = nVal;
3412 else if (bTextAsZero && aCell.hasString())
3414 if ( nMin > 0.0 )
3415 nMin = 0.0;
3418 break;
3419 case svDoubleRef :
3420 case svRefList :
3422 sal_uInt16 nErr = 0;
3423 PopDoubleRef( aRange, nParamCount, nRefInList);
3424 ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero );
3425 if (aValIter.GetFirst(nVal, nErr))
3427 if (nMin > nVal)
3428 nMin = nVal;
3429 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3430 while ((nErr == 0) && aValIter.GetNext(nVal, nErr))
3432 if (nMin > nVal)
3433 nMin = nVal;
3435 SetError(nErr);
3438 break;
3439 case svMatrix :
3440 case svExternalSingleRef:
3441 case svExternalDoubleRef:
3443 ScMatrixRef pMat = GetMatrix();
3444 if (pMat)
3446 nFuncFmtType = NUMBERFORMAT_NUMBER;
3447 nVal = pMat->GetMinValue(bTextAsZero);
3448 if (nMin > nVal)
3449 nMin = nVal;
3452 break;
3453 case svString :
3455 Pop();
3456 if ( bTextAsZero )
3458 if ( nMin > 0.0 )
3459 nMin = 0.0;
3461 else
3462 SetError(errIllegalParameter);
3464 break;
3465 default :
3466 Pop();
3467 SetError(errIllegalParameter);
3470 if ( nVal < nMin )
3471 PushDouble(0.0);
3472 else
3473 PushDouble(nMin);
3476 void ScInterpreter::ScMax( bool bTextAsZero )
3478 short nParamCount = GetByte();
3479 if (!MustHaveParamCountMin( nParamCount, 1))
3480 return;
3481 double nMax = -(::std::numeric_limits<double>::max());
3482 double nVal = 0.0;
3483 ScAddress aAdr;
3484 ScRange aRange;
3485 size_t nRefInList = 0;
3486 while (nParamCount-- > 0)
3488 switch (GetStackType())
3490 case svDouble :
3492 nVal = GetDouble();
3493 if (nMax < nVal) nMax = nVal;
3494 nFuncFmtType = NUMBERFORMAT_NUMBER;
3496 break;
3497 case svSingleRef :
3499 PopSingleRef( aAdr );
3500 ScRefCellValue aCell;
3501 aCell.assign(*pDok, aAdr);
3502 if (aCell.hasNumeric())
3504 nVal = GetCellValue(aAdr, aCell);
3505 CurFmtToFuncFmt();
3506 if (nMax < nVal) nMax = nVal;
3508 else if (bTextAsZero && aCell.hasString())
3510 if ( nMax < 0.0 )
3511 nMax = 0.0;
3514 break;
3515 case svDoubleRef :
3516 case svRefList :
3518 sal_uInt16 nErr = 0;
3519 PopDoubleRef( aRange, nParamCount, nRefInList);
3520 ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero );
3521 if (aValIter.GetFirst(nVal, nErr))
3523 if (nMax < nVal)
3524 nMax = nVal;
3525 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3526 while ((nErr == 0) && aValIter.GetNext(nVal, nErr))
3528 if (nMax < nVal)
3529 nMax = nVal;
3531 SetError(nErr);
3534 break;
3535 case svMatrix :
3536 case svExternalSingleRef:
3537 case svExternalDoubleRef:
3539 ScMatrixRef pMat = GetMatrix();
3540 if (pMat)
3542 nFuncFmtType = NUMBERFORMAT_NUMBER;
3543 nVal = pMat->GetMaxValue(bTextAsZero);
3544 if (nMax < nVal)
3545 nMax = nVal;
3548 break;
3549 case svString :
3551 Pop();
3552 if ( bTextAsZero )
3554 if ( nMax < 0.0 )
3555 nMax = 0.0;
3557 else
3558 SetError(errIllegalParameter);
3560 break;
3561 default :
3562 Pop();
3563 SetError(errIllegalParameter);
3566 if ( nVal > nMax )
3567 PushDouble(0.0);
3568 else
3569 PushDouble(nMax);
3572 void ScInterpreter::GetStVarParams( double& rVal, double& rValCount,
3573 bool bTextAsZero )
3575 short nParamCount = GetByte();
3577 std::vector<double> values;
3578 double fSum = 0.0;
3579 double vSum = 0.0;
3580 double vMean = 0.0;
3581 double fVal = 0.0;
3582 rValCount = 0.0;
3583 ScAddress aAdr;
3584 ScRange aRange;
3585 size_t nRefInList = 0;
3586 while (nParamCount-- > 0)
3588 switch (GetStackType())
3590 case svDouble :
3592 fVal = GetDouble();
3593 values.push_back(fVal);
3594 fSum += fVal;
3595 rValCount++;
3597 break;
3598 case svSingleRef :
3600 PopSingleRef( aAdr );
3601 ScRefCellValue aCell;
3602 aCell.assign(*pDok, aAdr);
3603 if (aCell.hasNumeric())
3605 fVal = GetCellValue(aAdr, aCell);
3606 values.push_back(fVal);
3607 fSum += fVal;
3608 rValCount++;
3610 else if (bTextAsZero && aCell.hasString())
3612 values.push_back(0.0);
3613 rValCount++;
3616 break;
3617 case svDoubleRef :
3618 case svRefList :
3620 sal_uInt16 nErr = 0;
3621 PopDoubleRef( aRange, nParamCount, nRefInList);
3622 ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero );
3623 if (aValIter.GetFirst(fVal, nErr))
3627 values.push_back(fVal);
3628 fSum += fVal;
3629 rValCount++;
3631 while ((nErr == 0) && aValIter.GetNext(fVal, nErr));
3634 break;
3635 case svMatrix :
3637 ScMatrixRef pMat = PopMatrix();
3638 if (pMat)
3640 SCSIZE nC, nR;
3641 pMat->GetDimensions(nC, nR);
3642 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
3644 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
3646 if (!pMat->IsString(nMatCol,nMatRow))
3648 fVal= pMat->GetDouble(nMatCol,nMatRow);
3649 values.push_back(fVal);
3650 fSum += fVal;
3651 rValCount++;
3653 else if ( bTextAsZero )
3655 values.push_back(0.0);
3656 rValCount++;
3662 break;
3663 case svString :
3665 Pop();
3666 if ( bTextAsZero )
3668 values.push_back(0.0);
3669 rValCount++;
3671 else
3672 SetError(errIllegalParameter);
3674 break;
3675 default :
3676 Pop();
3677 SetError(errIllegalParameter);
3681 ::std::vector<double>::size_type n = values.size();
3682 vMean = fSum / n;
3683 for (::std::vector<double>::size_type i = 0; i < n; i++)
3684 vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean);
3685 rVal = vSum;
3689 void ScInterpreter::ScVar( bool bTextAsZero )
3691 double nVal;
3692 double nValCount;
3693 GetStVarParams( nVal, nValCount, bTextAsZero );
3695 if (nValCount <= 1.0)
3696 PushError( errDivisionByZero );
3697 else
3698 PushDouble( nVal / (nValCount - 1.0));
3702 void ScInterpreter::ScVarP( bool bTextAsZero )
3704 double nVal;
3705 double nValCount;
3706 GetStVarParams( nVal, nValCount, bTextAsZero );
3708 PushDouble( div( nVal, nValCount));
3712 void ScInterpreter::ScStDev( bool bTextAsZero )
3714 double nVal;
3715 double nValCount;
3716 GetStVarParams( nVal, nValCount, bTextAsZero );
3717 if (nValCount <= 1.0)
3718 PushError( errDivisionByZero );
3719 else
3720 PushDouble( sqrt( nVal / (nValCount - 1.0)));
3724 void ScInterpreter::ScStDevP( bool bTextAsZero )
3726 double nVal;
3727 double nValCount;
3728 GetStVarParams( nVal, nValCount, bTextAsZero );
3729 if (nValCount == 0.0)
3730 PushError( errDivisionByZero );
3731 else
3732 PushDouble( sqrt( nVal / nValCount));
3734 /* this was: PushDouble( sqrt( div( nVal, nValCount)));
3736 * Besides that the special NAN gets lost in the call through sqrt(),
3737 * unxlngi6.pro then looped back and forth somewhere between div() and
3738 * ::rtl::math::setNan(). Tests showed that
3740 * sqrt( div( 1, 0));
3742 * produced a loop, but
3744 * double f1 = div( 1, 0);
3745 * sqrt( f1 );
3747 * was fine. There seems to be some compiler optimization problem. It does
3748 * not occur when compiled with debug=t.
3753 void ScInterpreter::ScColumns()
3755 sal_uInt8 nParamCount = GetByte();
3756 sal_uLong nVal = 0;
3757 SCCOL nCol1;
3758 SCROW nRow1;
3759 SCTAB nTab1;
3760 SCCOL nCol2;
3761 SCROW nRow2;
3762 SCTAB nTab2;
3763 while (nParamCount-- > 0)
3765 switch ( GetStackType() )
3767 case svSingleRef:
3768 PopError();
3769 nVal++;
3770 break;
3771 case svDoubleRef:
3772 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3773 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
3774 static_cast<sal_uLong>(nCol2 - nCol1 + 1);
3775 break;
3776 case svMatrix:
3778 ScMatrixRef pMat = PopMatrix();
3779 if (pMat)
3781 SCSIZE nC, nR;
3782 pMat->GetDimensions(nC, nR);
3783 nVal += nC;
3786 break;
3787 case svExternalSingleRef:
3788 PopError();
3789 nVal++;
3790 break;
3791 case svExternalDoubleRef:
3793 sal_uInt16 nFileId;
3794 OUString aTabName;
3795 ScComplexRefData aRef;
3796 PopExternalDoubleRef( nFileId, aTabName, aRef);
3797 ScRange aAbs = aRef.toAbs(aPos);
3798 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
3799 static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1);
3801 break;
3802 default:
3803 PopError();
3804 SetError(errIllegalParameter);
3807 PushDouble((double)nVal);
3811 void ScInterpreter::ScRows()
3813 sal_uInt8 nParamCount = GetByte();
3814 sal_uLong nVal = 0;
3815 SCCOL nCol1;
3816 SCROW nRow1;
3817 SCTAB nTab1;
3818 SCCOL nCol2;
3819 SCROW nRow2;
3820 SCTAB nTab2;
3821 while (nParamCount-- > 0)
3823 switch ( GetStackType() )
3825 case svSingleRef:
3826 PopError();
3827 nVal++;
3828 break;
3829 case svDoubleRef:
3830 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3831 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) *
3832 static_cast<sal_uLong>(nRow2 - nRow1 + 1);
3833 break;
3834 case svMatrix:
3836 ScMatrixRef pMat = PopMatrix();
3837 if (pMat)
3839 SCSIZE nC, nR;
3840 pMat->GetDimensions(nC, nR);
3841 nVal += nR;
3844 break;
3845 case svExternalSingleRef:
3846 PopError();
3847 nVal++;
3848 break;
3849 case svExternalDoubleRef:
3851 sal_uInt16 nFileId;
3852 OUString aTabName;
3853 ScComplexRefData aRef;
3854 PopExternalDoubleRef( nFileId, aTabName, aRef);
3855 ScRange aAbs = aRef.toAbs(aPos);
3856 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) *
3857 static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1);
3859 break;
3860 default:
3861 PopError();
3862 SetError(errIllegalParameter);
3865 PushDouble((double)nVal);
3868 void ScInterpreter::ScTables()
3870 sal_uInt8 nParamCount = GetByte();
3871 sal_uLong nVal;
3872 if ( nParamCount == 0 )
3873 nVal = pDok->GetTableCount();
3874 else
3876 nVal = 0;
3877 SCCOL nCol1;
3878 SCROW nRow1;
3879 SCTAB nTab1;
3880 SCCOL nCol2;
3881 SCROW nRow2;
3882 SCTAB nTab2;
3883 while (nParamCount-- > 0)
3885 switch ( GetStackType() )
3887 case svSingleRef:
3888 PopError();
3889 nVal++;
3890 break;
3891 case svDoubleRef:
3892 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3893 nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1);
3894 break;
3895 case svMatrix:
3896 PopError();
3897 nVal++;
3898 break;
3899 case svExternalSingleRef:
3900 PopError();
3901 nVal++;
3902 break;
3903 case svExternalDoubleRef:
3905 sal_uInt16 nFileId;
3906 OUString aTabName;
3907 ScComplexRefData aRef;
3908 PopExternalDoubleRef( nFileId, aTabName, aRef);
3909 ScRange aAbs = aRef.toAbs(aPos);
3910 nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1);
3912 break;
3913 default:
3914 PopError();
3915 SetError( errIllegalParameter );
3919 PushDouble( (double) nVal );
3923 void ScInterpreter::ScColumn()
3925 sal_uInt8 nParamCount = GetByte();
3926 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
3928 double nVal = 0;
3929 if (nParamCount == 0)
3931 nVal = aPos.Col() + 1;
3932 if (bMatrixFormula)
3934 SCCOL nCols;
3935 SCROW nRows;
3936 pMyFormulaCell->GetMatColsRows( nCols, nRows);
3937 if (nCols == 0)
3939 // Happens if called via ScViewFunc::EnterMatrix()
3940 // ScFormulaCell::GetResultDimensions() as of course a
3941 // matrix result is not available yet.
3942 nCols = 1;
3944 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1);
3945 if (pResMat)
3947 for (SCCOL i=0; i < nCols; ++i)
3948 pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
3949 PushMatrix( pResMat);
3950 return;
3954 else
3956 switch ( GetStackType() )
3958 case svSingleRef :
3960 SCCOL nCol1;
3961 SCROW nRow1;
3962 SCTAB nTab1;
3963 PopSingleRef( nCol1, nRow1, nTab1 );
3964 nVal = (double) (nCol1 + 1);
3966 break;
3967 case svDoubleRef :
3969 SCCOL nCol1;
3970 SCROW nRow1;
3971 SCTAB nTab1;
3972 SCCOL nCol2;
3973 SCROW nRow2;
3974 SCTAB nTab2;
3975 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
3976 if (nCol2 > nCol1)
3978 ScMatrixRef pResMat = GetNewMat(
3979 static_cast<SCSIZE>(nCol2-nCol1+1), 1);
3980 if (pResMat)
3982 for (SCCOL i = nCol1; i <= nCol2; i++)
3983 pResMat->PutDouble((double)(i+1),
3984 static_cast<SCSIZE>(i-nCol1), 0);
3985 PushMatrix(pResMat);
3986 return;
3988 else
3989 nVal = 0.0;
3991 else
3992 nVal = (double) (nCol1 + 1);
3994 break;
3995 default:
3996 SetError( errIllegalParameter );
3997 nVal = 0.0;
4000 PushDouble( nVal );
4005 void ScInterpreter::ScRow()
4007 sal_uInt8 nParamCount = GetByte();
4008 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
4010 double nVal = 0;
4011 if (nParamCount == 0)
4013 nVal = aPos.Row() + 1;
4014 if (bMatrixFormula)
4016 SCCOL nCols;
4017 SCROW nRows;
4018 pMyFormulaCell->GetMatColsRows( nCols, nRows);
4019 if (nRows == 0)
4021 // Happens if called via ScViewFunc::EnterMatrix()
4022 // ScFormulaCell::GetResultDimensions() as of course a
4023 // matrix result is not available yet.
4024 nRows = 1;
4026 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows));
4027 if (pResMat)
4029 for (SCROW i=0; i < nRows; i++)
4030 pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
4031 PushMatrix( pResMat);
4032 return;
4036 else
4038 switch ( GetStackType() )
4040 case svSingleRef :
4042 SCCOL nCol1;
4043 SCROW nRow1;
4044 SCTAB nTab1;
4045 PopSingleRef( nCol1, nRow1, nTab1 );
4046 nVal = (double) (nRow1 + 1);
4048 break;
4049 case svDoubleRef :
4051 SCCOL nCol1;
4052 SCROW nRow1;
4053 SCTAB nTab1;
4054 SCCOL nCol2;
4055 SCROW nRow2;
4056 SCTAB nTab2;
4057 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4058 if (nRow2 > nRow1)
4060 ScMatrixRef pResMat = GetNewMat( 1,
4061 static_cast<SCSIZE>(nRow2-nRow1+1));
4062 if (pResMat)
4064 for (SCROW i = nRow1; i <= nRow2; i++)
4065 pResMat->PutDouble((double)(i+1), 0,
4066 static_cast<SCSIZE>(i-nRow1));
4067 PushMatrix(pResMat);
4068 return;
4070 else
4071 nVal = 0.0;
4073 else
4074 nVal = (double) (nRow1 + 1);
4076 break;
4077 default:
4078 SetError( errIllegalParameter );
4079 nVal = 0.0;
4082 PushDouble( nVal );
4086 void ScInterpreter::ScTable()
4088 sal_uInt8 nParamCount = GetByte();
4089 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
4091 SCTAB nVal = 0;
4092 if ( nParamCount == 0 )
4093 nVal = aPos.Tab() + 1;
4094 else
4096 switch ( GetStackType() )
4098 case svString :
4100 svl::SharedString aStr = PopString();
4101 if ( pDok->GetTable(aStr.getString(), nVal))
4102 ++nVal;
4103 else
4104 SetError( errIllegalArgument );
4106 break;
4107 case svSingleRef :
4109 SCCOL nCol1;
4110 SCROW nRow1;
4111 SCTAB nTab1;
4112 PopSingleRef( nCol1, nRow1, nTab1 );
4113 nVal = nTab1 + 1;
4115 break;
4116 case svDoubleRef :
4118 SCCOL nCol1;
4119 SCROW nRow1;
4120 SCTAB nTab1;
4121 SCCOL nCol2;
4122 SCROW nRow2;
4123 SCTAB nTab2;
4124 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4125 nVal = nTab1 + 1;
4127 break;
4128 default:
4129 SetError( errIllegalParameter );
4131 if ( nGlobalError )
4132 nVal = 0;
4134 PushDouble( (double) nVal );
4138 namespace {
4140 class VectorMatrixAccessor
4142 public:
4143 VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) :
4144 mrMat(rMat), mbColVec(bColVec) {}
4146 bool IsEmpty(SCSIZE i) const
4148 return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0);
4151 bool IsEmptyPath(SCSIZE i) const
4153 return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0);
4156 bool IsValue(SCSIZE i) const
4158 return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0);
4161 bool IsString(SCSIZE i) const
4163 return mbColVec ? mrMat.IsString(0, i) : mrMat.IsString(i, 0);
4166 double GetDouble(SCSIZE i) const
4168 return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0);
4171 OUString GetString(SCSIZE i) const
4173 return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString();
4176 SCSIZE GetElementCount() const
4178 SCSIZE nC, nR;
4179 mrMat.GetDimensions(nC, nR);
4180 return mbColVec ? nR : nC;
4183 private:
4184 const ScMatrix& mrMat;
4185 bool mbColVec;
4188 /** returns -1 when the matrix value is smaller than the query value, 0 when
4189 they are equal, and 1 when the matrix value is larger than the query
4190 value. */
4191 static sal_Int32 lcl_CompareMatrix2Query(
4192 SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry)
4194 if (rMat.IsEmpty(i))
4196 /* TODO: in case we introduced query for real empty this would have to
4197 * be changed! */
4198 return -1; // empty always less than anything else
4201 /* FIXME: what is an empty path (result of IF(false;true_path) in
4202 * comparisons? */
4204 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
4205 if (rMat.IsValue(i))
4207 if (bByString)
4208 return -1; // numeric always less than string
4210 const double nVal1 = rMat.GetDouble(i);
4211 const double nVal2 = rEntry.GetQueryItem().mfVal;
4212 if (nVal1 == nVal2)
4213 return 0;
4215 return nVal1 < nVal2 ? -1 : 1;
4218 if (!bByString)
4219 return 1; // string always greater than numeric
4221 OUString aStr1 = rMat.GetString(i);
4222 OUString aStr2 = rEntry.GetQueryItem().maString.getString();
4224 return ScGlobal::GetCollator()->compareString(aStr1, aStr2); // case-insensitive
4227 /** returns the last item with the identical value as the original item
4228 value. */
4229 static void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat,
4230 SCSIZE nMatCount, bool bReverse)
4232 if (rMat.IsValue(rIndex))
4234 double nVal = rMat.GetDouble(rIndex);
4235 if (bReverse)
4236 while (rIndex > 0 && rMat.IsValue(rIndex-1) &&
4237 nVal == rMat.GetDouble(rIndex-1))
4238 --rIndex;
4239 else
4240 while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
4241 nVal == rMat.GetDouble(rIndex+1))
4242 ++rIndex;
4244 //! Order of IsEmptyPath, IsEmpty, IsString is significant!
4245 else if (rMat.IsEmptyPath(rIndex))
4247 if (bReverse)
4248 while (rIndex > 0 && rMat.IsEmptyPath(rIndex-1))
4249 --rIndex;
4250 else
4251 while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
4252 ++rIndex;
4254 else if (rMat.IsEmpty(rIndex))
4256 if (bReverse)
4257 while (rIndex > 0 && rMat.IsEmpty(rIndex-1))
4258 --rIndex;
4259 else
4260 while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4261 ++rIndex;
4263 else if (rMat.IsString(rIndex))
4265 OUString aStr( rMat.GetString(rIndex));
4266 if (bReverse)
4267 while (rIndex > 0 && rMat.IsString(rIndex-1) &&
4268 aStr == rMat.GetString(rIndex-1))
4269 --rIndex;
4270 else
4271 while (rIndex < nMatCount-1 && rMat.IsString(rIndex+1) &&
4272 aStr == rMat.GetString(rIndex+1))
4273 ++rIndex;
4275 else
4277 OSL_FAIL("lcl_GetLastMatch: unhandled matrix type");
4283 void ScInterpreter::ScMatch()
4286 sal_uInt8 nParamCount = GetByte();
4287 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
4289 double fTyp;
4290 if (nParamCount == 3)
4291 fTyp = GetDouble();
4292 else
4293 fTyp = 1.0;
4294 SCCOL nCol1 = 0;
4295 SCROW nRow1 = 0;
4296 SCTAB nTab1 = 0;
4297 SCCOL nCol2 = 0;
4298 SCROW nRow2 = 0;
4299 SCTAB nTab2 = 0;
4300 ScMatrixRef pMatSrc = NULL;
4302 switch (GetStackType())
4304 case svSingleRef:
4305 PopSingleRef( nCol1, nRow1, nTab1);
4306 nCol2 = nCol1;
4307 nRow2 = nRow1;
4308 nTab2 = nTab1;
4309 break;
4310 case svDoubleRef:
4312 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4313 if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
4315 PushIllegalParameter();
4316 return;
4319 break;
4320 case svMatrix:
4321 case svExternalDoubleRef:
4323 if (GetStackType() == svMatrix)
4324 pMatSrc = PopMatrix();
4325 else
4326 PopExternalDoubleRef(pMatSrc);
4328 if (!pMatSrc)
4330 PushIllegalParameter();
4331 return;
4334 break;
4335 default:
4336 PushIllegalParameter();
4337 return;
4340 if (nGlobalError == 0)
4342 double fVal;
4343 ScQueryParam rParam;
4344 rParam.nCol1 = nCol1;
4345 rParam.nRow1 = nRow1;
4346 rParam.nCol2 = nCol2;
4347 rParam.nTab = nTab1;
4349 ScQueryEntry& rEntry = rParam.GetEntry(0);
4350 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4351 rEntry.bDoQuery = true;
4352 if (fTyp < 0.0)
4353 rEntry.eOp = SC_GREATER_EQUAL;
4354 else if (fTyp > 0.0)
4355 rEntry.eOp = SC_LESS_EQUAL;
4356 switch ( GetStackType() )
4358 case svDouble:
4360 fVal = GetDouble();
4361 rItem.mfVal = fVal;
4362 rItem.meType = ScQueryEntry::ByValue;
4364 break;
4365 case svString:
4367 rItem.meType = ScQueryEntry::ByString;
4368 rItem.maString = GetString();
4370 break;
4371 case svDoubleRef :
4372 case svSingleRef :
4374 ScAddress aAdr;
4375 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4377 PushInt(0);
4378 return ;
4380 ScRefCellValue aCell;
4381 aCell.assign(*pDok, aAdr);
4382 if (aCell.hasNumeric())
4384 fVal = GetCellValue(aAdr, aCell);
4385 rItem.meType = ScQueryEntry::ByValue;
4386 rItem.mfVal = fVal;
4388 else
4390 GetCellString(rItem.maString, aCell);
4391 rItem.meType = ScQueryEntry::ByString;
4394 break;
4395 case svExternalSingleRef:
4397 ScExternalRefCache::TokenRef pToken;
4398 PopExternalSingleRef(pToken);
4399 if (!pToken)
4401 PushInt(0);
4402 return;
4404 if (pToken->GetType() == svDouble)
4406 rItem.meType = ScQueryEntry::ByValue;
4407 rItem.mfVal = pToken->GetDouble();
4409 else
4411 rItem.meType = ScQueryEntry::ByString;
4412 rItem.maString = pToken->GetString();
4415 break;
4416 case svExternalDoubleRef:
4417 // TODO: Implement this.
4418 PushIllegalParameter();
4419 return;
4420 break;
4421 case svMatrix :
4423 svl::SharedString aStr;
4424 ScMatValType nType = GetDoubleOrStringFromMatrix(
4425 rItem.mfVal, aStr);
4426 rItem.maString = aStr;
4427 rItem.meType = ScMatrix::IsNonValueType(nType) ?
4428 ScQueryEntry::ByString : ScQueryEntry::ByValue;
4430 break;
4431 default:
4433 PushIllegalParameter();
4434 return;
4437 if (rItem.meType == ScQueryEntry::ByString)
4439 bool bIsVBAMode = false;
4440 if ( pDok )
4441 bIsVBAMode = pDok->IsInVBAMode();
4443 // #TODO handle MSO wildcards
4444 if ( bIsVBAMode )
4445 rParam.bRegExp = false;
4446 else
4447 rParam.bRegExp = MayBeRegExp(rEntry.GetQueryItem().maString.getString(), pDok);
4450 if (pMatSrc) // The source data is matrix array.
4452 SCSIZE nC, nR;
4453 pMatSrc->GetDimensions( nC, nR);
4454 if (nC > 1 && nR > 1)
4456 // The source matrix must be a vector.
4457 PushIllegalParameter();
4458 return;
4460 SCSIZE nMatCount = (nC == 1) ? nR : nC;
4461 VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1);
4463 // simple serial search for equality mode (source data doesn't
4464 // need to be sorted).
4466 if (rEntry.eOp == SC_EQUAL)
4468 for (SCSIZE i = 0; i < nMatCount; ++i)
4470 if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0)
4472 PushDouble(i+1); // found !
4473 return;
4476 PushNA(); // not found
4477 return;
4480 // binary search for non-equality mode (the source data is
4481 // assumed to be sorted).
4483 bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
4484 SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
4485 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
4487 SCSIZE nMid = nFirst + nLen/2;
4488 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry);
4489 if (nCmp == 0)
4491 // exact match. find the last item with the same value.
4492 lcl_GetLastMatch( nMid, aMatAcc, nMatCount, !bAscOrder);
4493 PushDouble( nMid+1);
4494 return;
4497 if (nLen == 1) // first and last items are next to each other.
4499 if (nCmp < 0)
4500 nHitIndex = bAscOrder ? nLast : nFirst;
4501 else
4502 nHitIndex = bAscOrder ? nFirst : nLast;
4503 break;
4506 if (nCmp < 0)
4508 if (bAscOrder)
4509 nFirst = nMid;
4510 else
4511 nLast = nMid;
4513 else
4515 if (bAscOrder)
4516 nLast = nMid;
4517 else
4518 nFirst = nMid;
4522 if (nHitIndex == nMatCount-1) // last item
4524 sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry);
4525 if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
4527 // either the last item is an exact match or the real
4528 // hit is beyond the last item.
4529 PushDouble( nHitIndex+1);
4530 return;
4534 if (nHitIndex > 0) // valid hit must be 2nd item or higher
4536 PushDouble( nHitIndex); // non-exact match
4537 return;
4540 PushNA();
4541 return;
4544 SCCOLROW nDelta = 0;
4545 if (nCol1 == nCol2)
4546 { // search row in column
4547 rParam.nRow2 = nRow2;
4548 rEntry.nField = nCol1;
4549 ScAddress aResultPos( nCol1, nRow1, nTab1);
4550 if (!LookupQueryWithCache( aResultPos, rParam))
4552 PushNA();
4553 return;
4555 nDelta = aResultPos.Row() - nRow1;
4557 else
4558 { // search column in row
4559 SCCOL nC;
4560 rParam.bByRow = false;
4561 rParam.nRow2 = nRow1;
4562 rEntry.nField = nCol1;
4563 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false);
4564 // Advance Entry.nField in Iterator if column changed
4565 aCellIter.SetAdvanceQueryParamEntryField( true );
4566 if (fTyp == 0.0)
4567 { // EQUAL
4568 if ( aCellIter.GetFirst() )
4569 nC = aCellIter.GetCol();
4570 else
4572 PushNA();
4573 return;
4576 else
4577 { // <= or >=
4578 SCROW nR;
4579 if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
4581 PushNA();
4582 return;
4585 nDelta = nC - nCol1;
4587 PushDouble((double) (nDelta + 1));
4589 else
4590 PushIllegalParameter();
4595 void ScInterpreter::ScCountEmptyCells()
4597 if ( MustHaveParamCount( GetByte(), 1 ) )
4599 sal_uLong nMaxCount = 0, nCount = 0;
4600 CellType eCellType;
4601 switch (GetStackType())
4603 case svSingleRef :
4605 nMaxCount = 1;
4606 ScAddress aAdr;
4607 PopSingleRef( aAdr );
4608 eCellType = pDok->GetCellType(aAdr);
4609 if (eCellType != CELLTYPE_NONE)
4610 nCount = 1;
4612 break;
4613 case svDoubleRef :
4614 case svRefList :
4616 ScRange aRange;
4617 short nParam = 1;
4618 size_t nRefInList = 0;
4619 while (nParam-- > 0)
4621 PopDoubleRef( aRange, nParam, nRefInList);
4622 nMaxCount +=
4623 static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
4624 static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
4625 static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
4627 ScCellIterator aIter( pDok, aRange, glSubTotal);
4628 for (bool bHas = aIter.first(); bHas; bHas = aIter.next())
4630 if (!aIter.hasEmptyData())
4631 ++nCount;
4635 break;
4636 default : SetError(errIllegalParameter); break;
4638 PushDouble(nMaxCount - nCount);
4643 double ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc )
4645 sal_uInt8 nParamCount = GetByte();
4646 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
4647 return 0;
4649 SCCOL nCol3 = 0;
4650 SCROW nRow3 = 0;
4651 SCTAB nTab3 = 0;
4653 ScMatrixRef pSumExtraMatrix;
4654 bool bSumExtraRange = (nParamCount == 3);
4655 if (bSumExtraRange)
4657 // Save only the upperleft cell in case of cell range. The geometry
4658 // of the 3rd parameter is taken from the 1st parameter.
4660 switch ( GetStackType() )
4662 case svDoubleRef :
4664 SCCOL nColJunk = 0;
4665 SCROW nRowJunk = 0;
4666 SCTAB nTabJunk = 0;
4667 PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
4668 if ( nTabJunk != nTab3 )
4670 SetError( errIllegalParameter);
4671 return 0;
4674 break;
4675 case svSingleRef :
4676 PopSingleRef( nCol3, nRow3, nTab3 );
4677 break;
4678 case svMatrix:
4679 pSumExtraMatrix = PopMatrix();
4680 //! nCol3, nRow3, nTab3 remain 0
4681 break;
4682 case svExternalSingleRef:
4684 pSumExtraMatrix = new ScMatrix(1, 1, 0.0);
4685 ScExternalRefCache::TokenRef pToken;
4686 PopExternalSingleRef(pToken);
4687 if (!pToken)
4689 SetError( errIllegalParameter);
4690 return 0;
4693 if (pToken->GetType() == svDouble)
4694 pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0);
4695 else
4696 pSumExtraMatrix->PutString(pToken->GetString(), 0, 0);
4698 break;
4699 case svExternalDoubleRef:
4700 PopExternalDoubleRef(pSumExtraMatrix);
4701 break;
4702 default:
4703 SetError( errIllegalParameter);
4704 return 0;
4708 svl::SharedString aString;
4709 double fVal = 0.0;
4710 bool bIsString = true;
4711 switch ( GetStackType() )
4713 case svDoubleRef :
4714 case svSingleRef :
4716 ScAddress aAdr;
4717 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4718 return 0;
4720 ScRefCellValue aCell;
4721 aCell.assign(*pDok, aAdr);
4722 switch (aCell.meType)
4724 case CELLTYPE_VALUE :
4725 fVal = GetCellValue(aAdr, aCell);
4726 bIsString = false;
4727 break;
4728 case CELLTYPE_FORMULA :
4729 if (aCell.mpFormula->IsValue())
4731 fVal = GetCellValue(aAdr, aCell);
4732 bIsString = false;
4734 else
4735 GetCellString(aString, aCell);
4736 break;
4737 case CELLTYPE_STRING :
4738 case CELLTYPE_EDIT :
4739 GetCellString(aString, aCell);
4740 break;
4741 default:
4742 fVal = 0.0;
4743 bIsString = false;
4746 break;
4747 case svString:
4748 aString = GetString();
4749 break;
4750 case svMatrix :
4751 case svExternalDoubleRef:
4753 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
4754 bIsString = ScMatrix::IsNonValueType( nType);
4756 break;
4757 case svExternalSingleRef:
4759 ScExternalRefCache::TokenRef pToken;
4760 PopExternalSingleRef(pToken);
4761 if (pToken)
4763 if (pToken->GetType() == svDouble)
4765 fVal = pToken->GetDouble();
4766 bIsString = false;
4768 else
4769 aString = pToken->GetString();
4772 break;
4773 default:
4775 fVal = GetDouble();
4776 bIsString = false;
4780 double fSum = 0.0;
4781 double fMem = 0.0;
4782 double fRes = 0.0;
4783 double fCount = 0.0;
4784 bool bNull = true;
4785 short nParam = 1;
4786 size_t nRefInList = 0;
4787 while (nParam-- > 0)
4789 SCCOL nCol1 = 0;
4790 SCROW nRow1 = 0;
4791 SCTAB nTab1 = 0;
4792 SCCOL nCol2 = 0;
4793 SCROW nRow2 = 0;
4794 SCTAB nTab2 = 0;
4795 ScMatrixRef pQueryMatrix;
4796 switch ( GetStackType() )
4798 case svRefList :
4799 if (bSumExtraRange)
4801 SetError( errIllegalParameter);
4803 else
4805 ScRange aRange;
4806 PopDoubleRef( aRange, nParam, nRefInList);
4807 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4809 break;
4810 case svDoubleRef :
4811 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4812 break;
4813 case svSingleRef :
4814 PopSingleRef( nCol1, nRow1, nTab1 );
4815 nCol2 = nCol1;
4816 nRow2 = nRow1;
4817 nTab2 = nTab1;
4818 break;
4819 case svMatrix:
4820 case svExternalSingleRef:
4821 case svExternalDoubleRef:
4823 pQueryMatrix = GetMatrix();
4824 if (!pQueryMatrix)
4826 SetError( errIllegalParameter);
4827 return 0;
4829 nCol1 = 0;
4830 nRow1 = 0;
4831 nTab1 = 0;
4832 SCSIZE nC, nR;
4833 pQueryMatrix->GetDimensions( nC, nR);
4834 nCol2 = static_cast<SCCOL>(nC - 1);
4835 nRow2 = static_cast<SCROW>(nR - 1);
4836 nTab2 = 0;
4838 break;
4839 default:
4840 SetError( errIllegalParameter);
4842 if ( nTab1 != nTab2 )
4844 SetError( errIllegalParameter);
4847 if (bSumExtraRange)
4849 // Take the range geometry of the 1st parameter and apply it to
4850 // the 3rd. If parts of the resulting range would point outside
4851 // the sheet, don't complain but silently ignore and simply cut
4852 // them away, this is what Xcl does :-/
4854 // For the cut-away part we also don't need to determine the
4855 // criteria match, so shrink the source range accordingly,
4856 // instead of the result range.
4857 SCCOL nColDelta = nCol2 - nCol1;
4858 SCROW nRowDelta = nRow2 - nRow1;
4859 SCCOL nMaxCol;
4860 SCROW nMaxRow;
4861 if (pSumExtraMatrix)
4863 SCSIZE nC, nR;
4864 pSumExtraMatrix->GetDimensions( nC, nR);
4865 nMaxCol = static_cast<SCCOL>(nC - 1);
4866 nMaxRow = static_cast<SCROW>(nR - 1);
4868 else
4870 nMaxCol = MAXCOL;
4871 nMaxRow = MAXROW;
4873 if (nCol3 + nColDelta > nMaxCol)
4875 SCCOL nNewDelta = nMaxCol - nCol3;
4876 nCol2 = nCol1 + nNewDelta;
4879 if (nRow3 + nRowDelta > nMaxRow)
4881 SCROW nNewDelta = nMaxRow - nRow3;
4882 nRow2 = nRow1 + nNewDelta;
4885 else
4887 nCol3 = nCol1;
4888 nRow3 = nRow1;
4889 nTab3 = nTab1;
4892 if (nGlobalError == 0)
4894 ScQueryParam rParam;
4895 rParam.nRow1 = nRow1;
4896 rParam.nRow2 = nRow2;
4898 ScQueryEntry& rEntry = rParam.GetEntry(0);
4899 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
4900 rEntry.bDoQuery = true;
4901 if (!bIsString)
4903 rItem.meType = ScQueryEntry::ByValue;
4904 rItem.mfVal = fVal;
4905 rEntry.eOp = SC_EQUAL;
4907 else
4909 rParam.FillInExcelSyntax(pDok->GetSharedStringPool(), aString.getString(), 0);
4910 sal_uInt32 nIndex = 0;
4911 bool bNumber = pFormatter->IsNumberFormat(
4912 rItem.maString.getString(), nIndex, rItem.mfVal);
4913 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
4914 if (rItem.meType == ScQueryEntry::ByString)
4915 rParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
4917 ScAddress aAdr;
4918 aAdr.SetTab( nTab3 );
4919 rParam.nCol1 = nCol1;
4920 rParam.nCol2 = nCol2;
4921 rEntry.nField = nCol1;
4922 SCsCOL nColDiff = nCol3 - nCol1;
4923 SCsROW nRowDiff = nRow3 - nRow1;
4924 if (pQueryMatrix)
4926 // Never case-sensitive.
4927 sc::CompareOptions aOptions( pDok, rEntry, rParam.bRegExp);
4928 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
4929 if (nGlobalError || !pResultMatrix)
4931 SetError( errIllegalParameter);
4934 if (pSumExtraMatrix)
4936 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
4938 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
4940 if (pResultMatrix->IsValue( nCol, nRow) &&
4941 pResultMatrix->GetDouble( nCol, nRow))
4943 SCSIZE nC = nCol + nColDiff;
4944 SCSIZE nR = nRow + nRowDiff;
4945 if (pSumExtraMatrix->IsValue( nC, nR))
4947 fVal = pSumExtraMatrix->GetDouble( nC, nR);
4948 ++fCount;
4949 if ( bNull && fVal != 0.0 )
4951 bNull = false;
4952 fMem = fVal;
4954 else
4955 fSum += fVal;
4961 else
4963 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
4965 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
4967 if (pResultMatrix->GetDouble( nCol, nRow))
4969 aAdr.SetCol( nCol + nColDiff);
4970 aAdr.SetRow( nRow + nRowDiff);
4971 ScRefCellValue aCell;
4972 aCell.assign(*pDok, aAdr);
4973 if (aCell.hasNumeric())
4975 fVal = GetCellValue(aAdr, aCell);
4976 ++fCount;
4977 if ( bNull && fVal != 0.0 )
4979 bNull = false;
4980 fMem = fVal;
4982 else
4983 fSum += fVal;
4990 else
4992 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false);
4993 // Increment Entry.nField in iterator when switching to next column.
4994 aCellIter.SetAdvanceQueryParamEntryField( true );
4995 if ( aCellIter.GetFirst() )
4997 if (pSumExtraMatrix)
5001 SCSIZE nC = aCellIter.GetCol() + nColDiff;
5002 SCSIZE nR = aCellIter.GetRow() + nRowDiff;
5003 if (pSumExtraMatrix->IsValue( nC, nR))
5005 fVal = pSumExtraMatrix->GetDouble( nC, nR);
5006 ++fCount;
5007 if ( bNull && fVal != 0.0 )
5009 bNull = false;
5010 fMem = fVal;
5012 else
5013 fSum += fVal;
5015 } while ( aCellIter.GetNext() );
5017 else
5021 aAdr.SetCol( aCellIter.GetCol() + nColDiff);
5022 aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
5023 ScRefCellValue aCell;
5024 aCell.assign(*pDok, aAdr);
5025 if (aCell.hasNumeric())
5027 fVal = GetCellValue(aAdr, aCell);
5028 ++fCount;
5029 if ( bNull && fVal != 0.0 )
5031 bNull = false;
5032 fMem = fVal;
5034 else
5035 fSum += fVal;
5037 } while ( aCellIter.GetNext() );
5042 else
5044 SetError( errIllegalParameter);
5045 return 0;
5049 switch( eFunc )
5051 case ifSUMIF: fRes = ::rtl::math::approxAdd( fSum, fMem ); break;
5052 case ifAVERAGEIF: fRes = div( ::rtl::math::approxAdd( fSum, fMem ), fCount); break;
5054 return fRes;
5057 void ScInterpreter::ScSumIf()
5059 PushDouble( IterateParametersIf( ifSUMIF));
5062 void ScInterpreter::ScAverageIf()
5064 PushDouble( IterateParametersIf( ifAVERAGEIF));
5067 void ScInterpreter::ScCountIf()
5069 if ( MustHaveParamCount( GetByte(), 2 ) )
5071 svl::SharedString aString;
5072 double fVal = 0.0;
5073 bool bIsString = true;
5074 switch ( GetStackType() )
5076 case svDoubleRef :
5077 case svSingleRef :
5079 ScAddress aAdr;
5080 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5082 PushInt(0);
5083 return ;
5085 ScRefCellValue aCell;
5086 aCell.assign(*pDok, aAdr);
5087 switch (aCell.meType)
5089 case CELLTYPE_VALUE :
5090 fVal = GetCellValue(aAdr, aCell);
5091 bIsString = false;
5092 break;
5093 case CELLTYPE_FORMULA :
5094 if (aCell.mpFormula->IsValue())
5096 fVal = GetCellValue(aAdr, aCell);
5097 bIsString = false;
5099 else
5100 GetCellString(aString, aCell);
5101 break;
5102 case CELLTYPE_STRING :
5103 case CELLTYPE_EDIT :
5104 GetCellString(aString, aCell);
5105 break;
5106 default:
5107 fVal = 0.0;
5108 bIsString = false;
5111 break;
5112 case svMatrix:
5113 case svExternalSingleRef:
5114 case svExternalDoubleRef:
5116 ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString);
5117 bIsString = ScMatrix::IsNonValueType( nType);
5119 break;
5120 case svString:
5121 aString = GetString();
5122 break;
5123 default:
5125 fVal = GetDouble();
5126 bIsString = false;
5129 double fCount = 0.0;
5130 short nParam = 1;
5131 size_t nRefInList = 0;
5132 while (nParam-- > 0)
5134 SCCOL nCol1 = 0;
5135 SCROW nRow1 = 0;
5136 SCTAB nTab1 = 0;
5137 SCCOL nCol2 = 0;
5138 SCROW nRow2 = 0;
5139 SCTAB nTab2 = 0;
5140 ScMatrixRef pQueryMatrix;
5141 switch ( GetStackType() )
5143 case svDoubleRef :
5144 case svRefList :
5146 ScRange aRange;
5147 PopDoubleRef( aRange, nParam, nRefInList);
5148 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5150 break;
5151 case svSingleRef :
5152 PopSingleRef( nCol1, nRow1, nTab1 );
5153 nCol2 = nCol1;
5154 nRow2 = nRow1;
5155 nTab2 = nTab1;
5156 break;
5157 case svMatrix:
5158 case svExternalSingleRef:
5159 case svExternalDoubleRef:
5161 pQueryMatrix = GetMatrix();
5162 if (!pQueryMatrix)
5164 PushIllegalParameter();
5165 return;
5167 nCol1 = 0;
5168 nRow1 = 0;
5169 nTab1 = 0;
5170 SCSIZE nC, nR;
5171 pQueryMatrix->GetDimensions( nC, nR);
5172 nCol2 = static_cast<SCCOL>(nC - 1);
5173 nRow2 = static_cast<SCROW>(nR - 1);
5174 nTab2 = 0;
5176 break;
5177 default:
5178 PushIllegalParameter();
5179 return ;
5181 if ( nTab1 != nTab2 )
5183 PushIllegalParameter();
5184 return;
5186 if (nCol1 > nCol2)
5188 PushIllegalParameter();
5189 return;
5191 if (nGlobalError == 0)
5193 ScQueryParam rParam;
5194 rParam.nRow1 = nRow1;
5195 rParam.nRow2 = nRow2;
5197 ScQueryEntry& rEntry = rParam.GetEntry(0);
5198 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5199 rEntry.bDoQuery = true;
5200 if (!bIsString)
5202 rItem.meType = ScQueryEntry::ByValue;
5203 rItem.mfVal = fVal;
5204 rEntry.eOp = SC_EQUAL;
5206 else
5208 rParam.FillInExcelSyntax(pDok->GetSharedStringPool(), aString.getString(), 0);
5209 sal_uInt32 nIndex = 0;
5210 bool bNumber = pFormatter->IsNumberFormat(
5211 rItem.maString.getString(), nIndex, rItem.mfVal);
5212 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
5213 if (rItem.meType == ScQueryEntry::ByString)
5214 rParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
5216 rParam.nCol1 = nCol1;
5217 rParam.nCol2 = nCol2;
5218 rEntry.nField = nCol1;
5219 if (pQueryMatrix)
5221 // Never case-sensitive.
5222 sc::CompareOptions aOptions( pDok, rEntry, rParam.bRegExp);
5223 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5224 if (nGlobalError || !pResultMatrix)
5226 PushIllegalParameter();
5227 return;
5230 SCSIZE nSize = pResultMatrix->GetElementCount();
5231 for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
5233 if (pResultMatrix->IsValue( nIndex) &&
5234 pResultMatrix->GetDouble( nIndex))
5235 ++fCount;
5238 else
5240 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false);
5241 // Keep Entry.nField in iterator on column change
5242 aCellIter.SetAdvanceQueryParamEntryField( true );
5243 if ( aCellIter.GetFirst() )
5247 fCount++;
5248 } while ( aCellIter.GetNext() );
5252 else
5254 PushIllegalParameter();
5255 return;
5258 PushDouble(fCount);
5262 double ScInterpreter::IterateParametersIfs( ScIterFuncIfs eFunc )
5264 sal_uInt8 nParamCount = GetByte();
5265 sal_uInt8 nQueryCount = nParamCount / 2;
5267 bool bCheck;
5268 if ( eFunc == ifCOUNTIFS )
5269 bCheck = (nParamCount >= 2) && (nParamCount % 2 == 0);
5270 else
5271 bCheck = (nParamCount >= 3) && (nParamCount % 2 == 1);
5273 if ( !bCheck )
5275 SetError( errParameterExpected);
5277 else
5279 std::vector<sal_uInt8> aResArray;
5280 size_t nRowSize = 0;
5281 size_t nColSize = 0;
5282 double fVal = 0.0;
5283 double fSum = 0.0;
5284 double fMem = 0.0;
5285 double fRes = 0.0;
5286 double fCount = 0.0;
5287 short nParam = 1;
5288 size_t nRefInList = 0;
5289 SCCOL nDimensionCols = 0;
5290 SCROW nDimensionRows = 0;
5292 while (nParamCount > 1 && !nGlobalError)
5294 // take criteria
5295 svl::SharedString aString;
5296 fVal = 0.0;
5297 bool bIsString = true;
5298 switch ( GetStackType() )
5300 case svDoubleRef :
5301 case svSingleRef :
5303 ScAddress aAdr;
5304 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5305 return 0;
5307 ScRefCellValue aCell;
5308 aCell.assign(*pDok, aAdr);
5309 switch (aCell.meType)
5311 case CELLTYPE_VALUE :
5312 fVal = GetCellValue(aAdr, aCell);
5313 bIsString = false;
5314 break;
5315 case CELLTYPE_FORMULA :
5316 if (aCell.mpFormula->IsValue())
5318 fVal = GetCellValue(aAdr, aCell);
5319 bIsString = false;
5321 else
5322 GetCellString(aString, aCell);
5323 break;
5324 case CELLTYPE_STRING :
5325 case CELLTYPE_EDIT :
5326 GetCellString(aString, aCell);
5327 break;
5328 default:
5329 fVal = 0.0;
5330 bIsString = false;
5333 break;
5334 case svString:
5335 aString = GetString();
5336 break;
5337 case svMatrix :
5338 case svExternalDoubleRef:
5340 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString);
5341 bIsString = ScMatrix::IsNonValueType( nType);
5343 break;
5344 case svExternalSingleRef:
5346 ScExternalRefCache::TokenRef pToken;
5347 PopExternalSingleRef(pToken);
5348 if (pToken)
5350 if (pToken->GetType() == svDouble)
5352 fVal = pToken->GetDouble();
5353 bIsString = false;
5355 else
5356 aString = pToken->GetString();
5359 break;
5360 default:
5362 fVal = GetDouble();
5363 bIsString = false;
5367 if (nGlobalError)
5368 return 0; // and bail out, no need to evaluate other arguments
5370 // take range
5371 nParam = 1;
5372 nRefInList = 0;
5373 SCCOL nCol1 = 0;
5374 SCROW nRow1 = 0;
5375 SCTAB nTab1 = 0;
5376 SCCOL nCol2 = 0;
5377 SCROW nRow2 = 0;
5378 SCTAB nTab2 = 0;
5379 ScMatrixRef pQueryMatrix;
5380 switch ( GetStackType() )
5382 case svRefList :
5384 ScRange aRange;
5385 PopDoubleRef( aRange, nParam, nRefInList);
5386 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5388 break;
5389 case svDoubleRef :
5390 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
5391 break;
5392 case svSingleRef :
5393 PopSingleRef( nCol1, nRow1, nTab1 );
5394 nCol2 = nCol1;
5395 nRow2 = nRow1;
5396 nTab2 = nTab1;
5397 break;
5398 case svMatrix:
5399 case svExternalSingleRef:
5400 case svExternalDoubleRef:
5402 pQueryMatrix = PopMatrix();
5403 if (!pQueryMatrix)
5405 SetError( errIllegalParameter);
5406 return 0;
5408 nCol1 = 0;
5409 nRow1 = 0;
5410 nTab1 = 0;
5411 SCSIZE nC, nR;
5412 pQueryMatrix->GetDimensions( nC, nR);
5413 nCol2 = static_cast<SCCOL>(nC - 1);
5414 nRow2 = static_cast<SCROW>(nR - 1);
5415 nTab2 = 0;
5417 break;
5418 default:
5419 SetError( errIllegalParameter);
5420 return 0;
5422 if ( nTab1 != nTab2 )
5424 SetError( errIllegalArgument);
5425 return 0;
5428 // All reference ranges must be of same dimension and size.
5429 if (!nDimensionCols)
5430 nDimensionCols = nCol2 - nCol1 + 1;
5431 if (!nDimensionRows)
5432 nDimensionRows = nRow2 - nRow1 + 1;
5433 if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1)))
5435 SetError ( errIllegalArgument);
5436 return 0;
5439 // recalculate matrix values
5440 if (nGlobalError)
5441 return 0;
5443 // initialize temporary result matrix
5444 if (aResArray.empty())
5446 nColSize = nCol2 - nCol1 + 1;
5447 nRowSize = nRow2 - nRow1 + 1;
5448 aResArray.resize(nColSize*nRowSize, 0);
5451 ScQueryParam rParam;
5452 rParam.nRow1 = nRow1;
5453 rParam.nRow2 = nRow2;
5455 ScQueryEntry& rEntry = rParam.GetEntry(0);
5456 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5457 rEntry.bDoQuery = true;
5458 if (!bIsString)
5460 rItem.meType = ScQueryEntry::ByValue;
5461 rItem.mfVal = fVal;
5462 rEntry.eOp = SC_EQUAL;
5464 else
5466 rParam.FillInExcelSyntax(pDok->GetSharedStringPool(), aString.getString(), 0);
5467 sal_uInt32 nIndex = 0;
5468 bool bNumber = pFormatter->IsNumberFormat(
5469 rItem.maString.getString(), nIndex, rItem.mfVal);
5470 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
5471 if (rItem.meType == ScQueryEntry::ByString)
5472 rParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
5474 ScAddress aAdr;
5475 aAdr.SetTab( nTab1 );
5476 rParam.nCol1 = nCol1;
5477 rParam.nCol2 = nCol2;
5478 rEntry.nField = nCol1;
5479 SCsCOL nColDiff = -nCol1;
5480 SCsROW nRowDiff = -nRow1;
5481 if (pQueryMatrix)
5483 // Never case-sensitive.
5484 sc::CompareOptions aOptions( pDok, rEntry, rParam.bRegExp);
5485 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
5486 if (nGlobalError || !pResultMatrix)
5488 SetError( errIllegalParameter);
5489 return 0;
5492 // result matrix is filled with boolean values.
5493 std::vector<double> aResValues;
5494 pResultMatrix->GetDoubleArray(aResValues, true);
5495 if (aResArray.size() != aResValues.size())
5497 SetError( errIllegalParameter);
5498 return 0;
5501 std::vector<sal_uInt8>::iterator itRes = aResArray.begin(), itResEnd = aResArray.end();
5502 std::vector<double>::const_iterator itThisRes = aResValues.begin();
5503 for (; itRes != itResEnd; ++itRes, ++itThisRes)
5504 *itRes += *itThisRes;
5506 else
5508 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, false);
5509 // Increment Entry.nField in iterator when switching to next column.
5510 aCellIter.SetAdvanceQueryParamEntryField( true );
5511 if ( aCellIter.GetFirst() )
5515 size_t nC = aCellIter.GetCol() + nColDiff;
5516 size_t nR = aCellIter.GetRow() + nRowDiff;
5517 ++aResArray[nC*nRowSize+nR];
5518 } while ( aCellIter.GetNext() );
5521 nParamCount -= 2;
5524 if (nGlobalError)
5525 return 0; // bail out
5527 // main range - only for AVERAGEIFS and SUMIFS
5528 if (nParamCount == 1)
5530 nParam = 1;
5531 nRefInList = 0;
5532 bool bNull = true;
5533 SCCOL nMainCol1 = 0;
5534 SCROW nMainRow1 = 0;
5535 SCTAB nMainTab1 = 0;
5536 SCCOL nMainCol2 = 0;
5537 SCROW nMainRow2 = 0;
5538 SCTAB nMainTab2 = 0;
5539 ScMatrixRef pMainMatrix;
5540 switch ( GetStackType() )
5542 case svRefList :
5544 ScRange aRange;
5545 PopDoubleRef( aRange, nParam, nRefInList);
5546 aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2);
5548 break;
5549 case svDoubleRef :
5550 PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 );
5551 break;
5552 case svSingleRef :
5553 PopSingleRef( nMainCol1, nMainRow1, nMainTab1 );
5554 nMainCol2 = nMainCol1;
5555 nMainRow2 = nMainRow1;
5556 nMainTab2 = nMainTab1;
5557 break;
5558 case svMatrix:
5559 case svExternalSingleRef:
5560 case svExternalDoubleRef:
5562 pMainMatrix = PopMatrix();
5563 if (!pMainMatrix)
5565 SetError( errIllegalParameter);
5567 nMainCol1 = 0;
5568 nMainRow1 = 0;
5569 nMainTab1 = 0;
5570 SCSIZE nC, nR;
5571 pMainMatrix->GetDimensions( nC, nR);
5572 nMainCol2 = static_cast<SCCOL>(nC - 1);
5573 nMainRow2 = static_cast<SCROW>(nR - 1);
5574 nMainTab2 = 0;
5576 break;
5577 default:
5578 SetError( errIllegalParameter);
5579 return 0;
5581 if ( nMainTab1 != nMainTab2 )
5583 SetError( errIllegalArgument);
5584 return 0;
5587 // All reference ranges must be of same dimension and size.
5588 if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1)))
5590 SetError ( errIllegalArgument);
5591 return 0;
5594 if (nGlobalError)
5595 return 0; // bail out
5597 // end-result calculation
5598 ScAddress aAdr;
5599 aAdr.SetTab( nMainTab1 );
5600 if (pMainMatrix)
5602 std::vector<double> aMainValues;
5603 pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's.
5604 if (aResArray.size() != aMainValues.size())
5606 SetError( errIllegalArgument);
5607 return 0;
5610 std::vector<sal_uInt8>::const_iterator itRes = aResArray.begin(), itResEnd = aResArray.end();
5611 std::vector<double>::const_iterator itMain = aMainValues.begin();
5612 for (; itRes != itResEnd; ++itRes, ++itMain)
5614 if (*itRes != nQueryCount)
5615 continue;
5617 fVal = *itMain;
5618 if (rtl::math::isNan(fVal))
5619 continue;
5621 ++fCount;
5622 if (bNull && fVal != 0.0)
5624 bNull = false;
5625 fMem = fVal;
5627 else
5628 fSum += fVal;
5631 else
5633 std::vector<sal_uInt8>::const_iterator itRes = aResArray.begin(), itResEnd = aResArray.end();
5634 for (size_t nCol = 0; nCol < nColSize; ++nCol)
5636 for (size_t nRow = 0; nRow < nRowSize; ++nRow, ++itRes)
5638 if (*itRes == nQueryCount)
5640 aAdr.SetCol( static_cast<SCCOL>(nCol) + nMainCol1);
5641 aAdr.SetRow( static_cast<SCROW>(nRow) + nMainRow1);
5642 ScRefCellValue aCell;
5643 aCell.assign(*pDok, aAdr);
5644 if (aCell.hasNumeric())
5646 fVal = GetCellValue(aAdr, aCell);
5647 ++fCount;
5648 if ( bNull && fVal != 0.0 )
5650 bNull = false;
5651 fMem = fVal;
5653 else
5654 fSum += fVal;
5661 else
5663 std::vector<sal_uInt8>::const_iterator itRes = aResArray.begin(), itResEnd = aResArray.end();
5664 for (; itRes != itResEnd; ++itRes)
5665 if (*itRes == nQueryCount)
5666 ++fCount;
5669 switch( eFunc )
5671 case ifSUMIFS: fRes = ::rtl::math::approxAdd( fSum, fMem ); break;
5672 case ifAVERAGEIFS: fRes = div( ::rtl::math::approxAdd( fSum, fMem ), fCount); break;
5673 case ifCOUNTIFS: fRes = fCount; break;
5674 default: ; // nothing
5676 return fRes;
5678 return 0;
5681 void ScInterpreter::ScSumIfs()
5683 PushDouble( IterateParametersIfs( ifSUMIFS));
5686 void ScInterpreter::ScAverageIfs()
5688 PushDouble( IterateParametersIfs( ifAVERAGEIFS));
5691 void ScInterpreter::ScCountIfs()
5693 PushDouble( IterateParametersIfs( ifCOUNTIFS));
5696 void ScInterpreter::ScLookup()
5698 sal_uInt8 nParamCount = GetByte();
5699 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
5700 return ;
5702 ScMatrixRef pDataMat = NULL, pResMat = NULL;
5703 SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0;
5704 SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0;
5705 SCTAB nTab1 = 0, nResTab = 0;
5706 SCSIZE nLenMajor = 0; // length of major direction
5707 bool bVertical = true; // whether to lookup vertically or horizontally
5709 // The third parameter, result array, for double, string and single reference.
5710 double fResVal = 0.0;
5711 svl::SharedString aResStr;
5712 ScAddress aResAdr;
5713 StackVar eResArrayType = svUnknown;
5715 if (nParamCount == 3)
5717 eResArrayType = GetStackType();
5718 switch (eResArrayType)
5720 case svDoubleRef:
5722 SCTAB nTabJunk;
5723 PopDoubleRef(nResCol1, nResRow1, nResTab,
5724 nResCol2, nResRow2, nTabJunk);
5725 if (nResTab != nTabJunk ||
5726 ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0))
5728 // The result array must be a vector.
5729 PushIllegalParameter();
5730 return;
5733 break;
5734 case svMatrix:
5735 case svExternalSingleRef:
5736 case svExternalDoubleRef:
5738 pResMat = GetMatrix();
5739 if (!pResMat)
5741 PushIllegalParameter();
5742 return;
5744 SCSIZE nC, nR;
5745 pResMat->GetDimensions(nC, nR);
5746 if (nC != 1 && nR != 1)
5748 // Result matrix must be a vector.
5749 PushIllegalParameter();
5750 return;
5753 break;
5754 case svDouble:
5755 fResVal = GetDouble();
5756 break;
5757 case svString:
5758 aResStr = GetString();
5759 break;
5760 case svSingleRef:
5761 PopSingleRef( aResAdr );
5762 break;
5763 default:
5764 PushIllegalParameter();
5765 return;
5769 // For double, string and single reference.
5770 double fDataVal = 0.0;
5771 svl::SharedString aDataStr;
5772 ScAddress aDataAdr;
5773 bool bValueData = false;
5775 // Get the data-result range and also determine whether this is vertical
5776 // lookup or horizontal lookup.
5778 StackVar eDataArrayType = GetStackType();
5779 switch (eDataArrayType)
5781 case svDoubleRef:
5783 SCTAB nTabJunk;
5784 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk);
5785 if (nTab1 != nTabJunk)
5787 PushIllegalParameter();
5788 return;
5790 bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1);
5791 nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1;
5793 break;
5794 case svMatrix:
5795 case svExternalSingleRef:
5796 case svExternalDoubleRef:
5798 pDataMat = GetMatrix();
5799 if (!pDataMat)
5801 PushIllegalParameter();
5802 return;
5805 SCSIZE nC, nR;
5806 pDataMat->GetDimensions(nC, nR);
5807 bVertical = (nR >= nC);
5808 nLenMajor = bVertical ? nR : nC;
5810 break;
5811 case svDouble:
5813 fDataVal = GetDouble();
5814 bValueData = true;
5816 break;
5817 case svString:
5819 aDataStr = GetString();
5821 break;
5822 case svSingleRef:
5824 PopSingleRef( aDataAdr );
5825 ScRefCellValue aCell;
5826 aCell.assign(*pDok, aDataAdr);
5827 if (aCell.hasEmptyValue())
5829 // Empty cells aren't found anywhere, bail out early.
5830 SetError( NOTAVAILABLE);
5832 else if (aCell.hasNumeric())
5834 fDataVal = GetCellValue(aDataAdr, aCell);
5835 bValueData = true;
5837 else
5838 GetCellString(aDataStr, aCell);
5840 break;
5841 default:
5842 SetError( errIllegalParameter);
5846 if (nGlobalError)
5848 PushError( nGlobalError);
5849 return;
5852 // Get the lookup value.
5854 ScQueryParam aParam;
5855 ScQueryEntry& rEntry = aParam.GetEntry(0);
5856 if ( !FillEntry(rEntry) )
5857 return;
5859 if ( eDataArrayType == svDouble || eDataArrayType == svString ||
5860 eDataArrayType == svSingleRef )
5862 // Delta position for a single value is always 0.
5864 // Found if data <= query, but not if query is string and found data is
5865 // numeric or vice versa. This is how Excel does it but doesn't
5866 // document it.
5868 bool bFound = false;
5869 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
5871 if ( bValueData )
5873 if (rItem.meType == ScQueryEntry::ByString)
5874 bFound = false;
5875 else
5876 bFound = (fDataVal <= rItem.mfVal);
5878 else
5880 if (rItem.meType != ScQueryEntry::ByString)
5881 bFound = false;
5882 else
5883 bFound = (ScGlobal::GetCollator()->compareString(aDataStr.getString(), rItem.maString.getString()) <= 0);
5886 if (!bFound)
5888 PushNA();
5889 return;
5892 if (pResMat)
5894 if (pResMat->IsValue( 0, 0 ))
5895 PushDouble(pResMat->GetDouble( 0, 0 ));
5896 else
5897 PushString(pResMat->GetString(0, 0));
5899 else if (nParamCount == 3)
5901 switch (eResArrayType)
5903 case svDouble:
5904 PushDouble( fResVal );
5905 break;
5906 case svString:
5907 PushString( aResStr );
5908 break;
5909 case svDoubleRef:
5910 aResAdr.Set( nResCol1, nResRow1, nResTab);
5911 // fallthru
5912 case svSingleRef:
5913 PushCellResultToken( true, aResAdr, NULL, NULL);
5914 break;
5915 default:
5916 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eResArrayType, single value data");
5919 else
5921 switch (eDataArrayType)
5923 case svDouble:
5924 PushDouble( fDataVal );
5925 break;
5926 case svString:
5927 PushString( aDataStr );
5928 break;
5929 case svSingleRef:
5930 PushCellResultToken( true, aDataAdr, NULL, NULL);
5931 break;
5932 default:
5933 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eDataArrayType, single value data");
5936 return;
5939 // Now, perform the search to compute the delta position (nDelta).
5941 if (pDataMat)
5943 // Data array is given as a matrix.
5944 rEntry.bDoQuery = true;
5945 rEntry.eOp = SC_LESS_EQUAL;
5946 bool bFound = false;
5948 SCSIZE nC, nR;
5949 pDataMat->GetDimensions(nC, nR);
5951 // In case of non-vector matrix, only search the first row or column.
5952 ScMatrixRef pDataMat2;
5953 if (bVertical)
5955 ScMatrixRef pTempMat(new ScMatrix(1, nR, 0.0));
5956 for (SCSIZE i = 0; i < nR; ++i)
5957 if (pDataMat->IsValue(0, i))
5958 pTempMat->PutDouble(pDataMat->GetDouble(0, i), 0, i);
5959 else
5960 pTempMat->PutString(pDataMat->GetString(0, i), 0, i);
5961 pDataMat2 = pTempMat;
5963 else
5965 ScMatrixRef pTempMat(new ScMatrix(nC, 1, 0.0));
5966 for (SCSIZE i = 0; i < nC; ++i)
5967 if (pDataMat->IsValue(i, 0))
5968 pTempMat->PutDouble(pDataMat->GetDouble(i, 0), i, 0);
5969 else
5970 pTempMat->PutString(pDataMat->GetString(i, 0), i, 0);
5971 pDataMat2 = pTempMat;
5974 VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical);
5976 // binary search for non-equality mode (the source data is
5977 // assumed to be sorted in ascending order).
5979 SCCOLROW nDelta = -1;
5981 SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0;
5982 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
5984 SCSIZE nMid = nFirst + nLen/2;
5985 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry);
5986 if (nCmp == 0)
5988 // exact match. find the last item with the same value.
5989 lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor, false);
5990 nDelta = nMid;
5991 bFound = true;
5992 break;
5995 if (nLen == 1) // first and last items are next to each other.
5997 nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1;
5998 // If already the 1st item is greater there's nothing found.
5999 bFound = (nDelta >= 0);
6000 break;
6003 if (nCmp < 0)
6004 nFirst = nMid;
6005 else
6006 nLast = nMid;
6009 if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item
6011 sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry);
6012 if (nCmp <= 0)
6014 // either the last item is an exact match or the real
6015 // hit is beyond the last item.
6016 nDelta += 1;
6017 bFound = true;
6020 else if (nDelta > 0) // valid hit must be 2nd item or higher
6022 // non-exact match
6023 bFound = true;
6026 // With 0-9 < A-Z, if query is numeric and data found is string, or
6027 // vice versa, the (yet another undocumented) Excel behavior is to
6028 // return #N/A instead.
6030 if (bFound)
6032 VectorMatrixAccessor aMatAcc(*pDataMat, bVertical);
6033 SCCOLROW i = nDelta;
6034 SCSIZE n = aMatAcc.GetElementCount();
6035 if (static_cast<SCSIZE>(i) >= n)
6036 i = static_cast<SCCOLROW>(n);
6037 bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString;
6038 if (bByString == aMatAcc.IsValue(i))
6039 bFound = false;
6042 if (!bFound)
6044 PushNA();
6045 return;
6048 // Now that we've found the delta, push the result back to the cell.
6050 if (pResMat)
6052 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
6053 // result array is matrix.
6054 if (static_cast<SCSIZE>(nDelta) >= aResMatAcc.GetElementCount())
6056 PushNA();
6057 return;
6059 if (aResMatAcc.IsValue(nDelta))
6060 PushDouble(aResMatAcc.GetDouble(nDelta));
6061 else
6062 PushString(aResMatAcc.GetString(nDelta));
6064 else if (nParamCount == 3)
6066 // result array is cell range.
6067 ScAddress aAdr;
6068 aAdr.SetTab(nResTab);
6069 bool bResVertical = (nResRow2 - nResRow1) > 0;
6070 if (bResVertical)
6072 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
6073 if (nTempRow > MAXROW)
6075 PushDouble(0);
6076 return;
6078 aAdr.SetCol(nResCol1);
6079 aAdr.SetRow(nTempRow);
6081 else
6083 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
6084 if (nTempCol > MAXCOL)
6086 PushDouble(0);
6087 return;
6089 aAdr.SetCol(nTempCol);
6090 aAdr.SetRow(nResRow1);
6092 PushCellResultToken(true, aAdr, NULL, NULL);
6094 else
6096 // no result array. Use the data array to get the final value from.
6097 if (bVertical)
6099 if (pDataMat->IsValue(nC-1, nDelta))
6100 PushDouble(pDataMat->GetDouble(nC-1, nDelta));
6101 else
6102 PushString(pDataMat->GetString(nC-1, nDelta));
6104 else
6106 if (pDataMat->IsValue(nDelta, nR-1))
6107 PushDouble(pDataMat->GetDouble(nDelta, nR-1));
6108 else
6109 PushString(pDataMat->GetString(nDelta, nR-1));
6113 return;
6116 // Perform cell range search.
6118 aParam.nCol1 = nCol1;
6119 aParam.nRow1 = nRow1;
6120 aParam.nCol2 = bVertical ? nCol1 : nCol2;
6121 aParam.nRow2 = bVertical ? nRow2 : nRow1;
6122 aParam.bByRow = bVertical;
6124 rEntry.bDoQuery = true;
6125 rEntry.eOp = SC_LESS_EQUAL;
6126 rEntry.nField = nCol1;
6127 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6128 if (rItem.meType == ScQueryEntry::ByString)
6129 aParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
6131 ScQueryCellIterator aCellIter(pDok, nTab1, aParam, false);
6132 SCCOL nC;
6133 SCROW nR;
6134 // Advance Entry.nField in iterator upon switching columns if
6135 // lookup in row.
6136 aCellIter.SetAdvanceQueryParamEntryField(!bVertical);
6137 if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) )
6139 PushNA();
6140 return;
6143 SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1);
6145 if (pResMat)
6147 VectorMatrixAccessor aResMatAcc(*pResMat, bVertical);
6148 // Use the matrix result array.
6149 if (aResMatAcc.IsValue(nDelta))
6150 PushDouble(aResMatAcc.GetDouble(nDelta));
6151 else
6152 PushString(aResMatAcc.GetString(nDelta));
6154 else if (nParamCount == 3)
6156 switch (eResArrayType)
6158 case svDoubleRef:
6160 // Use the result array vector. Note that the result array is assumed
6161 // to be a vector (i.e. 1-dimensinoal array).
6163 ScAddress aAdr;
6164 aAdr.SetTab(nResTab);
6165 bool bResVertical = (nResRow2 - nResRow1) > 0;
6166 if (bResVertical)
6168 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
6169 if (nTempRow > MAXROW)
6171 PushDouble(0);
6172 return;
6174 aAdr.SetCol(nResCol1);
6175 aAdr.SetRow(nTempRow);
6177 else
6179 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
6180 if (nTempCol > MAXCOL)
6182 PushDouble(0);
6183 return;
6185 aAdr.SetCol(nTempCol);
6186 aAdr.SetRow(nResRow1);
6188 PushCellResultToken( true, aAdr, NULL, NULL);
6190 break;
6191 case svDouble:
6192 case svString:
6193 case svSingleRef:
6195 if (nDelta != 0)
6196 PushNA();
6197 else
6199 switch (eResArrayType)
6201 case svDouble:
6202 PushDouble( fResVal );
6203 break;
6204 case svString:
6205 PushString( aResStr );
6206 break;
6207 case svSingleRef:
6208 PushCellResultToken( true, aResAdr, NULL, NULL);
6209 break;
6210 default:
6211 ; // nothing
6215 break;
6216 default:
6217 OSL_FAIL( "ScInterpreter::ScLookup: unhandled eResArrayType, range search");
6220 else
6222 // Regardless of whether or not the result array exists, the last
6223 // array is always used as the "result" array.
6225 ScAddress aAdr;
6226 aAdr.SetTab(nTab1);
6227 if (bVertical)
6229 SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta);
6230 if (nTempRow > MAXROW)
6232 PushDouble(0);
6233 return;
6235 aAdr.SetCol(nCol2);
6236 aAdr.SetRow(nTempRow);
6238 else
6240 SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta);
6241 if (nTempCol > MAXCOL)
6243 PushDouble(0);
6244 return;
6246 aAdr.SetCol(nTempCol);
6247 aAdr.SetRow(nRow2);
6249 PushCellResultToken(true, aAdr, NULL, NULL);
6254 void ScInterpreter::ScHLookup()
6256 CalculateLookup(true);
6259 void ScInterpreter::CalculateLookup(bool bHLookup)
6261 sal_uInt8 nParamCount = GetByte();
6262 if (!MustHaveParamCount(nParamCount, 3, 4))
6263 return;
6265 // Optional 4th argument to declare whether or not the range is sorted.
6266 bool bSorted = true;
6267 if (nParamCount == 4)
6268 bSorted = GetBool();
6270 // Index of column to search.
6271 double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0;
6273 ScMatrixRef pMat = NULL;
6274 SCSIZE nC = 0, nR = 0;
6275 SCCOL nCol1 = 0;
6276 SCROW nRow1 = 0;
6277 SCTAB nTab1 = 0;
6278 SCCOL nCol2 = 0;
6279 SCROW nRow2 = 0;
6280 SCTAB nTab2;
6281 StackVar eType = GetStackType();
6282 if (eType == svDoubleRef)
6284 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6285 if (nTab1 != nTab2)
6287 PushIllegalParameter();
6288 return;
6291 else if (eType == svSingleRef)
6293 PopSingleRef(nCol1, nRow1, nTab1);
6294 nCol2 = nCol1;
6295 nRow2 = nRow1;
6297 else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef)
6299 pMat = GetMatrix();
6301 if (pMat)
6302 pMat->GetDimensions(nC, nR);
6303 else
6305 PushIllegalParameter();
6306 return;
6309 else
6311 PushIllegalParameter();
6312 return;
6315 if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) )
6317 PushIllegalArgument();
6318 return;
6321 SCROW nZIndex = static_cast<SCROW>(fIndex);
6322 SCCOL nSpIndex = static_cast<SCCOL>(fIndex);
6324 if (!pMat)
6326 nZIndex += nRow1; // Wertzeile
6327 nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column
6330 if (nGlobalError)
6332 PushIllegalParameter();
6333 return;
6336 ScQueryParam aParam;
6337 aParam.nCol1 = nCol1;
6338 aParam.nRow1 = nRow1;
6339 if ( bHLookup )
6341 aParam.nCol2 = nCol2;
6342 aParam.nRow2 = nRow1; // nur in der ersten Zeile suchen
6343 aParam.bByRow = false;
6345 else
6347 aParam.nCol2 = nCol1; // nur in der ersten Spalte suchen
6348 aParam.nRow2 = nRow2;
6349 aParam.nTab = nTab1;
6352 ScQueryEntry& rEntry = aParam.GetEntry(0);
6353 rEntry.bDoQuery = true;
6354 if ( bSorted )
6355 rEntry.eOp = SC_LESS_EQUAL;
6356 if ( !FillEntry(rEntry) )
6357 return;
6359 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6360 if (rItem.meType == ScQueryEntry::ByString)
6361 aParam.bRegExp = MayBeRegExp(rItem.maString.getString(), pDok);
6362 if (pMat)
6364 SCSIZE nMatCount = bHLookup ? nC : nR;
6365 SCSIZE nDelta = SCSIZE_MAX;
6366 if (rItem.meType == ScQueryEntry::ByString)
6368 //!!!!!!!
6369 //! TODO: enable regex on matrix strings
6370 //!!!!!!!
6371 svl::SharedString aParamStr = rItem.maString;
6372 if ( bSorted )
6374 static CollatorWrapper* pCollator = ScGlobal::GetCollator();
6375 for (SCSIZE i = 0; i < nMatCount; i++)
6377 if (bHLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i))
6379 sal_Int32 nRes =
6380 pCollator->compareString(
6381 bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString());
6382 if (nRes <= 0)
6383 nDelta = i;
6384 else if (i>0) // #i2168# ignore first mismatch
6385 i = nMatCount+1;
6387 else
6388 nDelta = i;
6391 else
6393 if (bHLookup)
6395 for (SCSIZE i = 0; i < nMatCount; i++)
6397 if (pMat->IsString(i, 0))
6399 if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase())
6401 nDelta = i;
6402 i = nMatCount + 1;
6407 else
6409 nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0);
6413 else
6415 if ( bSorted )
6417 // #i2168# ignore strings
6418 for (SCSIZE i = 0; i < nMatCount; i++)
6420 if (!(bHLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i)))
6422 if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal)
6423 nDelta = i;
6424 else
6425 i = nMatCount+1;
6429 else
6431 if (bHLookup)
6433 for (SCSIZE i = 0; i < nMatCount; i++)
6435 if (! pMat->IsString(i, 0) )
6437 if ( pMat->GetDouble(i,0) == rItem.mfVal)
6439 nDelta = i;
6440 i = nMatCount + 1;
6445 else
6447 nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0);
6451 if ( nDelta != SCSIZE_MAX )
6453 SCSIZE nX = static_cast<SCSIZE>(nSpIndex);
6454 SCSIZE nY = nDelta;
6455 if ( bHLookup )
6457 nX = nDelta;
6458 nY = static_cast<SCSIZE>(nZIndex);
6460 if ( pMat->IsString( nX, nY) )
6461 PushString(pMat->GetString( nX,nY).getString());
6462 else
6463 PushDouble(pMat->GetDouble( nX,nY));
6465 else
6466 PushNA();
6468 else
6470 rEntry.nField = nCol1;
6471 bool bFound = false;
6472 SCCOL nCol = 0;
6473 SCROW nRow = 0;
6474 if ( bSorted )
6475 rEntry.eOp = SC_LESS_EQUAL;
6476 if ( bHLookup )
6478 ScQueryCellIterator aCellIter(pDok, nTab1, aParam, false);
6479 // advance Entry.nField in Iterator upon switching columns
6480 aCellIter.SetAdvanceQueryParamEntryField( true );
6481 if ( bSorted )
6483 SCROW nRow1_temp;
6484 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp );
6486 else if ( aCellIter.GetFirst() )
6488 bFound = true;
6489 nCol = aCellIter.GetCol();
6491 nRow = nZIndex;
6493 else
6495 ScAddress aResultPos( nCol1, nRow1, nTab1);
6496 bFound = LookupQueryWithCache( aResultPos, aParam);
6497 nRow = aResultPos.Row();
6498 nCol = nSpIndex;
6501 if ( bFound )
6503 ScAddress aAdr( nCol, nRow, nTab1 );
6504 PushCellResultToken( true, aAdr, NULL, NULL);
6506 else
6507 PushNA();
6511 bool ScInterpreter::FillEntry(ScQueryEntry& rEntry)
6513 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6514 switch ( GetStackType() )
6516 case svDouble:
6518 rItem.meType = ScQueryEntry::ByValue;
6519 rItem.mfVal = GetDouble();
6521 break;
6522 case svString:
6524 rItem.meType = ScQueryEntry::ByString;
6525 rItem.maString = GetString();
6527 break;
6528 case svDoubleRef :
6529 case svSingleRef :
6531 ScAddress aAdr;
6532 if ( !PopDoubleRefOrSingleRef( aAdr ) )
6534 PushInt(0);
6535 return false;
6537 ScRefCellValue aCell;
6538 aCell.assign(*pDok, aAdr);
6539 if (aCell.hasNumeric())
6541 rItem.meType = ScQueryEntry::ByValue;
6542 rItem.mfVal = GetCellValue(aAdr, aCell);
6544 else
6546 GetCellString(rItem.maString, aCell);
6547 rItem.meType = ScQueryEntry::ByString;
6550 break;
6551 case svMatrix :
6553 svl::SharedString aStr;
6554 const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr);
6555 rItem.maString = aStr;
6556 rItem.meType = ScMatrix::IsNonValueType(nType) ?
6557 ScQueryEntry::ByString : ScQueryEntry::ByValue;
6559 break;
6560 default:
6562 PushIllegalParameter();
6563 return false;
6565 } // switch ( GetStackType() )
6566 return true;
6568 void ScInterpreter::ScVLookup()
6570 CalculateLookup(false);
6573 void ScInterpreter::ScSubTotal()
6575 sal_uInt8 nParamCount = GetByte();
6576 if ( MustHaveParamCountMin( nParamCount, 2 ) )
6578 // We must fish the 1st parameter deep from the stack! And push it on top.
6579 const FormulaToken* p = pStack[ sp - nParamCount ];
6580 PushTempToken( *p );
6581 int nFunc = (int) ::rtl::math::approxFloor( GetDouble() );
6582 bool bIncludeHidden = true;
6583 if (nFunc > 100)
6585 // For opcodes 101 through 111, we need to skip hidden cells.
6586 // Other than that these opcodes are identical to 1 through 11.
6587 bIncludeHidden = false;
6588 nFunc -= 100;
6591 if (nFunc < 1 || nFunc > 11 || !bIncludeHidden)
6592 PushIllegalArgument(); // simulate return on stack, not SetError(...)
6593 else
6595 // TODO: Make use of bIncludeHidden flag. Then it's false, we do need to skip hidden cells.
6596 cPar = nParamCount - 1;
6597 glSubTotal = true;
6598 switch( nFunc )
6600 case SUBTOTAL_FUNC_AVE : ScAverage(); break;
6601 case SUBTOTAL_FUNC_CNT : ScCount(); break;
6602 case SUBTOTAL_FUNC_CNT2 : ScCount2(); break;
6603 case SUBTOTAL_FUNC_MAX : ScMax(); break;
6604 case SUBTOTAL_FUNC_MIN : ScMin(); break;
6605 case SUBTOTAL_FUNC_PROD : ScProduct(); break;
6606 case SUBTOTAL_FUNC_STD : ScStDev(); break;
6607 case SUBTOTAL_FUNC_STDP : ScStDevP(); break;
6608 case SUBTOTAL_FUNC_SUM : ScSum(); break;
6609 case SUBTOTAL_FUNC_VAR : ScVar(); break;
6610 case SUBTOTAL_FUNC_VARP : ScVarP(); break;
6611 default : PushIllegalArgument(); break;
6613 glSubTotal = false;
6615 // Get rid of the 1st (fished) parameter.
6616 double nVal = GetDouble();
6617 Pop();
6618 PushDouble( nVal );
6622 ScDBQueryParamBase* ScInterpreter::GetDBParams( bool& rMissingField )
6624 bool bAllowMissingField = false;
6625 if ( rMissingField )
6627 bAllowMissingField = true;
6628 rMissingField = false;
6630 if ( GetByte() == 3 )
6632 // First, get the query criteria range.
6633 SAL_WNODEPRECATED_DECLARATIONS_PUSH
6634 ::std::auto_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() );
6635 SAL_WNODEPRECATED_DECLARATIONS_POP
6636 if (!pQueryRef.get())
6637 return NULL;
6639 bool bByVal = true;
6640 double nVal = 0.0;
6641 svl::SharedString aStr;
6642 ScRange aMissingRange;
6643 bool bRangeFake = false;
6644 switch (GetStackType())
6646 case svDouble :
6647 nVal = ::rtl::math::approxFloor( GetDouble() );
6648 if ( bAllowMissingField && nVal == 0.0 )
6649 rMissingField = true; // fake missing parameter
6650 break;
6651 case svString :
6652 bByVal = false;
6653 aStr = GetString();
6654 break;
6655 case svSingleRef :
6657 ScAddress aAdr;
6658 PopSingleRef( aAdr );
6659 ScRefCellValue aCell;
6660 aCell.assign(*pDok, aAdr);
6661 if (aCell.hasNumeric())
6662 nVal = GetCellValue(aAdr, aCell);
6663 else
6665 bByVal = false;
6666 GetCellString(aStr, aCell);
6669 break;
6670 case svDoubleRef :
6671 if ( bAllowMissingField )
6672 { // fake missing parameter for old SO compatibility
6673 bRangeFake = true;
6674 PopDoubleRef( aMissingRange );
6676 else
6678 PopError();
6679 SetError( errIllegalParameter );
6681 break;
6682 case svMissing :
6683 PopError();
6684 if ( bAllowMissingField )
6685 rMissingField = true;
6686 else
6687 SetError( errIllegalParameter );
6688 break;
6689 default:
6690 PopError();
6691 SetError( errIllegalParameter );
6694 if (nGlobalError)
6695 return NULL;
6697 SAL_WNODEPRECATED_DECLARATIONS_PUSH
6698 auto_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() );
6699 SAL_WNODEPRECATED_DECLARATIONS_POP
6701 if (nGlobalError || !pDBRef.get())
6702 return NULL;
6704 if ( bRangeFake )
6706 // range parameter must match entire database range
6707 if (pDBRef->isRangeEqual(aMissingRange))
6708 rMissingField = true;
6709 else
6710 SetError( errIllegalParameter );
6713 if (nGlobalError)
6714 return NULL;
6716 SCCOL nField = pDBRef->getFirstFieldColumn();
6717 if (rMissingField)
6718 ; // special case
6719 else if (bByVal)
6720 nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal));
6721 else
6723 sal_uInt16 nErr = 0;
6724 nField = pDBRef->findFieldColumn(aStr.getString(), &nErr);
6725 SetError(nErr);
6728 if (!ValidCol(nField))
6729 return NULL;
6731 SAL_WNODEPRECATED_DECLARATIONS_PUSH
6732 auto_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) );
6733 SAL_WNODEPRECATED_DECLARATIONS_POP
6735 if (pParam.get())
6737 // An allowed missing field parameter sets the result field
6738 // to any of the query fields, just to be able to return
6739 // some cell from the iterator.
6740 if ( rMissingField )
6741 nField = static_cast<SCCOL>(pParam->GetEntry(0).nField);
6742 pParam->mnField = nField;
6744 SCSIZE nCount = pParam->GetEntryCount();
6745 for ( SCSIZE i=0; i < nCount; i++ )
6747 ScQueryEntry& rEntry = pParam->GetEntry(i);
6748 if (!rEntry.bDoQuery)
6749 break;
6751 ScQueryEntry::Item& rItem = rEntry.GetQueryItem();
6752 sal_uInt32 nIndex = 0;
6753 OUString aQueryStr = rItem.maString.getString();
6754 bool bNumber = pFormatter->IsNumberFormat(
6755 aQueryStr, nIndex, rItem.mfVal);
6756 rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString;
6758 if (!bNumber && !pParam->bRegExp)
6759 pParam->bRegExp = MayBeRegExp(aQueryStr, pDok);
6761 return pParam.release();
6764 return NULL;
6768 void ScInterpreter::DBIterator( ScIterFunc eFunc )
6770 double nErg = 0.0;
6771 double fMem = 0.0;
6772 bool bNull = true;
6773 sal_uLong nCount = 0;
6774 bool bMissingField = false;
6775 SAL_WNODEPRECATED_DECLARATIONS_PUSH
6776 auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
6777 SAL_WNODEPRECATED_DECLARATIONS_POP
6778 if (pQueryParam.get())
6780 if (!pQueryParam->IsValidFieldIndex())
6782 SetError(errNoValue);
6783 return;
6785 ScDBQueryDataIterator aValIter(pDok, pQueryParam.release());
6786 ScDBQueryDataIterator::Value aValue;
6787 if ( aValIter.GetFirst(aValue) && !aValue.mnError )
6789 switch( eFunc )
6791 case ifPRODUCT: nErg = 1; break;
6792 case ifMAX: nErg = -MAXDOUBLE; break;
6793 case ifMIN: nErg = MAXDOUBLE; break;
6794 default: ; // nothing
6798 nCount++;
6799 switch( eFunc )
6801 case ifAVERAGE:
6802 case ifSUM:
6803 if ( bNull && aValue.mfValue != 0.0 )
6805 bNull = false;
6806 fMem = aValue.mfValue;
6808 else
6809 nErg += aValue.mfValue;
6810 break;
6811 case ifSUMSQ: nErg += aValue.mfValue * aValue.mfValue; break;
6812 case ifPRODUCT: nErg *= aValue.mfValue; break;
6813 case ifMAX: if( aValue.mfValue > nErg ) nErg = aValue.mfValue; break;
6814 case ifMIN: if( aValue.mfValue < nErg ) nErg = aValue.mfValue; break;
6815 default: ; // nothing
6818 while ( aValIter.GetNext(aValue) && !aValue.mnError );
6820 SetError(aValue.mnError);
6822 else
6823 SetError( errIllegalParameter);
6824 switch( eFunc )
6826 case ifCOUNT: nErg = nCount; break;
6827 case ifSUM: nErg = ::rtl::math::approxAdd( nErg, fMem ); break;
6828 case ifAVERAGE: nErg = ::rtl::math::approxAdd( nErg, fMem ) / nCount; break;
6829 default: ; // nothing
6831 PushDouble( nErg );
6835 void ScInterpreter::ScDBSum()
6837 DBIterator( ifSUM );
6841 void ScInterpreter::ScDBCount()
6843 bool bMissingField = true;
6844 SAL_WNODEPRECATED_DECLARATIONS_PUSH
6845 auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
6846 SAL_WNODEPRECATED_DECLARATIONS_POP
6847 if (pQueryParam.get())
6849 sal_uLong nCount = 0;
6850 if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL )
6851 { // count all matching records
6852 // TODO: currently the QueryIterators only return cell pointers of
6853 // existing cells, so if a query matches an empty cell there's
6854 // nothing returned, and therefor not counted!
6855 // Since this has ever been the case and this code here only came
6856 // into existance to fix #i6899 and it never worked before we'll
6857 // have to live with it until we reimplement the iterators to also
6858 // return empty cells, which would mean to adapt all callers of
6859 // iterators.
6860 ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get());
6861 p->nCol2 = p->nCol1; // Don't forget to select only one column.
6862 SCTAB nTab = p->nTab;
6863 // ScQueryCellIterator doesn't make use of ScDBQueryParamBase::mnField,
6864 // so the source range has to be restricted, like before the introduction
6865 // of ScDBQueryParamBase.
6866 p->nCol1 = p->nCol2 = p->mnField;
6867 ScQueryCellIterator aCellIter( pDok, nTab, *p);
6868 if ( aCellIter.GetFirst() )
6872 nCount++;
6873 } while ( aCellIter.GetNext() );
6876 else
6877 { // count only matching records with a value in the "result" field
6878 if (!pQueryParam->IsValidFieldIndex())
6880 SetError(errNoValue);
6881 return;
6883 ScDBQueryDataIterator aValIter( pDok, pQueryParam.release());
6884 ScDBQueryDataIterator::Value aValue;
6885 if ( aValIter.GetFirst(aValue) && !aValue.mnError )
6889 nCount++;
6891 while ( aValIter.GetNext(aValue) && !aValue.mnError );
6893 SetError(aValue.mnError);
6895 PushDouble( nCount );
6897 else
6898 PushIllegalParameter();
6902 void ScInterpreter::ScDBCount2()
6904 bool bMissingField = true;
6905 SAL_WNODEPRECATED_DECLARATIONS_PUSH
6906 auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
6907 SAL_WNODEPRECATED_DECLARATIONS_POP
6908 if (pQueryParam.get())
6910 if (!pQueryParam->IsValidFieldIndex())
6912 SetError(errNoValue);
6913 return;
6915 sal_uLong nCount = 0;
6916 pQueryParam->mbSkipString = false;
6917 ScDBQueryDataIterator aValIter( pDok, pQueryParam.release());
6918 ScDBQueryDataIterator::Value aValue;
6919 if ( aValIter.GetFirst(aValue) && !aValue.mnError )
6923 nCount++;
6925 while ( aValIter.GetNext(aValue) && !aValue.mnError );
6927 SetError(aValue.mnError);
6928 PushDouble( nCount );
6930 else
6931 PushIllegalParameter();
6935 void ScInterpreter::ScDBAverage()
6937 DBIterator( ifAVERAGE );
6941 void ScInterpreter::ScDBMax()
6943 DBIterator( ifMAX );
6947 void ScInterpreter::ScDBMin()
6949 DBIterator( ifMIN );
6953 void ScInterpreter::ScDBProduct()
6955 DBIterator( ifPRODUCT );
6959 void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount )
6961 std::vector<double> values;
6962 double vSum = 0.0;
6963 double vMean = 0.0;
6965 rValCount = 0.0;
6966 double fSum = 0.0;
6967 bool bMissingField = false;
6968 SAL_WNODEPRECATED_DECLARATIONS_PUSH
6969 auto_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) );
6970 SAL_WNODEPRECATED_DECLARATIONS_POP
6971 if (pQueryParam.get())
6973 if (!pQueryParam->IsValidFieldIndex())
6975 SetError(errNoValue);
6976 return;
6978 ScDBQueryDataIterator aValIter(pDok, pQueryParam.release());
6979 ScDBQueryDataIterator::Value aValue;
6980 if (aValIter.GetFirst(aValue) && !aValue.mnError)
6984 rValCount++;
6985 values.push_back(aValue.mfValue);
6986 fSum += aValue.mfValue;
6988 while ((aValue.mnError == 0) && aValIter.GetNext(aValue));
6990 SetError(aValue.mnError);
6992 else
6993 SetError( errIllegalParameter);
6995 vMean = fSum / values.size();
6997 for (size_t i = 0; i < values.size(); i++)
6998 vSum += (values[i] - vMean) * (values[i] - vMean);
7000 rVal = vSum;
7004 void ScInterpreter::ScDBStdDev()
7006 double fVal, fCount;
7007 GetDBStVarParams( fVal, fCount );
7008 PushDouble( sqrt(fVal/(fCount-1)));
7012 void ScInterpreter::ScDBStdDevP()
7014 double fVal, fCount;
7015 GetDBStVarParams( fVal, fCount );
7016 PushDouble( sqrt(fVal/fCount));
7020 void ScInterpreter::ScDBVar()
7022 double fVal, fCount;
7023 GetDBStVarParams( fVal, fCount );
7024 PushDouble(fVal/(fCount-1));
7028 void ScInterpreter::ScDBVarP()
7030 double fVal, fCount;
7031 GetDBStVarParams( fVal, fCount );
7032 PushDouble(fVal/fCount);
7035 void ScInterpreter::ScIndirect()
7037 sal_uInt8 nParamCount = GetByte();
7038 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7040 // Reference address syntax for INDIRECT is configurable.
7041 FormulaGrammar::AddressConvention eConv = GetGlobalConfig().meStringRefAddressSyntax;
7042 if (eConv == FormulaGrammar::CONV_UNSPECIFIED)
7043 // Use the current address syntax if unspecified.
7044 eConv = pDok->GetAddressConvention();
7046 if (nParamCount == 2 && 0.0 == ::rtl::math::approxFloor( GetDouble()))
7048 // Overwrite the config and try Excel R1C1.
7049 eConv = FormulaGrammar::CONV_XL_R1C1;
7051 const ScAddress::Details aDetails( eConv, aPos );
7052 SCTAB nTab = aPos.Tab();
7053 OUString sRefStr = GetString().getString();
7054 ScRefAddress aRefAd, aRefAd2;
7055 ScAddress::ExternalInfo aExtInfo;
7056 if (ConvertDoubleRef(pDok, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo))
7058 if (aExtInfo.mbExternal)
7060 PushExternalDoubleRef(
7061 aExtInfo.mnFileId, aExtInfo.maTabName,
7062 aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
7063 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab());
7065 else
7066 PushDoubleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
7067 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab() );
7069 else if (ConvertSingleRef(pDok, sRefStr, nTab, aRefAd, aDetails, &aExtInfo))
7071 if (aExtInfo.mbExternal)
7073 PushExternalSingleRef(
7074 aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab());
7076 else
7077 PushSingleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab() );
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);
7110 PushError( errNoRef);
7116 void ScInterpreter::ScAddressFunc()
7118 OUString sTabStr;
7120 sal_uInt8 nParamCount = GetByte();
7121 if( !MustHaveParamCount( nParamCount, 2, 5 ) )
7122 return;
7124 if( nParamCount >= 5 )
7125 sTabStr = GetString().getString();
7127 FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default
7128 if( nParamCount >= 4 && 0.0 == ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0)))
7129 eConv = FormulaGrammar::CONV_XL_R1C1;
7131 sal_uInt16 nFlags = SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE; // default
7132 if( nParamCount >= 3 )
7134 sal_uInt16 n = (sal_uInt16) ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0));
7135 switch ( n )
7137 default :
7138 PushNoValue();
7139 return;
7141 case 5:
7142 case 1 : break; // default
7143 case 6:
7144 case 2 : nFlags = SCA_ROW_ABSOLUTE; break;
7145 case 7:
7146 case 3 : nFlags = SCA_COL_ABSOLUTE; break;
7147 case 8:
7148 case 4 : nFlags = 0; break; // both relative
7151 nFlags |= SCA_VALID | SCA_VALID_ROW | SCA_VALID_COL;
7153 SCCOL nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble());
7154 SCROW nRow = (SCROW) ::rtl::math::approxFloor(GetDouble());
7155 if( eConv == FormulaGrammar::CONV_XL_R1C1 )
7157 // YUCK! The XL interface actually treats rel R1C1 refs differently
7158 // than A1
7159 if( !(nFlags & SCA_COL_ABSOLUTE) )
7160 nCol += aPos.Col() + 1;
7161 if( !(nFlags & SCA_ROW_ABSOLUTE) )
7162 nRow += aPos.Row() + 1;
7165 --nCol;
7166 --nRow;
7167 if(!ValidCol( nCol) || !ValidRow( nRow))
7169 PushIllegalArgument();
7170 return;
7173 const ScAddress::Details aDetails( eConv, aPos );
7174 const ScAddress aAdr( nCol, nRow, 0);
7175 OUString aRefStr(aAdr.Format(nFlags, pDok, aDetails));
7177 if( nParamCount >= 5 && !sTabStr.isEmpty() )
7179 OUString aDoc;
7180 if (eConv == FormulaGrammar::CONV_OOO)
7182 // Isolate Tab from 'Doc'#Tab
7183 sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr);
7184 if (nPos != -1)
7186 if (sTabStr[nPos+1] == '$')
7187 ++nPos; // also split 'Doc'#$Tab
7188 aDoc = sTabStr.copy( 0, nPos+1);
7189 sTabStr = sTabStr.copy( nPos+1);
7192 /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may
7193 * need some extra handling to isolate Tab from Doc. */
7194 if (sTabStr[0] != '\'' || !sTabStr.endsWith("'"))
7195 ScCompiler::CheckTabQuotes( sTabStr, eConv);
7196 if (!aDoc.isEmpty())
7197 sTabStr = aDoc + sTabStr;
7198 sTabStr += eConv == FormulaGrammar::CONV_XL_R1C1 ? OUString("!") : OUString(".");
7199 sTabStr += aRefStr;
7200 PushString( sTabStr );
7202 else
7203 PushString( aRefStr );
7207 void ScInterpreter::ScOffset()
7209 sal_uInt8 nParamCount = GetByte();
7210 if ( MustHaveParamCount( nParamCount, 3, 5 ) )
7212 long nColNew = -1, nRowNew = -1, nColPlus, nRowPlus;
7213 if (nParamCount == 5)
7214 nColNew = (long) ::rtl::math::approxFloor(GetDouble());
7215 if (nParamCount >= 4)
7216 nRowNew = (long) ::rtl::math::approxFloor(GetDoubleWithDefault( -1.0 ));
7217 nColPlus = (long) ::rtl::math::approxFloor(GetDouble());
7218 nRowPlus = (long) ::rtl::math::approxFloor(GetDouble());
7219 SCCOL nCol1(0);
7220 SCROW nRow1(0);
7221 SCTAB nTab1(0);
7222 SCCOL nCol2(0);
7223 SCROW nRow2(0);
7224 SCTAB nTab2(0);
7225 if (nColNew == 0 || nRowNew == 0)
7227 PushIllegalArgument();
7228 return;
7230 switch (GetStackType())
7232 case svSingleRef:
7234 PopSingleRef(nCol1, nRow1, nTab1);
7235 if (nParamCount == 3 || (nColNew < 0 && nRowNew < 0))
7237 nCol1 = (SCCOL)((long) nCol1 + nColPlus);
7238 nRow1 = (SCROW)((long) nRow1 + nRowPlus);
7239 if (!ValidCol(nCol1) || !ValidRow(nRow1))
7240 PushIllegalArgument();
7241 else
7242 PushSingleRef(nCol1, nRow1, nTab1);
7244 else
7246 if (nColNew < 0)
7247 nColNew = 1;
7248 if (nRowNew < 0)
7249 nRowNew = 1;
7250 nCol1 = (SCCOL)((long)nCol1+nColPlus);
7251 nRow1 = (SCROW)((long)nRow1+nRowPlus);
7252 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
7253 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
7254 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
7255 !ValidCol(nCol2) || !ValidRow(nRow2))
7256 PushIllegalArgument();
7257 else
7258 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
7260 break;
7262 case svExternalSingleRef:
7264 sal_uInt16 nFileId;
7265 OUString aTabName;
7266 ScSingleRefData aRef;
7267 PopExternalSingleRef(nFileId, aTabName, aRef);
7268 ScAddress aAbsRef = aRef.toAbs(aPos);
7269 nCol1 = aAbsRef.Col();
7270 nRow1 = aAbsRef.Row();
7271 nTab1 = aAbsRef.Tab();
7273 if (nParamCount == 3 || (nColNew < 0 && nRowNew < 0))
7275 nCol1 = (SCCOL)((long) nCol1 + nColPlus);
7276 nRow1 = (SCROW)((long) nRow1 + nRowPlus);
7277 if (!ValidCol(nCol1) || !ValidRow(nRow1))
7278 PushIllegalArgument();
7279 else
7280 PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1);
7282 else
7284 if (nColNew < 0)
7285 nColNew = 1;
7286 if (nRowNew < 0)
7287 nRowNew = 1;
7288 nCol1 = (SCCOL)((long)nCol1+nColPlus);
7289 nRow1 = (SCROW)((long)nRow1+nRowPlus);
7290 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
7291 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
7292 nTab2 = nTab1;
7293 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
7294 !ValidCol(nCol2) || !ValidRow(nRow2))
7295 PushIllegalArgument();
7296 else
7297 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7299 break;
7301 case svDoubleRef:
7303 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7304 if (nColNew < 0)
7305 nColNew = nCol2 - nCol1 + 1;
7306 if (nRowNew < 0)
7307 nRowNew = nRow2 - nRow1 + 1;
7308 nCol1 = (SCCOL)((long)nCol1+nColPlus);
7309 nRow1 = (SCROW)((long)nRow1+nRowPlus);
7310 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
7311 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
7312 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
7313 !ValidCol(nCol2) || !ValidRow(nRow2) || nTab1 != nTab2)
7314 PushIllegalArgument();
7315 else
7316 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
7317 break;
7319 case svExternalDoubleRef:
7321 sal_uInt16 nFileId;
7322 OUString aTabName;
7323 ScComplexRefData aRef;
7324 PopExternalDoubleRef(nFileId, aTabName, aRef);
7325 ScRange aAbs = aRef.toAbs(aPos);
7326 nCol1 = aAbs.aStart.Col();
7327 nRow1 = aAbs.aStart.Row();
7328 nTab1 = aAbs.aStart.Tab();
7329 nCol2 = aAbs.aEnd.Col();
7330 nRow2 = aAbs.aEnd.Row();
7331 nTab2 = aAbs.aEnd.Tab();
7332 if (nColNew < 0)
7333 nColNew = nCol2 - nCol1 + 1;
7334 if (nRowNew < 0)
7335 nRowNew = nRow2 - nRow1 + 1;
7336 nCol1 = (SCCOL)((long)nCol1+nColPlus);
7337 nRow1 = (SCROW)((long)nRow1+nRowPlus);
7338 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
7339 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
7340 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
7341 !ValidCol(nCol2) || !ValidRow(nRow2) || nTab1 != nTab2)
7342 PushIllegalArgument();
7343 else
7344 PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7345 break;
7347 default:
7348 PushIllegalParameter();
7349 break;
7350 } // end switch
7355 void ScInterpreter::ScIndex()
7357 sal_uInt8 nParamCount = GetByte();
7358 if ( MustHaveParamCount( nParamCount, 1, 4 ) )
7360 long nArea;
7361 size_t nAreaCount;
7362 SCCOL nCol;
7363 SCROW nRow;
7364 if (nParamCount == 4)
7365 nArea = (long) ::rtl::math::approxFloor(GetDouble());
7366 else
7367 nArea = 1;
7368 if (nParamCount >= 3)
7369 nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble());
7370 else
7371 nCol = 0;
7372 if (nParamCount >= 2)
7373 nRow = (SCROW) ::rtl::math::approxFloor(GetDouble());
7374 else
7375 nRow = 0;
7376 if (GetStackType() == svRefList)
7377 nAreaCount = (sp ? static_cast<ScToken*>(pStack[sp-1])->GetRefList()->size() : 0);
7378 else
7379 nAreaCount = 1; // one reference or array or whatever
7380 if (nAreaCount == 0 || (size_t)nArea > nAreaCount)
7382 PushError( errNoRef);
7383 return;
7385 else if (nArea < 1 || nCol < 0 || nRow < 0)
7387 PushIllegalArgument();
7388 return;
7390 switch (GetStackType())
7392 case svMatrix:
7393 case svExternalSingleRef:
7394 case svExternalDoubleRef:
7396 if (nArea != 1)
7397 SetError(errIllegalArgument);
7398 sal_uInt16 nOldSp = sp;
7399 ScMatrixRef pMat = GetMatrix();
7400 if (pMat)
7402 SCSIZE nC, nR;
7403 pMat->GetDimensions(nC, nR);
7404 // Access one element of a vector independent of col/row
7405 // orientation?
7406 bool bVector = ((nCol == 0 || nRow == 0) && (nC == 1 || nR == 1));
7407 SCSIZE nElement = ::std::max( static_cast<SCSIZE>(nCol),
7408 static_cast<SCSIZE>(nRow));
7409 if (nC == 0 || nR == 0 ||
7410 (!bVector && (static_cast<SCSIZE>(nCol) > nC ||
7411 static_cast<SCSIZE>(nRow) > nR)) ||
7412 (bVector && nElement > nC * nR))
7413 PushIllegalArgument();
7414 else if (nCol == 0 && nRow == 0)
7415 sp = nOldSp;
7416 else if (bVector)
7418 --nElement;
7419 if (pMat->IsString( nElement))
7420 PushString( pMat->GetString(nElement).getString());
7421 else
7422 PushDouble( pMat->GetDouble( nElement));
7424 else if (nCol == 0)
7426 ScMatrixRef pResMat = GetNewMat(nC, 1);
7427 if (pResMat)
7429 SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1);
7430 for (SCSIZE i = 0; i < nC; i++)
7431 if (!pMat->IsString(i, nRowMinus1))
7432 pResMat->PutDouble(pMat->GetDouble(i,
7433 nRowMinus1), i, 0);
7434 else
7435 pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0);
7437 PushMatrix(pResMat);
7439 else
7440 PushIllegalArgument();
7442 else if (nRow == 0)
7444 ScMatrixRef pResMat = GetNewMat(1, nR);
7445 if (pResMat)
7447 SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1);
7448 for (SCSIZE i = 0; i < nR; i++)
7449 if (!pMat->IsString(nColMinus1, i))
7450 pResMat->PutDouble(pMat->GetDouble(nColMinus1,
7451 i), i);
7452 else
7453 pResMat->PutString(pMat->GetString(nColMinus1, i), i);
7454 PushMatrix(pResMat);
7456 else
7457 PushIllegalArgument();
7459 else
7461 if (!pMat->IsString( static_cast<SCSIZE>(nCol-1),
7462 static_cast<SCSIZE>(nRow-1)))
7463 PushDouble( pMat->GetDouble(
7464 static_cast<SCSIZE>(nCol-1),
7465 static_cast<SCSIZE>(nRow-1)));
7466 else
7467 PushString( pMat->GetString(
7468 static_cast<SCSIZE>(nCol-1),
7469 static_cast<SCSIZE>(nRow-1)).getString());
7473 break;
7474 case svSingleRef:
7476 SCCOL nCol1 = 0;
7477 SCROW nRow1 = 0;
7478 SCTAB nTab1 = 0;
7479 PopSingleRef( nCol1, nRow1, nTab1);
7480 if (nCol > 1 || nRow > 1)
7481 PushIllegalArgument();
7482 else
7483 PushSingleRef( nCol1, nRow1, nTab1);
7485 break;
7486 case svDoubleRef:
7487 case svRefList:
7489 SCCOL nCol1 = 0;
7490 SCROW nRow1 = 0;
7491 SCTAB nTab1 = 0;
7492 SCCOL nCol2 = 0;
7493 SCROW nRow2 = 0;
7494 SCTAB nTab2 = 0;
7495 bool bRowArray = false;
7496 if (GetStackType() == svRefList)
7498 FormulaTokenRef xRef = PopToken();
7499 if (nGlobalError || !xRef)
7501 PushIllegalParameter();
7502 return;
7504 ScRange aRange( ScAddress::UNINITIALIZED);
7505 DoubleRefToRange( (*(static_cast<ScToken*>(xRef.get())->GetRefList()))[nArea-1], aRange);
7506 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7507 if ( nParamCount == 2 && nRow1 == nRow2 )
7508 bRowArray = true;
7510 else
7512 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
7513 if ( nParamCount == 2 && nRow1 == nRow2 )
7514 bRowArray = true;
7516 if ( nTab1 != nTab2 ||
7517 (nCol > 0 && nCol1+nCol-1 > nCol2) ||
7518 (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) ||
7519 ( nRow > nCol2 - nCol1 + 1 && bRowArray ))
7520 PushIllegalArgument();
7521 else if (nCol == 0 && nRow == 0)
7523 if ( nCol1 == nCol2 && nRow1 == nRow2 )
7524 PushSingleRef( nCol1, nRow1, nTab1 );
7525 else
7526 PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 );
7528 else if (nRow == 0)
7530 if ( nRow1 == nRow2 )
7531 PushSingleRef( nCol1+nCol-1, nRow1, nTab1 );
7532 else
7533 PushDoubleRef( nCol1+nCol-1, nRow1, nTab1,
7534 nCol1+nCol-1, nRow2, nTab1 );
7536 else if (nCol == 0)
7538 if ( nCol1 == nCol2 )
7539 PushSingleRef( nCol1, nRow1+nRow-1, nTab1 );
7540 else if ( bRowArray )
7542 nCol =(SCCOL) nRow;
7543 nRow = 1;
7544 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
7546 else
7547 PushDoubleRef( nCol1, nRow1+nRow-1, nTab1,
7548 nCol2, nRow1+nRow-1, nTab1);
7550 else
7551 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
7553 break;
7554 default:
7555 PushIllegalParameter();
7561 void ScInterpreter::ScMultiArea()
7563 // Legacy support, convert to RefList
7564 sal_uInt8 nParamCount = GetByte();
7565 if (MustHaveParamCountMin( nParamCount, 1))
7567 while (!nGlobalError && nParamCount-- > 1)
7569 ScUnionFunc();
7575 void ScInterpreter::ScAreas()
7577 sal_uInt8 nParamCount = GetByte();
7578 if (MustHaveParamCount( nParamCount, 1))
7580 size_t nCount = 0;
7581 switch (GetStackType())
7583 case svSingleRef:
7585 FormulaTokenRef xT = PopToken();
7586 ValidateRef( static_cast<ScToken*>(xT.get())->GetSingleRef());
7587 ++nCount;
7589 break;
7590 case svDoubleRef:
7592 FormulaTokenRef xT = PopToken();
7593 ValidateRef( static_cast<ScToken*>(xT.get())->GetDoubleRef());
7594 ++nCount;
7596 break;
7597 case svRefList:
7599 FormulaTokenRef xT = PopToken();
7600 ValidateRef( *(static_cast<ScToken*>(xT.get())->GetRefList()));
7601 nCount += static_cast<ScToken*>(xT.get())->GetRefList()->size();
7603 break;
7604 default:
7605 SetError( errIllegalParameter);
7607 PushDouble( double(nCount));
7612 void ScInterpreter::ScCurrency()
7614 sal_uInt8 nParamCount = GetByte();
7615 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7617 OUString aStr;
7618 double fDec;
7619 if (nParamCount == 2)
7621 fDec = ::rtl::math::approxFloor(GetDouble());
7622 if (fDec < -15.0 || fDec > 15.0)
7624 PushIllegalArgument();
7625 return;
7628 else
7629 fDec = 2.0;
7630 double fVal = GetDouble();
7631 double fFac;
7632 if ( fDec != 0.0 )
7633 fFac = pow( (double)10, fDec );
7634 else
7635 fFac = 1.0;
7636 if (fVal < 0.0)
7637 fVal = ceil(fVal*fFac-0.5)/fFac;
7638 else
7639 fVal = floor(fVal*fFac+0.5)/fFac;
7640 Color* pColor = NULL;
7641 if ( fDec < 0.0 )
7642 fDec = 0.0;
7643 sal_uLong nIndex = pFormatter->GetStandardFormat(
7644 NUMBERFORMAT_CURRENCY,
7645 ScGlobal::eLnge);
7646 if ( (sal_uInt16) fDec != pFormatter->GetFormatPrecision( nIndex ) )
7648 OUString sFormatString = pFormatter->GenerateFormat(
7649 nIndex,
7650 ScGlobal::eLnge,
7651 true, // mit Tausenderpunkt
7652 false, // nicht rot
7653 (sal_uInt16) fDec,// Nachkommastellen
7654 1); // 1 Vorkommanull
7655 if (!pFormatter->GetPreviewString(sFormatString,
7656 fVal,
7657 aStr,
7658 &pColor,
7659 ScGlobal::eLnge))
7660 SetError(errIllegalArgument);
7662 else
7664 pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor);
7666 PushString(aStr);
7671 void ScInterpreter::ScReplace()
7673 if ( MustHaveParamCount( GetByte(), 4 ) )
7675 OUString aNewStr = GetString().getString();
7676 double fCount = ::rtl::math::approxFloor( GetDouble());
7677 double fPos = ::rtl::math::approxFloor( GetDouble());
7678 OUString aOldStr = GetString().getString();
7679 if (fPos < 1.0 || fPos > static_cast<double>(STRING_MAXLEN)
7680 || fCount < 0.0 || fCount > static_cast<double>(STRING_MAXLEN))
7681 PushIllegalArgument();
7682 else
7684 sal_Int32 nCount = static_cast<sal_Int32>(fCount);
7685 sal_Int32 nPos = static_cast<sal_Int32>(fPos);
7686 sal_Int32 nLen = aOldStr.getLength();
7687 if (nPos > nLen + 1)
7688 nPos = nLen + 1;
7689 if (nCount > nLen - nPos + 1)
7690 nCount = nLen - nPos + 1;
7691 aOldStr = aOldStr.replaceAt( nPos-1, nCount, "" );
7692 if ( CheckStringResultLen( aOldStr, aNewStr ) )
7693 aOldStr = aOldStr.replaceAt( nPos-1, 0, aNewStr );
7694 PushString( aOldStr );
7700 void ScInterpreter::ScFixed()
7702 sal_uInt8 nParamCount = GetByte();
7703 if ( MustHaveParamCount( nParamCount, 1, 3 ) )
7705 OUString aStr;
7706 double fDec;
7707 bool bThousand;
7708 if (nParamCount == 3)
7709 bThousand = !GetBool(); // Param TRUE: keine Tausenderpunkte
7710 else
7711 bThousand = true;
7712 if (nParamCount >= 2)
7714 fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
7715 if (fDec < -15.0 || fDec > 15.0)
7717 PushIllegalArgument();
7718 return;
7721 else
7722 fDec = 2.0;
7723 double fVal = GetDouble();
7724 double fFac;
7725 if ( fDec != 0.0 )
7726 fFac = pow( (double)10, fDec );
7727 else
7728 fFac = 1.0;
7729 if (fVal < 0.0)
7730 fVal = ceil(fVal*fFac-0.5)/fFac;
7731 else
7732 fVal = floor(fVal*fFac+0.5)/fFac;
7733 Color* pColor = NULL;
7734 if (fDec < 0.0)
7735 fDec = 0.0;
7736 sal_uLong nIndex = pFormatter->GetStandardFormat(
7737 NUMBERFORMAT_NUMBER,
7738 ScGlobal::eLnge);
7739 OUString sFormatString = pFormatter->GenerateFormat(
7740 nIndex,
7741 ScGlobal::eLnge,
7742 bThousand, // mit Tausenderpunkt
7743 false, // nicht rot
7744 (sal_uInt16) fDec,// Nachkommastellen
7745 1); // 1 Vorkommanull
7746 if (!pFormatter->GetPreviewString(sFormatString,
7747 fVal,
7748 aStr,
7749 &pColor,
7750 ScGlobal::eLnge))
7751 PushIllegalArgument();
7752 else
7753 PushString(aStr);
7758 void ScInterpreter::ScFind()
7760 sal_uInt8 nParamCount = GetByte();
7761 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
7763 double fAnz;
7764 if (nParamCount == 3)
7765 fAnz = GetDouble();
7766 else
7767 fAnz = 1.0;
7768 OUString sStr = GetString().getString();
7769 if( fAnz < 1.0 || fAnz > (double) sStr.getLength() )
7770 PushNoValue();
7771 else
7773 sal_Int32 nPos = sStr.indexOf(GetString().getString(), static_cast<sal_Int32>(fAnz - 1));
7774 if (nPos == -1)
7775 PushNoValue();
7776 else
7777 PushDouble((double)(nPos + 1));
7783 void ScInterpreter::ScExact()
7785 nFuncFmtType = NUMBERFORMAT_LOGICAL;
7786 if ( MustHaveParamCount( GetByte(), 2 ) )
7788 svl::SharedString s1 = GetString();
7789 svl::SharedString s2 = GetString();
7790 PushInt( s1.getData() == s2.getData() );
7795 void ScInterpreter::ScLeft()
7797 sal_uInt8 nParamCount = GetByte();
7798 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7800 sal_Int32 n;
7801 if (nParamCount == 2)
7803 double nVal = ::rtl::math::approxFloor(GetDouble());
7804 if ( nVal < 0.0 || nVal > STRING_MAXLEN )
7806 PushIllegalArgument();
7807 return ;
7809 else
7810 n = (sal_Int32) nVal;
7812 else
7813 n = 1;
7814 OUString aStr = GetString().getString();
7815 n = std::min(n, aStr.getLength());
7816 aStr = aStr.copy( 0, n );
7817 PushString( aStr );
7821 typedef struct {
7822 UBlockCode from;
7823 UBlockCode to;
7824 } UBlockScript;
7826 static const UBlockScript scriptList[] = {
7827 {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO},
7828 {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES},
7829 {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT },
7830 {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS},
7831 {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS},
7832 {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS},
7833 {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT},
7834 {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES}
7836 #define scriptListCount sizeof (scriptList) / sizeof (UBlockScript)
7837 bool SAL_CALL lcl_getScriptClass(sal_uInt32 currentChar)
7839 // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian
7840 if( (currentChar == 0x005c || currentChar == 0x20ac) &&
7841 (MsLangId::getSystemLanguage() == LANGUAGE_JAPANESE) )
7842 return true;
7843 sal_uInt16 i;
7844 static sal_Int16 nRet = 0;
7845 UBlockCode block = (UBlockCode)ublock_getCode((sal_uInt32)currentChar);
7846 for ( i = 0; i < scriptListCount; i++) {
7847 if (block <= scriptList[i].to) break;
7849 nRet = (i < scriptListCount && block >= scriptList[i].from);
7850 return nRet;
7852 bool IsDBCS(sal_Unicode ch)
7854 return lcl_getScriptClass(ch);
7856 sal_Int32 getLengthB(const OUString &str)
7858 if(str.isEmpty())
7859 return 0;
7860 sal_Int32 index = 0;
7861 sal_Int32 length = 0;
7862 while(index < str.getLength()){
7863 if(IsDBCS(str[index]))
7864 length += 2;
7865 else
7866 length++;
7867 index++;
7869 return length;
7871 void ScInterpreter::ScLenB()
7873 PushDouble( getLengthB(GetString().getString()) );
7875 OUString lcl_RightB(const OUString &rStr, sal_Int32 n)
7877 if( n < getLengthB(rStr) )
7879 OUStringBuffer aBuf(rStr);
7880 sal_Int32 index = aBuf.getLength();
7881 while(index-- >= 0)
7883 if(0 == n)
7885 aBuf.remove( 0, index + 1);
7886 break;
7888 if(-1 == n)
7890 aBuf.remove( 0, index + 2 );
7891 aBuf.insert( 0, " ");
7892 break;
7894 if(IsDBCS(aBuf[index]))
7895 n -= 2;
7896 else
7897 n--;
7899 return aBuf.makeStringAndClear();
7901 return rStr;
7903 void ScInterpreter::ScRightB()
7905 sal_uInt8 nParamCount = GetByte();
7906 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7908 sal_Int32 n;
7909 if (nParamCount == 2)
7911 double nVal = ::rtl::math::approxFloor(GetDouble());
7912 if ( nVal < 0.0 || nVal > STRING_MAXLEN )
7914 PushIllegalArgument();
7915 return ;
7917 else
7918 n = (sal_Int32) nVal;
7920 else
7921 n = 1;
7922 OUString aStr(lcl_RightB(GetString().getString(), n));
7923 PushString( aStr );
7926 OUString lcl_LeftB(const OUString &rStr, sal_Int32 n)
7928 if( n < getLengthB(rStr) )
7930 OUStringBuffer aBuf(rStr);
7931 sal_Int32 index = -1;
7932 while(index++ < aBuf.getLength())
7934 if(0 == n)
7936 aBuf.truncate(index);
7937 break;
7939 if(-1 == n)
7941 aBuf.truncate( index - 1 );
7942 aBuf.append(" ");
7943 break;
7945 if(IsDBCS(aBuf[index]))
7946 n -= 2;
7947 else
7948 n--;
7950 return aBuf.makeStringAndClear();
7952 return rStr;
7954 void ScInterpreter::ScLeftB()
7956 sal_uInt8 nParamCount = GetByte();
7957 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
7959 sal_Int32 n;
7960 if (nParamCount == 2)
7962 double nVal = ::rtl::math::approxFloor(GetDouble());
7963 if ( nVal < 0.0 || nVal > STRING_MAXLEN )
7965 PushIllegalArgument();
7966 return ;
7968 else
7969 n = (sal_Int32) nVal;
7971 else
7972 n = 1;
7973 OUString aStr(lcl_LeftB(GetString().getString(), n));
7974 PushString( aStr );
7977 void ScInterpreter::ScMidB()
7979 if ( MustHaveParamCount( GetByte(), 3 ) )
7981 double fAnz = ::rtl::math::approxFloor(GetDouble());
7982 double fAnfang = ::rtl::math::approxFloor(GetDouble());
7983 OUString aStr = GetString().getString();
7984 if (fAnfang < 1.0 || fAnz < 0.0 || fAnfang > double(STRING_MAXLEN) || fAnz > double(STRING_MAXLEN))
7985 PushIllegalArgument();
7986 else
7989 aStr = lcl_LeftB(aStr, (sal_Int32)fAnfang + (sal_Int32)fAnz - 1);
7990 sal_Int32 nCnt = getLengthB(aStr) - (sal_Int32)fAnfang + 1;
7991 aStr = lcl_RightB(aStr, nCnt>0 ? nCnt:0);
7992 PushString(aStr);
7997 void ScInterpreter::ScRight()
7999 sal_uInt8 nParamCount = GetByte();
8000 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
8002 sal_Int32 n;
8003 if (nParamCount == 2)
8005 double nVal = ::rtl::math::approxFloor(GetDouble());
8006 if ( nVal < 0.0 || nVal > STRING_MAXLEN )
8008 PushIllegalArgument();
8009 return ;
8011 else
8012 n = (sal_Int32) nVal;
8014 else
8015 n = 1;
8016 OUString aStr = GetString().getString();
8017 if( n < aStr.getLength() )
8018 aStr = aStr.copy( aStr.getLength() - n );
8019 PushString( aStr );
8024 void ScInterpreter::ScSearch()
8026 sal_uInt8 nParamCount = GetByte();
8027 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
8029 double fAnz;
8030 if (nParamCount == 3)
8032 fAnz = ::rtl::math::approxFloor(GetDouble());
8033 if (fAnz > double(STRING_MAXLEN))
8035 PushIllegalArgument();
8036 return;
8039 else
8040 fAnz = 1.0;
8041 OUString sStr = GetString().getString();
8042 OUString SearchStr = GetString().getString();
8043 sal_Int32 nPos = fAnz - 1;
8044 sal_Int32 nEndPos = sStr.getLength();
8045 if( nPos >= nEndPos )
8046 PushNoValue();
8047 else
8049 utl::SearchParam::SearchType eSearchType =
8050 (MayBeRegExp( SearchStr, pDok ) ?
8051 utl::SearchParam::SRCH_REGEXP : utl::SearchParam::SRCH_NORMAL);
8052 utl::SearchParam sPar(SearchStr, eSearchType, false, false, false);
8053 utl::TextSearch sT( sPar, *ScGlobal::pCharClass );
8054 int nBool = sT.SearchForward(sStr, &nPos, &nEndPos);
8055 if (!nBool)
8056 PushNoValue();
8057 else
8058 PushDouble((double)(nPos) + 1);
8063 void ScInterpreter::ScMid()
8065 if ( MustHaveParamCount( GetByte(), 3 ) )
8067 double fAnz = ::rtl::math::approxFloor(GetDouble());
8068 double fAnfang = ::rtl::math::approxFloor(GetDouble());
8069 OUString aStr = GetString().getString();
8070 if (fAnfang < 1.0 || fAnz < 0.0 || fAnfang > double(STRING_MAXLEN) || fAnz > double(STRING_MAXLEN))
8071 PushIllegalArgument();
8072 else
8074 sal_Int32 nCharacters = std::min<sal_Int32>(static_cast<sal_Int32>(fAnz), aStr.getLength() - fAnfang + 1);
8075 OUString sRes;
8076 if (nCharacters > 0)
8077 sRes = aStr.copy(static_cast<sal_Int32>(fAnfang-1), nCharacters);
8078 PushString(sRes);
8083 void ScInterpreter::ScText()
8085 if ( MustHaveParamCount( GetByte(), 2 ) )
8087 OUString sFormatString = GetString().getString();
8088 svl::SharedString aStr;
8089 bool bString = false;
8090 double fVal = 0.0;
8091 switch (GetStackType())
8093 case svError:
8094 PopError();
8095 break;
8096 case svDouble:
8097 fVal = PopDouble();
8098 break;
8099 default:
8101 FormulaTokenRef xTok( PopToken());
8102 if (!nGlobalError)
8104 PushTempToken( xTok.get());
8105 // Temporarily override the ConvertStringToValue()
8106 // error for GetCellValue() / GetCellValueOrZero()
8107 sal_uInt16 nSErr = mnStringNoValueError;
8108 mnStringNoValueError = errNotNumericString;
8109 fVal = GetDouble();
8110 mnStringNoValueError = nSErr;
8111 if (nGlobalError == errNotNumericString)
8113 // Not numeric.
8114 nGlobalError = 0;
8115 PushTempToken( xTok.get());
8116 aStr = GetString();
8117 bString = true;
8122 if (nGlobalError)
8123 PushError( nGlobalError);
8124 else
8126 OUString aResult;
8127 Color* pColor = NULL;
8128 LanguageType eCellLang;
8129 const ScPatternAttr* pPattern = pDok->GetPattern(
8130 aPos.Col(), aPos.Row(), aPos.Tab() );
8131 if ( pPattern )
8132 eCellLang = ((const SvxLanguageItem&)
8133 pPattern->GetItem( ATTR_LANGUAGE_FORMAT )).GetValue();
8134 else
8135 eCellLang = ScGlobal::eLnge;
8136 if (bString)
8138 if (!pFormatter->GetPreviewString( sFormatString, aStr.getString(),
8139 aResult, &pColor, eCellLang))
8140 PushIllegalArgument();
8141 else
8142 PushString( aResult);
8144 else
8146 if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal,
8147 aResult, &pColor, eCellLang))
8148 PushIllegalArgument();
8149 else
8150 PushString( aResult);
8157 void ScInterpreter::ScSubstitute()
8159 sal_uInt8 nParamCount = GetByte();
8160 if ( MustHaveParamCount( nParamCount, 3, 4 ) )
8162 sal_Int32 nAnz;
8163 if (nParamCount == 4)
8165 double fAnz = ::rtl::math::approxFloor(GetDouble());
8166 if( fAnz < 1 || fAnz > STRING_MAXLEN )
8168 PushIllegalArgument();
8169 return;
8171 else
8172 nAnz = (sal_Int32) fAnz;
8174 else
8175 nAnz = 0;
8176 OUString sNewStr = GetString().getString();
8177 OUString sOldStr = GetString().getString();
8178 OUString sStr = GetString().getString();
8179 sal_Int32 nPos = 0;
8180 sal_Int32 nCount = 0;
8181 sal_Int32 nNewLen = sNewStr.getLength();
8182 sal_Int32 nOldLen = sOldStr.getLength();
8183 while( true )
8185 nPos = sStr.indexOf( sOldStr, nPos );
8186 if (nPos != -1)
8188 nCount++;
8189 if( !nAnz || nCount == nAnz )
8191 sStr = sStr.replaceAt(nPos,nOldLen, "");
8192 if ( CheckStringResultLen( sStr, sNewStr ) )
8194 sStr = sStr.replaceAt(nPos, 0, sNewStr);
8195 nPos = sal::static_int_cast<sal_Int32>( nPos + nNewLen );
8197 else
8198 break;
8200 else
8201 nPos++;
8203 else
8204 break;
8206 PushString( sStr );
8211 void ScInterpreter::ScRept()
8213 if ( MustHaveParamCount( GetByte(), 2 ) )
8215 double fAnz = ::rtl::math::approxFloor(GetDouble());
8216 OUString aStr = GetString().getString();
8217 if ( fAnz < 0.0 )
8218 PushIllegalArgument();
8219 else if ( fAnz * aStr.getLength() > STRING_MAXLEN )
8221 PushError( errStringOverflow );
8223 else if ( fAnz == 0.0 )
8224 PushString( EMPTY_OUSTRING );
8225 else
8227 const sal_Int32 nLen = aStr.getLength();
8228 sal_Int32 n = (sal_Int32) fAnz;
8229 OUStringBuffer aRes(n*nLen);
8230 while( n-- )
8231 aRes.append(aStr);
8232 PushString( aRes.makeStringAndClear() );
8238 void ScInterpreter::ScConcat()
8240 sal_uInt8 nParamCount = GetByte();
8241 OUString aRes;
8242 while( nParamCount-- > 0)
8244 OUString aStr = GetString().getString();
8245 aRes = aStr + aRes;
8247 PushString( aRes );
8251 void ScInterpreter::ScErrorType()
8253 sal_uInt16 nErr;
8254 sal_uInt16 nOldError = nGlobalError;
8255 nGlobalError = 0;
8256 switch ( GetStackType() )
8258 case svRefList :
8260 FormulaTokenRef x = PopToken();
8261 if (nGlobalError)
8262 nErr = nGlobalError;
8263 else
8265 const ScRefList* pRefList = static_cast<ScToken*>(x.get())->GetRefList();
8266 size_t n = pRefList->size();
8267 if (!n)
8268 nErr = errNoRef;
8269 else if (n > 1)
8270 nErr = errNoValue;
8271 else
8273 ScRange aRange;
8274 DoubleRefToRange( (*pRefList)[0], aRange);
8275 if (nGlobalError)
8276 nErr = nGlobalError;
8277 else
8279 ScAddress aAdr;
8280 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
8281 nErr = pDok->GetErrCode( aAdr );
8282 else
8283 nErr = nGlobalError;
8288 break;
8289 case svDoubleRef :
8291 ScRange aRange;
8292 PopDoubleRef( aRange );
8293 if ( nGlobalError )
8294 nErr = nGlobalError;
8295 else
8297 ScAddress aAdr;
8298 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
8299 nErr = pDok->GetErrCode( aAdr );
8300 else
8301 nErr = nGlobalError;
8304 break;
8305 case svSingleRef :
8307 ScAddress aAdr;
8308 PopSingleRef( aAdr );
8309 if ( nGlobalError )
8310 nErr = nGlobalError;
8311 else
8312 nErr = pDok->GetErrCode( aAdr );
8314 break;
8315 default:
8316 PopError();
8317 nErr = nGlobalError;
8319 if ( nErr )
8321 nGlobalError = 0;
8322 PushDouble( nErr );
8324 else
8326 nGlobalError = nOldError;
8327 PushNA();
8332 bool ScInterpreter::MayBeRegExp( const OUString& rStr, const ScDocument* pDoc )
8334 if ( pDoc && !pDoc->GetDocOptions().IsFormulaRegexEnabled() )
8335 return false;
8336 if ( rStr.isEmpty() || (rStr.getLength() == 1 && !rStr.startsWith(".")) )
8337 return false; // single meta characters can not be a regexp
8338 static const sal_Unicode cre[] = { '.','*','+','?','[',']','^','$','\\','<','>','(',')','|', 0 };
8339 const sal_Unicode* p1 = rStr.getStr();
8340 sal_Unicode c1;
8341 while ( ( c1 = *p1++ ) != 0 )
8343 const sal_Unicode* p2 = cre;
8344 while ( *p2 )
8346 if ( c1 == *p2++ )
8347 return true;
8350 return false;
8353 static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument * pDoc,
8354 const ScQueryParam & rParam, const ScQueryEntry & rEntry )
8356 bool bFound = false;
8357 ScQueryCellIterator aCellIter( pDoc, rParam.nTab, rParam, false);
8358 if (rEntry.eOp != SC_EQUAL)
8360 // range lookup <= or >=
8361 SCCOL nCol;
8362 SCROW nRow;
8363 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow);
8364 if (bFound)
8366 o_rResultPos.SetCol( nCol);
8367 o_rResultPos.SetRow( nRow);
8370 else if (aCellIter.GetFirst())
8372 // EQUAL
8373 bFound = true;
8374 o_rResultPos.SetCol( aCellIter.GetCol());
8375 o_rResultPos.SetRow( aCellIter.GetRow());
8377 return bFound;
8380 bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos,
8381 const ScQueryParam & rParam ) const
8383 bool bFound = false;
8384 const ScQueryEntry& rEntry = rParam.GetEntry(0);
8385 bool bColumnsMatch = (rParam.nCol1 == rEntry.nField);
8386 OSL_ENSURE( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match");
8387 if (!bColumnsMatch)
8388 bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry);
8389 else
8391 ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab,
8392 rParam.nCol2, rParam.nRow2, rParam.nTab);
8393 ScLookupCache& rCache = pDok->GetLookupCache( aLookupRange);
8394 ScLookupCache::QueryCriteria aCriteria( rEntry);
8395 ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos,
8396 aCriteria, aPos);
8397 switch (eCacheResult)
8399 case ScLookupCache::NOT_CACHED :
8400 case ScLookupCache::CRITERIA_DIFFERENT :
8401 bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry);
8402 if (eCacheResult == ScLookupCache::NOT_CACHED)
8403 rCache.insert( o_rResultPos, aCriteria, aPos, bFound);
8404 break;
8405 case ScLookupCache::FOUND :
8406 bFound = true;
8407 break;
8408 case ScLookupCache::NOT_AVAILABLE :
8409 ; // nothing, bFound remains FALSE
8410 break;
8413 return bFound;
8416 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */