update dev300-m57
[ooovba.git] / sc / source / core / tool / interpr1.cxx
blob18f162d11aa41cf0ac4b5d87105507c4dd168e5e
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: interpr1.cxx,v $
10 * $Revision: 1.61 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_sc.hxx"
34 // INCLUDE ---------------------------------------------------------------
36 #include "scitems.hxx"
37 #include <svx/langitem.hxx>
38 #include <svx/algitem.hxx>
39 #include <unotools/textsearch.hxx>
40 #include <svtools/zforlist.hxx>
41 #include <svtools/zformat.hxx>
42 #include <tools/urlobj.hxx>
43 #include <unotools/charclass.hxx>
44 #include <sfx2/docfile.hxx>
45 #include <sfx2/printer.hxx>
46 #include <unotools/collatorwrapper.hxx>
47 #include <unotools/transliterationwrapper.hxx>
48 #include <rtl/logfile.hxx>
50 #include "interpre.hxx"
51 #include "patattr.hxx"
52 #include "global.hxx"
53 #include "document.hxx"
54 #include "dociter.hxx"
55 #include "cell.hxx"
56 #include "scmatrix.hxx"
57 #include "docoptio.hxx"
58 #include "globstr.hrc"
59 #include "attrib.hxx"
60 #include "jumpmatrix.hxx"
62 #ifndef _COMPHELPER_PROCESSFACTORY_HXX_
63 #include <comphelper/processfactory.hxx>
64 #endif
66 #include <stdlib.h>
67 #include <string.h>
68 #include <math.h>
69 #include <vector>
70 #include "cellkeytranslator.hxx"
71 #include "lookupcache.hxx"
72 #include "rangenam.hxx"
73 #include "compiler.hxx"
74 #include "externalrefmgr.hxx"
75 #include <basic/sbstar.hxx>
77 #define SC_DOUBLE_MAXVALUE 1.7e307
79 IMPL_FIXEDMEMPOOL_NEWDEL( ScTokenStack, 8, 4 )
80 IMPL_FIXEDMEMPOOL_NEWDEL( ScInterpreter, 32, 16 )
82 ScTokenStack* ScInterpreter::pGlobalStack = NULL;
83 BOOL ScInterpreter::bGlobalStackInUse = FALSE;
85 using namespace formula;
86 //-----------------------------------------------------------------------------
87 // Funktionen
88 //-----------------------------------------------------------------------------
91 void ScInterpreter::ScIfJump()
93 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIfJump" );
94 const short* pJump = pCur->GetJump();
95 short nJumpCount = pJump[ 0 ];
96 MatrixDoubleRefToMatrix();
97 switch ( GetStackType() )
99 case svMatrix:
101 ScMatrixRef pMat = PopMatrix();
102 if ( !pMat )
103 PushIllegalParameter();
104 else
106 FormulaTokenRef xNew;
107 ScTokenMatrixMap::const_iterator aMapIter;
108 // DoubleError handled by JumpMatrix
109 pMat->SetErrorInterpreter( NULL);
110 SCSIZE nCols, nRows;
111 pMat->GetDimensions( nCols, nRows );
112 if ( nCols == 0 || nRows == 0 )
113 PushIllegalArgument();
114 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
115 pCur)) != pTokenMatrixMap->end()))
116 xNew = (*aMapIter).second;
117 else
119 ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
120 for ( SCSIZE nC=0; nC < nCols; ++nC )
122 for ( SCSIZE nR=0; nR < nRows; ++nR )
124 double fVal;
125 bool bTrue;
126 ScMatValType nType = 0;
127 const ScMatrixValue* pMatVal = pMat->Get( nC, nR,
128 nType);
129 bool bIsValue = ScMatrix::IsValueType( nType);
130 if ( bIsValue )
132 fVal = pMatVal->fVal;
133 bIsValue = ::rtl::math::isFinite( fVal );
134 bTrue = bIsValue && (fVal != 0.0);
135 if ( bTrue )
136 fVal = 1.0;
138 else
140 // Treat empty and empty path as 0, but string
141 // as error.
142 bIsValue = !ScMatrix::IsRealStringType( nType);
143 bTrue = false;
144 fVal = (bIsValue ? 0.0 : CreateDoubleError( errNoValue));
146 if ( bTrue )
147 { // TRUE
148 if( nJumpCount >= 2 )
149 { // THEN path
150 pJumpMat->SetJump( nC, nR, fVal,
151 pJump[ 1 ],
152 pJump[ nJumpCount ]);
154 else
155 { // no parameter given for THEN
156 pJumpMat->SetJump( nC, nR, fVal,
157 pJump[ nJumpCount ],
158 pJump[ nJumpCount ]);
161 else
162 { // FALSE
163 if( nJumpCount == 3 && bIsValue )
164 { // ELSE path
165 pJumpMat->SetJump( nC, nR, fVal,
166 pJump[ 2 ],
167 pJump[ nJumpCount ]);
169 else
170 { // no parameter given for ELSE,
171 // or DoubleError
172 pJumpMat->SetJump( nC, nR, fVal,
173 pJump[ nJumpCount ],
174 pJump[ nJumpCount ]);
179 xNew = new ScJumpMatrixToken( pJumpMat );
180 GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type(pCur, xNew));
182 PushTempToken( xNew);
183 // set endpoint of path for main code line
184 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
187 break;
188 default:
190 if ( GetBool() )
191 { // TRUE
192 if( nJumpCount >= 2 )
193 { // THEN path
194 aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] );
196 else
197 { // no parameter given for THEN
198 nFuncFmtType = NUMBERFORMAT_LOGICAL;
199 PushInt(1);
200 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
203 else
204 { // FALSE
205 if( nJumpCount == 3 )
206 { // ELSE path
207 aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] );
209 else
210 { // no parameter given for ELSE
211 nFuncFmtType = NUMBERFORMAT_LOGICAL;
212 PushInt(0);
213 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
221 void ScInterpreter::ScChoseJump()
223 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScChoseJump" );
224 // We have to set a jump, if there was none chosen because of an error set
225 // it to endpoint.
226 bool bHaveJump = false;
227 const short* pJump = pCur->GetJump();
228 short nJumpCount = pJump[ 0 ];
229 MatrixDoubleRefToMatrix();
230 switch ( GetStackType() )
232 case svMatrix:
234 ScMatrixRef pMat = PopMatrix();
235 if ( !pMat )
236 PushIllegalParameter();
237 else
239 FormulaTokenRef xNew;
240 ScTokenMatrixMap::const_iterator aMapIter;
241 // DoubleError handled by JumpMatrix
242 pMat->SetErrorInterpreter( NULL);
243 SCSIZE nCols, nRows;
244 pMat->GetDimensions( nCols, nRows );
245 if ( nCols == 0 || nRows == 0 )
246 PushIllegalParameter();
247 else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find(
248 pCur)) != pTokenMatrixMap->end()))
249 xNew = (*aMapIter).second;
250 else
252 ScJumpMatrix* pJumpMat = new ScJumpMatrix( nCols, nRows );
253 for ( SCSIZE nC=0; nC < nCols; ++nC )
255 for ( SCSIZE nR=0; nR < nRows; ++nR )
257 double fVal;
258 ScMatValType nType;
259 const ScMatrixValue* pMatVal = pMat->Get( nC, nR,
260 nType);
261 bool bIsValue = ScMatrix::IsValueType( nType);
262 if ( bIsValue )
264 fVal = pMatVal->fVal;
265 bIsValue = ::rtl::math::isFinite( fVal );
266 if ( bIsValue )
268 fVal = ::rtl::math::approxFloor( fVal);
269 if ( (fVal < 1) || (fVal >= nJumpCount))
271 bIsValue = FALSE;
272 fVal = CreateDoubleError(
273 errIllegalArgument);
277 else
279 fVal = CreateDoubleError( errNoValue);
281 if ( bIsValue )
283 pJumpMat->SetJump( nC, nR, fVal,
284 pJump[ (short)fVal ],
285 pJump[ nJumpCount ]);
287 else
289 pJumpMat->SetJump( nC, nR, fVal,
290 pJump[ nJumpCount ],
291 pJump[ nJumpCount ]);
295 xNew = new ScJumpMatrixToken( pJumpMat );
296 GetTokenMatrixMap().insert( ScTokenMatrixMap::value_type(
297 pCur, xNew));
299 PushTempToken( xNew);
300 // set endpoint of path for main code line
301 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
302 bHaveJump = true;
305 break;
306 default:
308 double nJumpIndex = ::rtl::math::approxFloor( GetDouble() );
309 if (!nGlobalError && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount))
311 aCode.Jump( pJump[ (short) nJumpIndex ], pJump[ nJumpCount ] );
312 bHaveJump = true;
314 else
315 PushIllegalArgument();
318 if (!bHaveJump)
319 aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] );
322 void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, ScMatrixRef& pResMat, SCSIZE nParmCols, SCSIZE nParmRows )
324 SCSIZE nJumpCols, nJumpRows;
325 SCSIZE nResCols, nResRows;
326 SCSIZE nAdjustCols, nAdjustRows;
327 pJumpM->GetDimensions( nJumpCols, nJumpRows );
328 pJumpM->GetResMatDimensions( nResCols, nResRows );
329 if (( nJumpCols == 1 && nParmCols > nResCols ) ||
330 ( nJumpRows == 1 && nParmRows > nResRows ))
332 if ( nJumpCols == 1 && nJumpRows == 1 )
334 nAdjustCols = nParmCols > nResCols ? nParmCols : nResCols;
335 nAdjustRows = nParmRows > nResRows ? nParmRows : nResRows;
337 else if ( nJumpCols == 1 )
339 nAdjustCols = nParmCols;
340 nAdjustRows = nResRows;
342 else
344 nAdjustCols = nResCols;
345 nAdjustRows = nParmRows;
347 pJumpM->SetNewResMat( nAdjustCols, nAdjustRows );
348 pResMat = pJumpM->GetResultMatrix();
352 bool ScInterpreter::JumpMatrix( short nStackLevel )
354 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::JumpMatrix" );
355 pJumpMatrix = static_cast<ScToken*>(pStack[sp-nStackLevel])->GetJumpMatrix();
356 ScMatrixRef pResMat = pJumpMatrix->GetResultMatrix();
357 SCSIZE nC, nR;
358 if ( nStackLevel == 2 )
360 if ( aCode.HasStacked() )
361 aCode.Pop(); // pop what Jump() pushed
362 else
364 DBG_ERRORFILE( "ScInterpreter::JumpMatrix: pop goes the weasel" );
367 if ( !pResMat )
369 Pop();
370 SetError( errUnknownStackVariable );
372 else
374 pJumpMatrix->GetPos( nC, nR );
375 switch ( GetStackType() )
377 case svDouble:
379 double fVal = GetDouble();
380 if ( nGlobalError )
382 fVal = CreateDoubleError( nGlobalError );
383 nGlobalError = 0;
385 pResMat->PutDouble( fVal, nC, nR );
387 break;
388 case svString:
390 const String& rStr = GetString();
391 if ( nGlobalError )
393 pResMat->PutDouble( CreateDoubleError( nGlobalError),
394 nC, nR);
395 nGlobalError = 0;
397 else
398 pResMat->PutString( rStr, nC, nR );
400 break;
401 case svSingleRef:
403 ScAddress aAdr;
404 PopSingleRef( aAdr );
405 if ( nGlobalError )
407 pResMat->PutDouble( CreateDoubleError( nGlobalError),
408 nC, nR);
409 nGlobalError = 0;
411 else
413 ScBaseCell* pCell = GetCell( aAdr );
414 if (HasCellEmptyData( pCell))
415 pResMat->PutEmpty( nC, nR );
416 else if (HasCellValueData( pCell))
418 double fVal = GetCellValue( aAdr, pCell);
419 if ( nGlobalError )
421 fVal = CreateDoubleError(
422 nGlobalError);
423 nGlobalError = 0;
425 pResMat->PutDouble( fVal, nC, nR );
427 else
429 String aStr;
430 GetCellString( aStr, pCell );
431 if ( nGlobalError )
433 pResMat->PutDouble( CreateDoubleError(
434 nGlobalError), nC, nR);
435 nGlobalError = 0;
437 else
438 pResMat->PutString( aStr, nC, nR);
442 break;
443 case svDoubleRef:
444 { // upper left plus offset within matrix
445 double fVal;
446 ScRange aRange;
447 PopDoubleRef( aRange );
448 if ( nGlobalError )
450 fVal = CreateDoubleError( nGlobalError );
451 nGlobalError = 0;
452 pResMat->PutDouble( fVal, nC, nR );
454 else
456 // Do not modify the original range because we use it
457 // to adjust the size of the result matrix if necessary.
458 ScAddress aAdr( aRange.aStart);
459 ULONG nCol = (ULONG)aAdr.Col() + nC;
460 ULONG nRow = (ULONG)aAdr.Row() + nR;
461 if ((nCol > static_cast<ULONG>(aRange.aEnd.Col()) &&
462 aRange.aEnd.Col() != aRange.aStart.Col())
463 || (nRow > static_cast<ULONG>(aRange.aEnd.Row()) &&
464 aRange.aEnd.Row() != aRange.aStart.Row()))
466 fVal = CreateDoubleError( NOTAVAILABLE );
467 pResMat->PutDouble( fVal, nC, nR );
469 else
471 // Replicate column and/or row of a vector if it is
472 // one. Note that this could be a range reference
473 // that in fact consists of only one cell, e.g. A1:A1
474 if (aRange.aEnd.Col() == aRange.aStart.Col())
475 nCol = aRange.aStart.Col();
476 if (aRange.aEnd.Row() == aRange.aStart.Row())
477 nRow = aRange.aStart.Row();
478 aAdr.SetCol( static_cast<SCCOL>(nCol) );
479 aAdr.SetRow( static_cast<SCROW>(nRow) );
480 ScBaseCell* pCell = GetCell( aAdr );
481 if (HasCellEmptyData( pCell))
482 pResMat->PutEmpty( nC, nR );
483 else if (HasCellValueData( pCell))
485 double fCellVal = GetCellValue( aAdr, pCell);
486 if ( nGlobalError )
488 fCellVal = CreateDoubleError(
489 nGlobalError);
490 nGlobalError = 0;
492 pResMat->PutDouble( fCellVal, nC, nR );
494 else
496 String aStr;
497 GetCellString( aStr, pCell );
498 if ( nGlobalError )
500 pResMat->PutDouble( CreateDoubleError(
501 nGlobalError), nC, nR);
502 nGlobalError = 0;
504 else
505 pResMat->PutString( aStr, nC, nR );
508 SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1;
509 SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1;
510 lcl_AdjustJumpMatrix( pJumpMatrix, pResMat, nParmCols, nParmRows );
513 break;
514 case svMatrix:
515 { // match matrix offsets
516 double fVal;
517 ScMatrixRef pMat = PopMatrix();
518 if ( nGlobalError )
520 fVal = CreateDoubleError( nGlobalError );
521 nGlobalError = 0;
522 pResMat->PutDouble( fVal, nC, nR );
524 else if ( !pMat )
526 fVal = CreateDoubleError( errUnknownVariable );
527 pResMat->PutDouble( fVal, nC, nR );
529 else
531 SCSIZE nCols, nRows;
532 pMat->GetDimensions( nCols, nRows );
533 if ((nCols <= nC && nCols != 1) ||
534 (nRows <= nR && nRows != 1))
536 fVal = CreateDoubleError( NOTAVAILABLE );
537 pResMat->PutDouble( fVal, nC, nR );
539 else
541 if ( pMat->IsValue( nC, nR ) )
543 fVal = pMat->GetDouble( nC, nR );
544 pResMat->PutDouble( fVal, nC, nR );
546 else if ( pMat->IsEmpty( nC, nR ) )
547 pResMat->PutEmpty( nC, nR );
548 else
550 const String& rStr = pMat->GetString( nC, nR );
551 pResMat->PutString( rStr, nC, nR );
554 lcl_AdjustJumpMatrix( pJumpMatrix, pResMat, nCols, nRows );
557 break;
558 case svError:
560 PopError();
561 double fVal = CreateDoubleError( nGlobalError);
562 nGlobalError = 0;
563 pResMat->PutDouble( fVal, nC, nR );
565 break;
566 default:
568 Pop();
569 double fVal = CreateDoubleError( errIllegalArgument);
570 pResMat->PutDouble( fVal, nC, nR );
575 bool bCont = pJumpMatrix->Next( nC, nR );
576 if ( bCont )
578 double fBool;
579 short nStart, nNext, nStop;
580 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
581 while ( bCont && nStart == nNext )
582 { // push all results that have no jump path
583 if ( pResMat )
585 // a FALSE without path results in an empty path value
586 if ( fBool == 0.0 )
587 pResMat->PutEmptyPath( nC, nR );
588 else
589 pResMat->PutDouble( fBool, nC, nR );
591 bCont = pJumpMatrix->Next( nC, nR );
592 if ( bCont )
593 pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop );
595 if ( bCont && nStart != nNext )
597 const ScTokenVec* pParams = pJumpMatrix->GetJumpParameters();
598 if ( pParams )
600 for ( ScTokenVec::const_iterator i = pParams->begin();
601 i != pParams->end(); ++i )
603 // This is not the current state of the interpreter, so
604 // push without error, and elements' errors are coded into
605 // double.
606 PushWithoutError( *(*i));
609 aCode.Jump( nStart, nNext, nStop );
612 if ( !bCont )
613 { // we're done with it, throw away jump matrix, keep result
614 pJumpMatrix = NULL;
615 Pop();
616 PushMatrix( pResMat );
617 // Remove jump matrix from map and remember result matrix in case it
618 // could be reused in another path of the same condition.
619 if (pTokenMatrixMap)
621 pTokenMatrixMap->erase( pCur);
622 pTokenMatrixMap->insert( ScTokenMatrixMap::value_type( pCur,
623 pStack[sp-1]));
625 return true;
627 return false;
631 ScCompareOptions::ScCompareOptions( ScDocument* pDoc, const ScQueryEntry& rEntry, bool bReg ) :
632 aQueryEntry(rEntry),
633 bRegEx(bReg),
634 bMatchWholeCell(pDoc->GetDocOptions().IsMatchWholeCell()),
635 bIgnoreCase(true)
637 bRegEx = (bRegEx && (aQueryEntry.eOp == SC_EQUAL || aQueryEntry.eOp == SC_NOT_EQUAL));
638 // Interpreter functions usually are case insensitive, except the simple
639 // comparison operators, for which these options aren't used. Override in
640 // struct if needed.
644 double ScInterpreter::CompareFunc( const ScCompare& rComp, ScCompareOptions* pOptions )
646 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::CompareFunc" );
647 // Keep DoubleError if encountered
648 // #i40539# if bEmpty is set, bVal/nVal are uninitialized
649 if ( !rComp.bEmpty[0] && rComp.bVal[0] && !::rtl::math::isFinite( rComp.nVal[0]))
650 return rComp.nVal[0];
651 if ( !rComp.bEmpty[1] && rComp.bVal[1] && !::rtl::math::isFinite( rComp.nVal[1]))
652 return rComp.nVal[1];
654 double fRes = 0;
655 if ( rComp.bEmpty[ 0 ] )
657 if ( rComp.bEmpty[ 1 ] )
658 ; // empty cell == empty cell, fRes 0
659 else if( rComp.bVal[ 1 ] )
661 if ( !::rtl::math::approxEqual( rComp.nVal[ 1 ], 0.0 ) )
663 if ( rComp.nVal[ 1 ] < 0.0 )
664 fRes = 1; // empty cell > -x
665 else
666 fRes = -1; // empty cell < x
668 // else: empty cell == 0.0
670 else
672 if ( rComp.pVal[ 1 ]->Len() )
673 fRes = -1; // empty cell < "..."
674 // else: empty cell == ""
677 else if ( rComp.bEmpty[ 1 ] )
679 if( rComp.bVal[ 0 ] )
681 if ( !::rtl::math::approxEqual( rComp.nVal[ 0 ], 0.0 ) )
683 if ( rComp.nVal[ 0 ] < 0.0 )
684 fRes = -1; // -x < empty cell
685 else
686 fRes = 1; // x > empty cell
688 // else: empty cell == 0.0
690 else
692 if ( rComp.pVal[ 0 ]->Len() )
693 fRes = 1; // "..." > empty cell
694 // else: "" == empty cell
697 else if( rComp.bVal[ 0 ] )
699 if( rComp.bVal[ 1 ] )
701 if ( !::rtl::math::approxEqual( rComp.nVal[ 0 ], rComp.nVal[ 1 ] ) )
703 if( rComp.nVal[ 0 ] - rComp.nVal[ 1 ] < 0 )
704 fRes = -1;
705 else
706 fRes = 1;
709 else
710 fRes = -1; // number is less than string
712 else if( rComp.bVal[ 1 ] )
713 fRes = 1; // number is less than string
714 else
716 // Both strings.
717 if (pOptions)
719 // All similar to Sctable::ValidQuery(), *rComp.pVal[1] actually
720 // is/must be identical to *rEntry.pStr, which is essential for
721 // regex to work through GetSearchTextPtr().
722 ScQueryEntry& rEntry = pOptions->aQueryEntry;
723 DBG_ASSERT( *rComp.pVal[1] == *rEntry.pStr, "ScInterpreter::CompareFunc: broken options");
724 if (pOptions->bRegEx)
726 xub_StrLen nStart = 0;
727 xub_StrLen nStop = rComp.pVal[0]->Len();
728 bool bMatch = rEntry.GetSearchTextPtr(
729 !pOptions->bIgnoreCase)->SearchFrwrd( *rComp.pVal[0],
730 &nStart, &nStop);
731 if (bMatch && pOptions->bMatchWholeCell && (nStart != 0 || nStop != rComp.pVal[0]->Len()))
732 bMatch = false; // RegEx must match entire string.
733 fRes = (bMatch ? 0 : 1);
735 else if (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL)
737 ::utl::TransliterationWrapper* pTransliteration =
738 (pOptions->bIgnoreCase ? ScGlobal::pTransliteration :
739 ScGlobal::pCaseTransliteration);
740 bool bMatch;
741 if (pOptions->bMatchWholeCell)
742 bMatch = pTransliteration->isEqual( *rComp.pVal[0], *rComp.pVal[1]);
743 else
745 String aCell( pTransliteration->transliterate(
746 *rComp.pVal[0], ScGlobal::eLnge, 0,
747 rComp.pVal[0]->Len(), NULL));
748 String aQuer( pTransliteration->transliterate(
749 *rComp.pVal[1], ScGlobal::eLnge, 0,
750 rComp.pVal[1]->Len(), NULL));
751 bMatch = (aCell.Search( aQuer ) != STRING_NOTFOUND);
753 fRes = (bMatch ? 0 : 1);
755 else if (pOptions->bIgnoreCase)
756 fRes = (double) ScGlobal::pCollator->compareString(
757 *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] );
758 else
759 fRes = (double) ScGlobal::pCaseCollator->compareString(
760 *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] );
762 else if (pDok->GetDocOptions().IsIgnoreCase())
763 fRes = (double) ScGlobal::pCollator->compareString(
764 *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] );
765 else
766 fRes = (double) ScGlobal::pCaseCollator->compareString(
767 *rComp.pVal[ 0 ], *rComp.pVal[ 1 ] );
769 return fRes;
773 double ScInterpreter::Compare()
775 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::Compare" );
776 String aVal1, aVal2;
777 ScCompare aComp( &aVal1, &aVal2 );
778 for( short i = 1; i >= 0; i-- )
780 switch ( GetRawStackType() )
782 case svEmptyCell:
783 aComp.bEmpty[ i ] = TRUE;
784 break;
785 case svMissing:
786 case svDouble:
787 aComp.nVal[ i ] = GetDouble();
788 aComp.bVal[ i ] = TRUE;
789 break;
790 case svString:
791 *aComp.pVal[ i ] = GetString();
792 aComp.bVal[ i ] = FALSE;
793 break;
794 case svDoubleRef :
795 case svSingleRef :
797 ScAddress aAdr;
798 if ( !PopDoubleRefOrSingleRef( aAdr ) )
799 break;
800 ScBaseCell* pCell = GetCell( aAdr );
801 if (HasCellEmptyData( pCell))
802 aComp.bEmpty[ i ] = TRUE;
803 else if (HasCellStringData( pCell))
805 GetCellString( *aComp.pVal[ i ], pCell);
806 aComp.bVal[ i ] = FALSE;
808 else
810 aComp.nVal[ i ] = GetCellValue( aAdr, pCell );
811 aComp.bVal[ i ] = TRUE;
814 break;
815 default:
816 SetError( errIllegalParameter);
817 break;
820 if( nGlobalError )
821 return 0;
822 nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL;
823 return CompareFunc( aComp );
827 ScMatrixRef ScInterpreter::CompareMat( ScCompareOptions* pOptions )
829 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::CompareMat" );
830 String aVal1, aVal2;
831 ScCompare aComp( &aVal1, &aVal2 );
832 ScMatrixRef pMat[2];
833 ScAddress aAdr;
834 for( short i = 1; i >= 0; i-- )
836 switch (GetRawStackType())
838 case svEmptyCell:
839 aComp.bEmpty[ i ] = TRUE;
840 break;
841 case svMissing:
842 case svDouble:
843 aComp.nVal[ i ] = GetDouble();
844 aComp.bVal[ i ] = TRUE;
845 break;
846 case svString:
847 *aComp.pVal[ i ] = GetString();
848 aComp.bVal[ i ] = FALSE;
849 break;
850 case svSingleRef:
852 PopSingleRef( aAdr );
853 ScBaseCell* pCell = GetCell( aAdr );
854 if (HasCellEmptyData( pCell))
855 aComp.bEmpty[ i ] = TRUE;
856 else if (HasCellStringData( pCell))
858 GetCellString( *aComp.pVal[ i ], pCell);
859 aComp.bVal[ i ] = FALSE;
861 else
863 aComp.nVal[ i ] = GetCellValue( aAdr, pCell );
864 aComp.bVal[ i ] = TRUE;
867 break;
868 case svDoubleRef:
869 case svMatrix:
870 pMat[ i ] = GetMatrix();
871 if ( !pMat[ i ] )
872 SetError( errIllegalParameter);
873 else
874 pMat[i]->SetErrorInterpreter( NULL);
875 // errors are transported as DoubleError inside matrix
876 break;
877 default:
878 SetError( errIllegalParameter);
879 break;
882 ScMatrixRef pResMat = NULL;
883 if( !nGlobalError )
885 if ( pMat[0] && pMat[1] )
887 SCSIZE nC0, nC1;
888 SCSIZE nR0, nR1;
889 pMat[0]->GetDimensions( nC0, nR0 );
890 pMat[1]->GetDimensions( nC1, nR1 );
891 SCSIZE nC = Max( nC0, nC1 );
892 SCSIZE nR = Max( nR0, nR1 );
893 pResMat = GetNewMat( nC, nR);
894 if ( !pResMat )
895 return NULL;
896 for ( SCSIZE j=0; j<nC; j++ )
898 for ( SCSIZE k=0; k<nR; k++ )
900 SCSIZE nCol = j, nRow = k;
901 if ( pMat[0]->ValidColRowOrReplicated( nCol, nRow ) &&
902 pMat[1]->ValidColRowOrReplicated( nCol, nRow ))
904 for ( short i=1; i>=0; i-- )
906 if ( pMat[i]->IsString(j,k) )
908 aComp.bVal[i] = FALSE;
909 *aComp.pVal[i] = pMat[i]->GetString(j,k);
910 aComp.bEmpty[i] = pMat[i]->IsEmpty(j,k);
912 else
914 aComp.bVal[i] = TRUE;
915 aComp.nVal[i] = pMat[i]->GetDouble(j,k);
916 aComp.bEmpty[i] = FALSE;
919 pResMat->PutDouble( CompareFunc( aComp, pOptions ), j,k );
921 else
922 pResMat->PutString( ScGlobal::GetRscString(STR_NO_VALUE), j,k );
926 else if ( pMat[0] || pMat[1] )
928 short i = ( pMat[0] ? 0 : 1);
929 SCSIZE nC, nR;
930 pMat[i]->GetDimensions( nC, nR );
931 pResMat = GetNewMat( nC, nR);
932 if ( !pResMat )
933 return NULL;
934 SCSIZE n = nC * nR;
935 for ( SCSIZE j=0; j<n; j++ )
937 if ( pMat[i]->IsValue(j) )
939 aComp.bVal[i] = TRUE;
940 aComp.nVal[i] = pMat[i]->GetDouble(j);
941 aComp.bEmpty[i] = FALSE;
943 else
945 aComp.bVal[i] = FALSE;
946 *aComp.pVal[i] = pMat[i]->GetString(j);
947 aComp.bEmpty[i] = pMat[i]->IsEmpty(j);
949 pResMat->PutDouble( CompareFunc( aComp, pOptions ), j );
953 nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL;
954 return pResMat;
958 ScMatrixRef ScInterpreter::QueryMat( ScMatrix* pMat, ScCompareOptions& rOptions )
960 short nSaveCurFmtType = nCurFmtType;
961 short nSaveFuncFmtType = nFuncFmtType;
962 PushMatrix( pMat);
963 if (rOptions.aQueryEntry.bQueryByString)
964 PushString( *rOptions.aQueryEntry.pStr);
965 else
966 PushDouble( rOptions.aQueryEntry.nVal);
967 ScMatrixRef pResultMatrix = CompareMat( &rOptions);
968 nCurFmtType = nSaveCurFmtType;
969 nFuncFmtType = nSaveFuncFmtType;
970 if (nGlobalError || !pResultMatrix)
972 SetError( errIllegalParameter);
973 return pResultMatrix;
976 switch (rOptions.aQueryEntry.eOp)
978 case SC_EQUAL:
979 pResultMatrix->CompareEqual();
980 break;
981 case SC_LESS:
982 pResultMatrix->CompareLess();
983 break;
984 case SC_GREATER:
985 pResultMatrix->CompareGreater();
986 break;
987 case SC_LESS_EQUAL:
988 pResultMatrix->CompareLessEqual();
989 break;
990 case SC_GREATER_EQUAL:
991 pResultMatrix->CompareGreaterEqual();
992 break;
993 case SC_NOT_EQUAL:
994 pResultMatrix->CompareNotEqual();
995 break;
996 default:
997 SetError( errIllegalArgument);
998 DBG_ERROR1( "ScInterpreter::QueryMat: unhandled comparison operator: %d", (int)rOptions.aQueryEntry.eOp);
1000 return pResultMatrix;
1004 void ScInterpreter::ScEqual()
1006 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScEqual" );
1007 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1009 ScMatrixRef pMat = CompareMat();
1010 if ( !pMat )
1011 PushIllegalParameter();
1012 else
1014 pMat->CompareEqual();
1015 PushMatrix( pMat );
1018 else
1019 PushInt( Compare() == 0 );
1023 void ScInterpreter::ScNotEqual()
1025 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScNotEqual" );
1026 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1028 ScMatrixRef pMat = CompareMat();
1029 if ( !pMat )
1030 PushIllegalParameter();
1031 else
1033 pMat->CompareNotEqual();
1034 PushMatrix( pMat );
1037 else
1038 PushInt( Compare() != 0 );
1042 void ScInterpreter::ScLess()
1044 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLess" );
1045 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1047 ScMatrixRef pMat = CompareMat();
1048 if ( !pMat )
1049 PushIllegalParameter();
1050 else
1052 pMat->CompareLess();
1053 PushMatrix( pMat );
1056 else
1057 PushInt( Compare() < 0 );
1061 void ScInterpreter::ScGreater()
1063 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGreater" );
1064 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1066 ScMatrixRef pMat = CompareMat();
1067 if ( !pMat )
1068 PushIllegalParameter();
1069 else
1071 pMat->CompareGreater();
1072 PushMatrix( pMat );
1075 else
1076 PushInt( Compare() > 0 );
1080 void ScInterpreter::ScLessEqual()
1082 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLessEqual" );
1083 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1085 ScMatrixRef pMat = CompareMat();
1086 if ( !pMat )
1087 PushIllegalParameter();
1088 else
1090 pMat->CompareLessEqual();
1091 PushMatrix( pMat );
1094 else
1095 PushInt( Compare() <= 0 );
1099 void ScInterpreter::ScGreaterEqual()
1101 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScGreaterEqual" );
1102 if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix )
1104 ScMatrixRef pMat = CompareMat();
1105 if ( !pMat )
1106 PushIllegalParameter();
1107 else
1109 pMat->CompareGreaterEqual();
1110 PushMatrix( pMat );
1113 else
1114 PushInt( Compare() >= 0 );
1118 void ScInterpreter::ScAnd()
1120 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScAnd" );
1121 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1122 short nParamCount = GetByte();
1123 if ( MustHaveParamCountMin( nParamCount, 1 ) )
1125 BOOL bHaveValue = FALSE;
1126 short nRes = TRUE;
1127 size_t nRefInList = 0;
1128 while( nParamCount-- > 0)
1130 if ( !nGlobalError )
1132 switch ( GetStackType() )
1134 case svDouble :
1135 bHaveValue = TRUE;
1136 nRes &= ( PopDouble() != 0.0 );
1137 break;
1138 case svString :
1139 Pop();
1140 SetError( errNoValue );
1141 break;
1142 case svSingleRef :
1144 ScAddress aAdr;
1145 PopSingleRef( aAdr );
1146 if ( !nGlobalError )
1148 ScBaseCell* pCell = GetCell( aAdr );
1149 if ( HasCellValueData( pCell ) )
1151 bHaveValue = TRUE;
1152 nRes &= ( GetCellValue( aAdr, pCell ) != 0.0 );
1154 // else: Xcl setzt hier keinen Fehler
1157 break;
1158 case svDoubleRef:
1159 case svRefList:
1161 ScRange aRange;
1162 PopDoubleRef( aRange, nParamCount, nRefInList);
1163 if ( !nGlobalError )
1165 double fVal;
1166 USHORT nErr = 0;
1167 ScValueIterator aValIter( pDok, aRange );
1168 if ( aValIter.GetFirst( fVal, nErr ) )
1170 bHaveValue = TRUE;
1173 nRes &= ( fVal != 0.0 );
1174 } while ( (nErr == 0) &&
1175 aValIter.GetNext( fVal, nErr ) );
1177 SetError( nErr );
1180 break;
1181 case svMatrix:
1183 ScMatrixRef pMat = GetMatrix();
1184 if ( pMat )
1186 bHaveValue = TRUE;
1187 double fVal = pMat->And();
1188 USHORT nErr = GetDoubleErrorValue( fVal );
1189 if ( nErr )
1191 SetError( nErr );
1192 nRes = FALSE;
1194 else
1195 nRes &= (fVal != 0.0);
1197 // else: GetMatrix did set errIllegalParameter
1199 break;
1200 default:
1201 Pop();
1202 SetError( errIllegalParameter);
1205 else
1206 Pop();
1208 if ( bHaveValue )
1209 PushInt( nRes );
1210 else
1211 PushNoValue();
1216 void ScInterpreter::ScOr()
1218 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScOr" );
1219 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1220 short nParamCount = GetByte();
1221 if ( MustHaveParamCountMin( nParamCount, 1 ) )
1223 BOOL bHaveValue = FALSE;
1224 short nRes = FALSE;
1225 size_t nRefInList = 0;
1226 while( nParamCount-- > 0)
1228 if ( !nGlobalError )
1230 switch ( GetStackType() )
1232 case svDouble :
1233 bHaveValue = TRUE;
1234 nRes |= ( PopDouble() != 0.0 );
1235 break;
1236 case svString :
1237 Pop();
1238 SetError( errNoValue );
1239 break;
1240 case svSingleRef :
1242 ScAddress aAdr;
1243 PopSingleRef( aAdr );
1244 if ( !nGlobalError )
1246 ScBaseCell* pCell = GetCell( aAdr );
1247 if ( HasCellValueData( pCell ) )
1249 bHaveValue = TRUE;
1250 nRes |= ( GetCellValue( aAdr, pCell ) != 0.0 );
1252 // else: Xcl setzt hier keinen Fehler
1255 break;
1256 case svDoubleRef:
1257 case svRefList:
1259 ScRange aRange;
1260 PopDoubleRef( aRange, nParamCount, nRefInList);
1261 if ( !nGlobalError )
1263 double fVal;
1264 USHORT nErr = 0;
1265 ScValueIterator aValIter( pDok, aRange );
1266 if ( aValIter.GetFirst( fVal, nErr ) )
1268 bHaveValue = TRUE;
1271 nRes |= ( fVal != 0.0 );
1272 } while ( (nErr == 0) &&
1273 aValIter.GetNext( fVal, nErr ) );
1275 SetError( nErr );
1278 break;
1279 case svMatrix:
1281 bHaveValue = TRUE;
1282 ScMatrixRef pMat = GetMatrix();
1283 if ( pMat )
1285 bHaveValue = TRUE;
1286 double fVal = pMat->Or();
1287 USHORT nErr = GetDoubleErrorValue( fVal );
1288 if ( nErr )
1290 SetError( nErr );
1291 nRes = FALSE;
1293 else
1294 nRes |= (fVal != 0.0);
1296 // else: GetMatrix did set errIllegalParameter
1298 break;
1299 default:
1300 Pop();
1301 SetError( errIllegalParameter);
1304 else
1305 Pop();
1307 if ( bHaveValue )
1308 PushInt( nRes );
1309 else
1310 PushNoValue();
1315 void ScInterpreter::ScNeg()
1317 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScNeg" );
1318 // Simple negation doesn't change current format type to number, keep
1319 // current type.
1320 nFuncFmtType = nCurFmtType;
1321 switch ( GetStackType() )
1323 case svMatrix :
1325 ScMatrixRef pMat = GetMatrix();
1326 if ( !pMat )
1327 PushIllegalParameter();
1328 else
1330 SCSIZE nC, nR;
1331 pMat->GetDimensions( nC, nR );
1332 ScMatrixRef pResMat = GetNewMat( nC, nR);
1333 if ( !pResMat )
1334 PushIllegalArgument();
1335 else
1337 SCSIZE nCount = nC * nR;
1338 for ( SCSIZE j=0; j<nCount; ++j )
1340 if ( pMat->IsValueOrEmpty(j) )
1341 pResMat->PutDouble( -pMat->GetDouble(j), j );
1342 else
1343 pResMat->PutString(
1344 ScGlobal::GetRscString( STR_NO_VALUE ), j );
1346 PushMatrix( pResMat );
1350 break;
1351 default:
1352 PushDouble( -GetDouble() );
1357 void ScInterpreter::ScPercentSign()
1359 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScPercentSign" );
1360 nFuncFmtType = NUMBERFORMAT_PERCENT;
1361 const FormulaToken* pSaveCur = pCur;
1362 BYTE nSavePar = cPar;
1363 PushInt( 100 );
1364 cPar = 2;
1365 FormulaByteToken aDivOp( ocDiv, cPar );
1366 pCur = &aDivOp;
1367 ScDiv();
1368 pCur = pSaveCur;
1369 cPar = nSavePar;
1373 void ScInterpreter::ScNot()
1375 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScNot" );
1376 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1377 switch ( GetStackType() )
1379 case svMatrix :
1381 ScMatrixRef pMat = GetMatrix();
1382 if ( !pMat )
1383 PushIllegalParameter();
1384 else
1386 SCSIZE nC, nR;
1387 pMat->GetDimensions( nC, nR );
1388 ScMatrixRef pResMat = GetNewMat( nC, nR);
1389 if ( !pResMat )
1390 PushIllegalArgument();
1391 else
1393 SCSIZE nCount = nC * nR;
1394 for ( SCSIZE j=0; j<nCount; ++j )
1396 if ( pMat->IsValueOrEmpty(j) )
1397 pResMat->PutDouble( (pMat->GetDouble(j) == 0.0), j );
1398 else
1399 pResMat->PutString(
1400 ScGlobal::GetRscString( STR_NO_VALUE ), j );
1402 PushMatrix( pResMat );
1406 break;
1407 default:
1408 PushInt( GetDouble() == 0.0 );
1413 void ScInterpreter::ScPi()
1415 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScPi" );
1416 PushDouble(F_PI);
1420 void ScInterpreter::ScRandom()
1422 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRandom" );
1423 PushDouble((double)rand() / ((double)RAND_MAX+1.0));
1427 void ScInterpreter::ScTrue()
1429 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScTrue" );
1430 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1431 PushInt(1);
1435 void ScInterpreter::ScFalse()
1437 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScFalse" );
1438 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1439 PushInt(0);
1443 void ScInterpreter::ScDeg()
1445 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDeg" );
1446 PushDouble((GetDouble() / F_PI) * 180.0);
1450 void ScInterpreter::ScRad()
1452 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRad" );
1453 PushDouble(GetDouble() * (F_PI / 180));
1457 void ScInterpreter::ScSin()
1459 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSin" );
1460 PushDouble(::rtl::math::sin(GetDouble()));
1464 void ScInterpreter::ScCos()
1466 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCos" );
1467 PushDouble(::rtl::math::cos(GetDouble()));
1471 void ScInterpreter::ScTan()
1473 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScTan" );
1474 PushDouble(::rtl::math::tan(GetDouble()));
1478 void ScInterpreter::ScCot()
1480 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCot" );
1481 PushDouble(1.0 / ::rtl::math::tan(GetDouble()));
1485 void ScInterpreter::ScArcSin()
1487 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcSin" );
1488 PushDouble(asin(GetDouble()));
1492 void ScInterpreter::ScArcCos()
1494 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcCos" );
1495 PushDouble(acos(GetDouble()));
1499 void ScInterpreter::ScArcTan()
1501 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcTan" );
1502 PushDouble(atan(GetDouble()));
1506 void ScInterpreter::ScArcCot()
1508 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcCot" );
1509 PushDouble((F_PI2) - atan(GetDouble()));
1513 void ScInterpreter::ScSinHyp()
1515 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSinHyp" );
1516 PushDouble(sinh(GetDouble()));
1520 void ScInterpreter::ScCosHyp()
1522 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCosHyp" );
1523 PushDouble(cosh(GetDouble()));
1527 void ScInterpreter::ScTanHyp()
1529 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScTanHyp" );
1530 PushDouble(tanh(GetDouble()));
1534 void ScInterpreter::ScCotHyp()
1536 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCotHyp" );
1537 PushDouble(1.0 / tanh(GetDouble()));
1541 void ScInterpreter::ScArcSinHyp()
1543 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcSinHyp" );
1544 double nVal = GetDouble();
1545 PushDouble(log(nVal + sqrt((nVal * nVal) + 1.0)));
1549 void ScInterpreter::ScArcCosHyp()
1551 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcCosHyp" );
1552 double nVal = GetDouble();
1553 if (nVal < 1.0)
1554 PushIllegalArgument();
1555 else
1556 PushDouble(log(nVal + sqrt((nVal * nVal) - 1.0)));
1560 void ScInterpreter::ScArcTanHyp()
1562 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcTanHyp" );
1563 double fVal = GetDouble();
1564 if (fabs(fVal) >= 1.0)
1565 PushIllegalArgument();
1566 else
1567 PushDouble( ::rtl::math::atanh( fVal));
1571 void ScInterpreter::ScArcCotHyp()
1573 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScArcCotHyp" );
1574 double nVal = GetDouble();
1575 if (fabs(nVal) <= 1.0)
1576 PushIllegalArgument();
1577 else
1578 PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0)));
1582 void ScInterpreter::ScExp()
1584 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScExp" );
1585 PushDouble(exp(GetDouble()));
1589 void ScInterpreter::ScSqrt()
1591 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSqrt" );
1592 double fVal = GetDouble();
1593 if (fVal >= 0.0)
1594 PushDouble(sqrt(fVal));
1595 else
1596 PushIllegalArgument();
1600 void ScInterpreter::ScIsEmpty()
1602 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsEmpty" );
1603 short nRes = 0;
1604 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1605 switch ( GetRawStackType() )
1607 case svEmptyCell:
1609 FormulaTokenRef p = PopToken();
1610 if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited())
1611 nRes = 1;
1613 break;
1614 case svDoubleRef :
1615 case svSingleRef :
1617 ScAddress aAdr;
1618 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1619 break;
1620 // NOTE: this could test also on inherited emptiness, but then the
1621 // cell tested wouldn't be empty. Must correspond with
1622 // ScCountEmptyCells().
1623 // if (HasCellEmptyData( GetCell( aAdr)))
1624 CellType eCellType = GetCellType( GetCell( aAdr ) );
1625 if((eCellType == CELLTYPE_NONE) || (eCellType == CELLTYPE_NOTE))
1626 nRes = 1;
1628 break;
1629 case svMatrix:
1631 ScMatrixRef pMat = PopMatrix();
1632 if ( !pMat )
1633 ; // nothing
1634 else if ( !pJumpMatrix )
1635 nRes = pMat->IsEmpty( 0 );
1636 else
1638 SCSIZE nCols, nRows, nC, nR;
1639 pMat->GetDimensions( nCols, nRows);
1640 pJumpMatrix->GetPos( nC, nR);
1641 if ( nC < nCols && nR < nRows )
1642 nRes = pMat->IsEmpty( nC, nR);
1643 // else: FALSE, not empty (which is what Xcl does)
1646 break;
1647 default:
1648 Pop();
1650 nGlobalError = 0;
1651 PushInt( nRes );
1655 short ScInterpreter::IsString()
1657 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::IsString" );
1658 nFuncFmtType = NUMBERFORMAT_LOGICAL;
1659 short nRes = 0;
1660 switch ( GetRawStackType() )
1662 case svString:
1663 Pop();
1664 nRes = 1;
1665 break;
1666 case svDoubleRef :
1667 case svSingleRef :
1669 ScAddress aAdr;
1670 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1671 break;
1672 ScBaseCell* pCell = GetCell( aAdr );
1673 if (GetCellErrCode( pCell ) == 0)
1675 switch ( GetCellType( pCell ) )
1677 case CELLTYPE_STRING :
1678 case CELLTYPE_EDIT :
1679 nRes = 1;
1680 break;
1681 case CELLTYPE_FORMULA :
1682 nRes = !((ScFormulaCell*)pCell)->IsValue() &&
1683 !((ScFormulaCell*)pCell)->IsEmpty();
1684 break;
1685 default:
1686 ; // nothing
1690 break;
1691 case svMatrix:
1693 ScMatrixRef pMat = PopMatrix();
1694 if ( !pMat )
1695 ; // nothing
1696 else if ( !pJumpMatrix )
1697 nRes = pMat->IsString(0) && !pMat->IsEmpty(0);
1698 else
1700 SCSIZE nCols, nRows, nC, nR;
1701 pMat->GetDimensions( nCols, nRows);
1702 pJumpMatrix->GetPos( nC, nR);
1703 if ( nC < nCols && nR < nRows )
1704 nRes = pMat->IsString( nC, nR) && !pMat->IsEmpty( nC, nR);
1707 break;
1708 default:
1709 Pop();
1711 nGlobalError = 0;
1712 return nRes;
1716 void ScInterpreter::ScIsString()
1718 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsString" );
1719 PushInt( IsString() );
1723 void ScInterpreter::ScIsNonString()
1725 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsNonString" );
1726 PushInt( !IsString() );
1730 void ScInterpreter::ScIsLogical()
1732 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsLogical" );
1733 short nRes = 0;
1734 switch ( GetStackType() )
1736 case svDoubleRef :
1737 case svSingleRef :
1739 ScAddress aAdr;
1740 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1741 break;
1742 ScBaseCell* pCell = GetCell( aAdr );
1743 if (GetCellErrCode( pCell ) == 0)
1745 if (HasCellValueData(pCell))
1747 ULONG nFormat = GetCellNumberFormat( aAdr, pCell );
1748 nRes = ( pFormatter->GetType(nFormat)
1749 == NUMBERFORMAT_LOGICAL);
1753 break;
1754 case svMatrix:
1755 // TODO: we don't have type information for arrays except
1756 // numerical/string.
1757 // Fall thru
1758 default:
1759 PopError();
1760 if ( !nGlobalError )
1761 nRes = ( nCurFmtType == NUMBERFORMAT_LOGICAL );
1763 nCurFmtType = nFuncFmtType = NUMBERFORMAT_LOGICAL;
1764 nGlobalError = 0;
1765 PushInt( nRes );
1769 void ScInterpreter::ScType()
1771 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScType" );
1772 short nType = 0;
1773 switch ( GetStackType() )
1775 case svDoubleRef :
1776 case svSingleRef :
1778 ScAddress aAdr;
1779 if ( !PopDoubleRefOrSingleRef( aAdr ) )
1780 break;
1781 ScBaseCell* pCell = GetCell( aAdr );
1782 if (GetCellErrCode( pCell ) == 0)
1784 switch ( GetCellType( pCell ) )
1786 // NOTE: this is Xcl nonsense!
1787 case CELLTYPE_NOTE :
1788 nType = 1; // empty cell is value (0)
1789 break;
1790 case CELLTYPE_STRING :
1791 case CELLTYPE_EDIT :
1792 nType = 2;
1793 break;
1794 case CELLTYPE_VALUE :
1796 ULONG nFormat = GetCellNumberFormat( aAdr, pCell );
1797 if (pFormatter->GetType(nFormat)
1798 == NUMBERFORMAT_LOGICAL)
1799 nType = 4;
1800 else
1801 nType = 1;
1803 break;
1804 case CELLTYPE_FORMULA :
1805 nType = 8;
1806 break;
1807 default:
1808 PushIllegalArgument();
1811 else
1812 nType = 16;
1814 break;
1815 case svString:
1816 PopError();
1817 if ( nGlobalError )
1819 nType = 16;
1820 nGlobalError = 0;
1822 else
1823 nType = 2;
1824 break;
1825 case svMatrix:
1826 PopMatrix();
1827 if ( nGlobalError )
1829 nType = 16;
1830 nGlobalError = 0;
1832 else
1833 nType = 64;
1834 // we could return the type of one element if in JumpMatrix or
1835 // ForceArray mode, but Xcl doesn't ...
1836 break;
1837 default:
1838 PopError();
1839 if ( nGlobalError )
1841 nType = 16;
1842 nGlobalError = 0;
1844 else
1845 nType = 1;
1847 PushInt( nType );
1851 inline BOOL lcl_FormatHasNegColor( const SvNumberformat* pFormat )
1853 return pFormat && pFormat->GetColor( 1 );
1857 inline BOOL lcl_FormatHasOpenPar( const SvNumberformat* pFormat )
1859 return pFormat && (pFormat->GetFormatstring().Search( '(' ) != STRING_NOTFOUND);
1863 void ScInterpreter::ScCell()
1864 { // ATTRIBUTE ; [REF]
1865 BYTE nParamCount = GetByte();
1866 if( MustHaveParamCount( nParamCount, 1, 2 ) )
1868 ScAddress aCellPos( aPos );
1869 BOOL bError = FALSE;
1870 if( nParamCount == 2 )
1871 bError = !PopDoubleRefOrSingleRef( aCellPos );
1872 String aInfoType( GetString() );
1873 if( bError || nGlobalError )
1874 PushIllegalParameter();
1875 else
1877 String aFuncResult;
1878 ScBaseCell* pCell = GetCell( aCellPos );
1880 ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::pLocale, ocCell);
1882 // *** ADDRESS INFO ***
1883 if( aInfoType.EqualsAscii( "COL" ) )
1884 { // column number (1-based)
1885 PushInt( aCellPos.Col() + 1 );
1887 else if( aInfoType.EqualsAscii( "ROW" ) )
1888 { // row number (1-based)
1889 PushInt( aCellPos.Row() + 1 );
1891 else if( aInfoType.EqualsAscii( "SHEET" ) )
1892 { // table number (1-based)
1893 PushInt( aCellPos.Tab() + 1 );
1895 else if( aInfoType.EqualsAscii( "ADDRESS" ) )
1896 { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW
1897 USHORT nFlags = (aCellPos.Tab() == aPos.Tab()) ? (SCA_ABS) : (SCA_ABS_3D);
1898 aCellPos.Format( aFuncResult, nFlags, pDok, pDok->GetAddressConvention() );
1899 PushString( aFuncResult );
1901 else if( aInfoType.EqualsAscii( "FILENAME" ) )
1902 { // file name and table name: 'FILENAME'#$TABLE
1903 SCTAB nTab = aCellPos.Tab();
1904 if( nTab < pDok->GetTableCount() )
1906 if( pDok->GetLinkMode( nTab ) == SC_LINK_VALUE )
1907 pDok->GetName( nTab, aFuncResult );
1908 else
1910 SfxObjectShell* pShell = pDok->GetDocumentShell();
1911 if( pShell && pShell->GetMedium() )
1913 aFuncResult = (sal_Unicode) '\'';
1914 const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject();
1915 aFuncResult += String( rURLObj.GetMainURL( INetURLObject::DECODE_UNAMBIGUOUS ) );
1916 aFuncResult.AppendAscii( "'#$" );
1917 String aTabName;
1918 pDok->GetName( nTab, aTabName );
1919 aFuncResult += aTabName;
1923 PushString( aFuncResult );
1925 else if( aInfoType.EqualsAscii( "COORD" ) )
1926 { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW
1927 // Yes, passing tab as col is intentional!
1928 ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format(
1929 aFuncResult, (SCA_COL_ABSOLUTE|SCA_VALID_COL), NULL, pDok->GetAddressConvention() );
1930 aFuncResult += ':';
1931 String aCellStr;
1932 aCellPos.Format( aCellStr, (SCA_COL_ABSOLUTE|SCA_VALID_COL|SCA_ROW_ABSOLUTE|SCA_VALID_ROW),
1933 NULL, pDok->GetAddressConvention() );
1934 aFuncResult += aCellStr;
1935 PushString( aFuncResult );
1938 // *** CELL PROPERTIES ***
1939 else if( aInfoType.EqualsAscii( "CONTENTS" ) )
1940 { // contents of the cell, no formatting
1941 if( pCell && pCell->HasStringData() )
1943 GetCellString( aFuncResult, pCell );
1944 PushString( aFuncResult );
1946 else
1947 PushDouble( GetCellValue( aCellPos, pCell ) );
1949 else if( aInfoType.EqualsAscii( "TYPE" ) )
1950 { // b = blank; l = string (label); v = otherwise (value)
1951 if( HasCellStringData( pCell ) )
1952 aFuncResult = 'l';
1953 else
1954 aFuncResult = HasCellValueData( pCell ) ? 'v' : 'b';
1955 PushString( aFuncResult );
1957 else if( aInfoType.EqualsAscii( "WIDTH" ) )
1958 { // column width (rounded off as count of zero characters in standard font and size)
1959 Printer* pPrinter = pDok->GetPrinter();
1960 MapMode aOldMode( pPrinter->GetMapMode() );
1961 Font aOldFont( pPrinter->GetFont() );
1962 Font aDefFont;
1964 pPrinter->SetMapMode( MAP_TWIP );
1965 // font color doesn't matter here
1966 pDok->GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter );
1967 pPrinter->SetFont( aDefFont );
1968 long nZeroWidth = pPrinter->GetTextWidth( String( '0' ) );
1969 pPrinter->SetFont( aOldFont );
1970 pPrinter->SetMapMode( aOldMode );
1971 int nZeroCount = (int)(pDok->GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth);
1972 PushInt( nZeroCount );
1974 else if( aInfoType.EqualsAscii( "PREFIX" ) )
1975 { // ' = left; " = right; ^ = centered
1976 if( HasCellStringData( pCell ) )
1978 const SvxHorJustifyItem* pJustAttr = (const SvxHorJustifyItem*)
1979 pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_HOR_JUSTIFY );
1980 switch( pJustAttr->GetValue() )
1982 case SVX_HOR_JUSTIFY_STANDARD:
1983 case SVX_HOR_JUSTIFY_LEFT:
1984 case SVX_HOR_JUSTIFY_BLOCK: aFuncResult = '\''; break;
1985 case SVX_HOR_JUSTIFY_CENTER: aFuncResult = '^'; break;
1986 case SVX_HOR_JUSTIFY_RIGHT: aFuncResult = '"'; break;
1987 case SVX_HOR_JUSTIFY_REPEAT: aFuncResult = '\\'; break;
1990 PushString( aFuncResult );
1992 else if( aInfoType.EqualsAscii( "PROTECT" ) )
1993 { // 1 = cell locked
1994 const ScProtectionAttr* pProtAttr = (const ScProtectionAttr*)
1995 pDok->GetAttr( aCellPos.Col(), aCellPos.Row(), aCellPos.Tab(), ATTR_PROTECTION );
1996 PushInt( pProtAttr->GetProtection() ? 1 : 0 );
1999 // *** FORMATTING ***
2000 else if( aInfoType.EqualsAscii( "FORMAT" ) )
2001 { // specific format code for standard formats
2002 ULONG nFormat = pDok->GetNumberFormat( aCellPos );
2003 BOOL bAppendPrec = TRUE;
2004 USHORT nPrec, nLeading;
2005 BOOL bThousand, bIsRed;
2006 pFormatter->GetFormatSpecialInfo( nFormat, bThousand, bIsRed, nPrec, nLeading );
2008 switch( pFormatter->GetType( nFormat ) )
2010 case NUMBERFORMAT_NUMBER: aFuncResult = (bThousand ? ',' : 'F'); break;
2011 case NUMBERFORMAT_CURRENCY: aFuncResult = 'C'; break;
2012 case NUMBERFORMAT_SCIENTIFIC: aFuncResult = 'S'; break;
2013 case NUMBERFORMAT_PERCENT: aFuncResult = 'P'; break;
2014 default:
2016 bAppendPrec = FALSE;
2017 switch( pFormatter->GetIndexTableOffset( nFormat ) )
2019 case NF_DATE_SYSTEM_SHORT:
2020 case NF_DATE_SYS_DMMMYY:
2021 case NF_DATE_SYS_DDMMYY:
2022 case NF_DATE_SYS_DDMMYYYY:
2023 case NF_DATE_SYS_DMMMYYYY:
2024 case NF_DATE_DIN_DMMMYYYY:
2025 case NF_DATE_SYS_DMMMMYYYY:
2026 case NF_DATE_DIN_DMMMMYYYY: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D1" ) ); break;
2027 case NF_DATE_SYS_DDMMM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D2" ) ); break;
2028 case NF_DATE_SYS_MMYY: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D3" ) ); break;
2029 case NF_DATETIME_SYSTEM_SHORT_HHMM:
2030 case NF_DATETIME_SYS_DDMMYYYY_HHMMSS:
2031 aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D4" ) ); break;
2032 case NF_DATE_DIN_MMDD: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D5" ) ); break;
2033 case NF_TIME_HHMMSSAMPM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D6" ) ); break;
2034 case NF_TIME_HHMMAMPM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D7" ) ); break;
2035 case NF_TIME_HHMMSS: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D8" ) ); break;
2036 case NF_TIME_HHMM: aFuncResult.AssignAscii( RTL_CONSTASCII_STRINGPARAM( "D9" ) ); break;
2037 default: aFuncResult = 'G';
2041 if( bAppendPrec )
2042 aFuncResult += String::CreateFromInt32( nPrec );
2043 const SvNumberformat* pFormat = pFormatter->GetEntry( nFormat );
2044 if( lcl_FormatHasNegColor( pFormat ) )
2045 aFuncResult += '-';
2046 if( lcl_FormatHasOpenPar( pFormat ) )
2047 aFuncResult.AppendAscii( RTL_CONSTASCII_STRINGPARAM( "()" ) );
2048 PushString( aFuncResult );
2050 else if( aInfoType.EqualsAscii( "COLOR" ) )
2051 { // 1 = negative values are colored, otherwise 0
2052 const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) );
2053 PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 );
2055 else if( aInfoType.EqualsAscii( "PARENTHESES" ) )
2056 { // 1 = format string contains a '(' character, otherwise 0
2057 const SvNumberformat* pFormat = pFormatter->GetEntry( pDok->GetNumberFormat( aCellPos ) );
2058 PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 );
2060 else
2061 PushIllegalArgument();
2067 void ScInterpreter::ScIsRef()
2069 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCell" );
2070 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2071 short nRes = 0;
2072 switch ( GetStackType() )
2074 case svSingleRef :
2076 ScAddress aAdr;
2077 PopSingleRef( aAdr );
2078 if ( !nGlobalError )
2079 nRes = 1;
2081 break;
2082 case svDoubleRef :
2084 ScRange aRange;
2085 PopDoubleRef( aRange );
2086 if ( !nGlobalError )
2087 nRes = 1;
2089 break;
2090 case svRefList :
2092 FormulaTokenRef x = PopToken();
2093 if ( !nGlobalError )
2094 nRes = !static_cast<ScToken*>(x.get())->GetRefList()->empty();
2096 break;
2097 default:
2098 Pop();
2100 nGlobalError = 0;
2101 PushInt( nRes );
2105 void ScInterpreter::ScIsValue()
2107 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsValue" );
2108 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2109 short nRes = 0;
2110 switch ( GetRawStackType() )
2112 case svDouble:
2113 Pop();
2114 nRes = 1;
2115 break;
2116 case svDoubleRef :
2117 case svSingleRef :
2119 ScAddress aAdr;
2120 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2121 break;
2122 ScBaseCell* pCell = GetCell( aAdr );
2123 if (GetCellErrCode( pCell ) == 0)
2125 switch ( GetCellType( pCell ) )
2127 case CELLTYPE_VALUE :
2128 nRes = 1;
2129 break;
2130 case CELLTYPE_FORMULA :
2131 nRes = ((ScFormulaCell*)pCell)->IsValue() &&
2132 !((ScFormulaCell*)pCell)->IsEmpty();
2133 break;
2134 default:
2135 ; // nothing
2139 break;
2140 case svMatrix:
2142 ScMatrixRef pMat = PopMatrix();
2143 if ( !pMat )
2144 ; // nothing
2145 else if ( !pJumpMatrix )
2147 if (pMat->GetErrorIfNotString( 0 ) == 0)
2148 nRes = pMat->IsValue( 0 );
2150 else
2152 SCSIZE nCols, nRows, nC, nR;
2153 pMat->GetDimensions( nCols, nRows);
2154 pJumpMatrix->GetPos( nC, nR);
2155 if ( nC < nCols && nR < nRows )
2156 if (pMat->GetErrorIfNotString( nC, nR) == 0)
2157 nRes = pMat->IsValue( nC, nR);
2160 break;
2161 default:
2162 Pop();
2164 nGlobalError = 0;
2165 PushInt( nRes );
2169 void ScInterpreter::ScIsFormula()
2171 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsFormula" );
2172 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2173 short nRes = 0;
2174 switch ( GetStackType() )
2176 case svDoubleRef :
2177 case svSingleRef :
2179 ScAddress aAdr;
2180 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2181 break;
2182 nRes = (GetCellType( GetCell( aAdr ) ) == CELLTYPE_FORMULA);
2184 break;
2185 default:
2186 Pop();
2188 nGlobalError = 0;
2189 PushInt( nRes );
2193 void ScInterpreter::ScFormula()
2195 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScFormula" );
2196 String aFormula;
2197 switch ( GetStackType() )
2199 case svDoubleRef :
2200 case svSingleRef :
2202 ScAddress aAdr;
2203 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2204 break;
2205 ScBaseCell* pCell = GetCell( aAdr );
2206 switch ( GetCellType( pCell ) )
2208 case CELLTYPE_FORMULA :
2209 ((ScFormulaCell*)pCell)->GetFormula( aFormula );
2210 break;
2211 default:
2212 SetError( NOTAVAILABLE );
2215 break;
2216 default:
2217 Pop();
2218 SetError( NOTAVAILABLE );
2220 PushString( aFormula );
2225 void ScInterpreter::ScIsNV()
2227 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsNV" );
2228 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2229 short nRes = 0;
2230 switch ( GetStackType() )
2232 case svDoubleRef :
2233 case svSingleRef :
2235 ScAddress aAdr;
2236 PopDoubleRefOrSingleRef( aAdr );
2237 if ( nGlobalError == NOTAVAILABLE )
2238 nRes = 1;
2239 else
2241 ScBaseCell* pCell = GetCell( aAdr );
2242 USHORT nErr = GetCellErrCode( pCell );
2243 nRes = (nErr == NOTAVAILABLE);
2246 break;
2247 case svMatrix:
2249 ScMatrixRef pMat = PopMatrix();
2250 if ( !pMat )
2251 ; // nothing
2252 else if ( !pJumpMatrix )
2253 nRes = (pMat->GetErrorIfNotString( 0 ) == NOTAVAILABLE);
2254 else
2256 SCSIZE nCols, nRows, nC, nR;
2257 pMat->GetDimensions( nCols, nRows);
2258 pJumpMatrix->GetPos( nC, nR);
2259 if ( nC < nCols && nR < nRows )
2260 nRes = (pMat->GetErrorIfNotString( nC, nR) == NOTAVAILABLE);
2263 break;
2264 default:
2265 PopError();
2266 if ( nGlobalError == NOTAVAILABLE )
2267 nRes = 1;
2269 nGlobalError = 0;
2270 PushInt( nRes );
2274 void ScInterpreter::ScIsErr()
2276 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsErr" );
2277 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2278 short nRes = 0;
2279 switch ( GetStackType() )
2281 case svDoubleRef :
2282 case svSingleRef :
2284 ScAddress aAdr;
2285 PopDoubleRefOrSingleRef( aAdr );
2286 if ( nGlobalError && nGlobalError != NOTAVAILABLE )
2287 nRes = 1;
2288 else
2290 ScBaseCell* pCell = GetCell( aAdr );
2291 USHORT nErr = GetCellErrCode( pCell );
2292 nRes = (nErr && nErr != NOTAVAILABLE);
2295 break;
2296 case svMatrix:
2298 ScMatrixRef pMat = PopMatrix();
2299 if ( nGlobalError || !pMat )
2300 nRes = ((nGlobalError && nGlobalError != NOTAVAILABLE) || !pMat);
2301 else if ( !pJumpMatrix )
2303 USHORT nErr = pMat->GetErrorIfNotString( 0 );
2304 nRes = (nErr && nErr != NOTAVAILABLE);
2306 else
2308 SCSIZE nCols, nRows, nC, nR;
2309 pMat->GetDimensions( nCols, nRows);
2310 pJumpMatrix->GetPos( nC, nR);
2311 if ( nC < nCols && nR < nRows )
2313 USHORT nErr = pMat->GetErrorIfNotString( nC, nR);
2314 nRes = (nErr && nErr != NOTAVAILABLE);
2318 break;
2319 default:
2320 PopError();
2321 if ( nGlobalError && nGlobalError != NOTAVAILABLE )
2322 nRes = 1;
2324 nGlobalError = 0;
2325 PushInt( nRes );
2329 void ScInterpreter::ScIsError()
2331 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsError" );
2332 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2333 short nRes = 0;
2334 switch ( GetStackType() )
2336 case svDoubleRef :
2337 case svSingleRef :
2339 ScAddress aAdr;
2340 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2342 nRes = 1;
2343 break;
2345 if ( nGlobalError )
2346 nRes = 1;
2347 else
2349 ScBaseCell* pCell = GetCell( aAdr );
2350 nRes = (GetCellErrCode( pCell ) != 0);
2353 break;
2354 case svMatrix:
2356 ScMatrixRef pMat = PopMatrix();
2357 if ( nGlobalError || !pMat )
2358 nRes = 1;
2359 else if ( !pJumpMatrix )
2360 nRes = (pMat->GetErrorIfNotString( 0 ) != 0);
2361 else
2363 SCSIZE nCols, nRows, nC, nR;
2364 pMat->GetDimensions( nCols, nRows);
2365 pJumpMatrix->GetPos( nC, nR);
2366 if ( nC < nCols && nR < nRows )
2367 nRes = (pMat->GetErrorIfNotString( nC, nR) != 0);
2370 break;
2371 default:
2372 PopError();
2373 if ( nGlobalError )
2374 nRes = 1;
2376 nGlobalError = 0;
2377 PushInt( nRes );
2381 short ScInterpreter::IsEven()
2383 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::IsEven" );
2384 nFuncFmtType = NUMBERFORMAT_LOGICAL;
2385 short nRes = 0;
2386 double fVal = 0.0;
2387 switch ( GetStackType() )
2389 case svDoubleRef :
2390 case svSingleRef :
2392 ScAddress aAdr;
2393 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2394 break;
2395 ScBaseCell* pCell = GetCell( aAdr );
2396 USHORT nErr = GetCellErrCode( pCell );
2397 if (nErr != 0)
2398 SetError(nErr);
2399 else
2401 switch ( GetCellType( pCell ) )
2403 case CELLTYPE_VALUE :
2404 fVal = GetCellValue( aAdr, pCell );
2405 nRes = 1;
2406 break;
2407 case CELLTYPE_FORMULA :
2408 if( ((ScFormulaCell*)pCell)->IsValue() )
2410 fVal = GetCellValue( aAdr, pCell );
2411 nRes = 1;
2413 break;
2414 default:
2415 ; // nothing
2419 break;
2420 case svDouble:
2422 fVal = PopDouble();
2423 nRes = 1;
2425 break;
2426 case svMatrix:
2428 ScMatrixRef pMat = PopMatrix();
2429 if ( !pMat )
2430 ; // nothing
2431 else if ( !pJumpMatrix )
2433 nRes = pMat->IsValue( 0 );
2434 if ( nRes )
2435 fVal = pMat->GetDouble( 0 );
2437 else
2439 SCSIZE nCols, nRows, nC, nR;
2440 pMat->GetDimensions( nCols, nRows);
2441 pJumpMatrix->GetPos( nC, nR);
2442 if ( nC < nCols && nR < nRows )
2444 nRes = pMat->IsValue( nC, nR);
2445 if ( nRes )
2446 fVal = pMat->GetDouble( nC, nR);
2448 else
2449 SetError( errNoValue);
2452 break;
2453 default:
2454 ; // nothing
2456 if ( !nRes )
2457 SetError( errIllegalParameter);
2458 else
2459 nRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 );
2460 return nRes;
2464 void ScInterpreter::ScIsEven()
2466 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsEven" );
2467 PushInt( IsEven() );
2471 void ScInterpreter::ScIsOdd()
2473 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIsOdd" );
2474 PushInt( !IsEven() );
2478 void ScInterpreter::ScN()
2480 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScN" );
2481 USHORT nErr = nGlobalError;
2482 nGlobalError = 0;
2483 double fVal;
2484 if ( GetRawStackType() == svString )
2486 fVal = 0.0;
2487 Pop();
2489 else
2490 fVal = GetDouble();
2491 if ( nGlobalError == NOTAVAILABLE || nGlobalError == errIllegalArgument )
2492 nGlobalError = 0; // N(#NA) and N("text") are ok
2493 if ( !nGlobalError && nErr != NOTAVAILABLE )
2494 nGlobalError = nErr;
2495 PushDouble( fVal );
2499 void ScInterpreter::ScTrim()
2500 { // trimmt nicht nur sondern schnibbelt auch doppelte raus!
2501 String aVal( GetString() );
2502 aVal.EraseLeadingChars();
2503 aVal.EraseTrailingChars();
2504 String aStr;
2505 register const sal_Unicode* p = aVal.GetBuffer();
2506 register const sal_Unicode* const pEnd = p + aVal.Len();
2507 while ( p < pEnd )
2509 if ( *p != ' ' || p[-1] != ' ' ) // erster kann kein ' ' sein, -1 ist also ok
2510 aStr += *p;
2511 p++;
2513 PushString( aStr );
2517 void ScInterpreter::ScUpper()
2519 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScTrim" );
2520 String aString = GetString();
2521 ScGlobal::pCharClass->toUpper(aString);
2522 PushString(aString);
2526 void ScInterpreter::ScPropper()
2528 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScPropper" );
2529 //2do: what to do with I18N-CJK ?!?
2530 String aStr( GetString() );
2531 const xub_StrLen nLen = aStr.Len();
2532 // #i82487# don't try to write to empty string's BufferAccess
2533 // (would crash now that the empty string is const)
2534 if ( nLen > 0 )
2536 String aUpr( ScGlobal::pCharClass->upper( aStr ) );
2537 String aLwr( ScGlobal::pCharClass->lower( aStr ) );
2538 register sal_Unicode* pStr = aStr.GetBufferAccess();
2539 const sal_Unicode* pUpr = aUpr.GetBuffer();
2540 const sal_Unicode* pLwr = aLwr.GetBuffer();
2541 *pStr = *pUpr;
2542 String aTmpStr( 'x' );
2543 xub_StrLen nPos = 1;
2544 while( nPos < nLen )
2546 aTmpStr.SetChar( 0, pStr[nPos-1] );
2547 if ( !ScGlobal::pCharClass->isLetter( aTmpStr, 0 ) )
2548 pStr[nPos] = pUpr[nPos];
2549 else
2550 pStr[nPos] = pLwr[nPos];
2551 nPos++;
2553 aStr.ReleaseBufferAccess( nLen );
2555 PushString( aStr );
2559 void ScInterpreter::ScLower()
2561 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLower" );
2562 String aString( GetString() );
2563 ScGlobal::pCharClass->toLower(aString);
2564 PushString(aString);
2568 void ScInterpreter::ScLen()
2570 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLen" );
2571 String aStr( GetString() );
2572 PushDouble( aStr.Len() );
2576 void ScInterpreter::ScT()
2578 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScT" );
2579 switch ( GetStackType() )
2581 case svDoubleRef :
2582 case svSingleRef :
2584 ScAddress aAdr;
2585 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2587 PushInt(0);
2588 return ;
2590 BOOL bValue = FALSE;
2591 ScBaseCell* pCell = GetCell( aAdr );
2592 if ( GetCellErrCode( pCell ) == 0 )
2594 switch ( GetCellType( pCell ) )
2596 case CELLTYPE_VALUE :
2597 bValue = TRUE;
2598 break;
2599 case CELLTYPE_FORMULA :
2600 bValue = ((ScFormulaCell*)pCell)->IsValue();
2601 break;
2602 default:
2603 ; // nothing
2606 if ( bValue )
2607 PushString( EMPTY_STRING );
2608 else
2610 // wie GetString()
2611 GetCellString( aTempStr, pCell );
2612 PushString( aTempStr );
2615 break;
2616 case svDouble :
2618 PopError();
2619 PushString( EMPTY_STRING );
2621 break;
2622 case svString :
2623 ; // leave on stack
2624 break;
2625 default :
2626 PushError( errUnknownOpCode);
2631 void ScInterpreter::ScValue()
2633 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScValue" );
2634 String aInputString;
2635 double fVal;
2637 switch ( GetRawStackType() )
2639 case svMissing:
2640 case svEmptyCell:
2641 Pop();
2642 PushInt(0);
2643 return;
2644 case svDouble:
2645 return; // leave on stack
2646 //break;
2648 case svSingleRef:
2649 case svDoubleRef:
2651 ScAddress aAdr;
2652 if ( !PopDoubleRefOrSingleRef( aAdr ) )
2654 PushInt(0);
2655 return;
2657 ScBaseCell* pCell = GetCell( aAdr );
2658 if ( pCell && pCell->HasStringData() )
2659 GetCellString( aInputString, pCell );
2660 else if ( pCell && pCell->HasValueData() )
2662 PushDouble( GetCellValue(aAdr, pCell) );
2663 return;
2665 else
2667 PushDouble(0.0);
2668 return;
2671 break;
2672 case svMatrix:
2674 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
2675 aInputString);
2676 switch (nType)
2678 case SC_MATVAL_EMPTY:
2679 fVal = 0.0;
2680 // fallthru
2681 case SC_MATVAL_VALUE:
2682 case SC_MATVAL_BOOLEAN:
2683 PushDouble( fVal);
2684 return;
2685 //break;
2686 case SC_MATVAL_STRING:
2687 // evaluated below
2688 break;
2689 default:
2690 PushIllegalArgument();
2693 break;
2694 default:
2695 aInputString = GetString();
2696 break;
2699 sal_uInt32 nFIndex = 0; // 0 for default locale
2700 if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal))
2701 PushDouble(fVal);
2702 else
2703 PushIllegalArgument();
2707 //2do: this should be a proper unicode string method
2708 inline BOOL lcl_ScInterpreter_IsPrintable( sal_Unicode c )
2710 return 0x20 <= c && c != 0x7f;
2713 void ScInterpreter::ScClean()
2715 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScClean" );
2716 String aStr( GetString() );
2717 for ( xub_StrLen i = 0; i < aStr.Len(); i++ )
2719 if ( !lcl_ScInterpreter_IsPrintable( aStr.GetChar( i ) ) )
2720 aStr.Erase(i,1);
2722 PushString(aStr);
2726 void ScInterpreter::ScCode()
2728 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCode" );
2729 //2do: make it full range unicode?
2730 const String& rStr = GetString();
2731 PushInt( (sal_uChar) ByteString::ConvertFromUnicode( rStr.GetChar(0), gsl_getSystemTextEncoding() ) );
2735 void ScInterpreter::ScChar()
2737 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScChar" );
2738 //2do: make it full range unicode?
2739 double fVal = GetDouble();
2740 if (fVal < 0.0 || fVal >= 256.0)
2741 PushIllegalArgument();
2742 else
2744 String aStr( '0' );
2745 aStr.SetChar( 0, ByteString::ConvertToUnicode( (sal_Char) fVal, gsl_getSystemTextEncoding() ) );
2746 PushString( aStr );
2751 /* #i70213# fullwidth/halfwidth conversion provided by
2752 * Takashi Nakamoto <bluedwarf@ooo>
2753 * erAck: added Excel compatibility conversions as seen in issue's test case. */
2755 static ::rtl::OUString lcl_convertIntoHalfWidth( const ::rtl::OUString & rStr )
2757 static bool bFirstASCCall = true;
2758 static utl::TransliterationWrapper aTrans( ::comphelper::getProcessServiceFactory(), 0 );
2760 if( bFirstASCCall )
2762 aTrans.loadModuleByImplName( ::rtl::OUString::createFromAscii( "FULLWIDTH_HALFWIDTH_LIKE_ASC" ), LANGUAGE_SYSTEM );
2763 bFirstASCCall = false;
2766 return aTrans.transliterate( rStr, 0, USHORT( rStr.getLength() ), NULL );
2770 static ::rtl::OUString lcl_convertIntoFullWidth( const ::rtl::OUString & rStr )
2772 static bool bFirstJISCall = true;
2773 static utl::TransliterationWrapper aTrans( ::comphelper::getProcessServiceFactory(), 0 );
2775 if( bFirstJISCall )
2777 aTrans.loadModuleByImplName( ::rtl::OUString::createFromAscii( "HALFWIDTH_FULLWIDTH_LIKE_JIS" ), LANGUAGE_SYSTEM );
2778 bFirstJISCall = false;
2781 return aTrans.transliterate( rStr, 0, USHORT( rStr.getLength() ), NULL );
2785 /* ODFF:
2786 * Summary: Converts half-width to full-width ASCII and katakana characters.
2787 * Semantics: Conversion is done for half-width ASCII and katakana characters,
2788 * other characters are simply copied from T to the result. This is the
2789 * complementary function to ASC.
2790 * For references regarding halfwidth and fullwidth characters see
2791 * http://www.unicode.org/reports/tr11/
2792 * http://www.unicode.org/charts/charindex2.html#H
2793 * http://www.unicode.org/charts/charindex2.html#F
2795 void ScInterpreter::ScJis()
2797 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScJis" );
2798 if (MustHaveParamCount( GetByte(), 1))
2799 PushString( lcl_convertIntoFullWidth( GetString()));
2803 /* ODFF:
2804 * Summary: Converts full-width to half-width ASCII and katakana characters.
2805 * Semantics: Conversion is done for full-width ASCII and katakana characters,
2806 * other characters are simply copied from T to the result. This is the
2807 * complementary function to JIS.
2809 void ScInterpreter::ScAsc()
2811 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScAsc" );
2812 if (MustHaveParamCount( GetByte(), 1))
2813 PushString( lcl_convertIntoHalfWidth( GetString()));
2817 void ScInterpreter::ScMin( BOOL bTextAsZero )
2819 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScMin" );
2820 short nParamCount = GetByte();
2821 if (!MustHaveParamCountMin( nParamCount, 1))
2822 return;
2823 double nMin = ::std::numeric_limits<double>::max();
2824 double nVal = 0.0;
2825 ScAddress aAdr;
2826 ScRange aRange;
2827 size_t nRefInList = 0;
2828 while (nParamCount-- > 0)
2830 switch (GetStackType())
2832 case svDouble :
2834 nVal = GetDouble();
2835 if (nMin > nVal) nMin = nVal;
2836 nFuncFmtType = NUMBERFORMAT_NUMBER;
2838 break;
2839 case svSingleRef :
2841 PopSingleRef( aAdr );
2842 ScBaseCell* pCell = GetCell( aAdr );
2843 if (HasCellValueData(pCell))
2845 nVal = GetCellValue( aAdr, pCell );
2846 CurFmtToFuncFmt();
2847 if (nMin > nVal) nMin = nVal;
2849 else if ( bTextAsZero && HasCellStringData( pCell ) )
2851 if ( nMin > 0.0 )
2852 nMin = 0.0;
2855 break;
2856 case svDoubleRef :
2857 case svRefList :
2859 USHORT nErr = 0;
2860 PopDoubleRef( aRange, nParamCount, nRefInList);
2861 ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero );
2862 if (aValIter.GetFirst(nVal, nErr))
2864 if (nMin > nVal)
2865 nMin = nVal;
2866 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
2867 while ((nErr == 0) && aValIter.GetNext(nVal, nErr))
2869 if (nMin > nVal)
2870 nMin = nVal;
2872 SetError(nErr);
2875 break;
2876 case svMatrix :
2878 ScMatrixRef pMat = PopMatrix();
2879 if (pMat)
2881 SCSIZE nC, nR;
2882 nFuncFmtType = NUMBERFORMAT_NUMBER;
2883 pMat->GetDimensions(nC, nR);
2884 if (pMat->IsNumeric())
2886 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
2887 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
2889 nVal = pMat->GetDouble(nMatCol,nMatRow);
2890 if (nMin > nVal) nMin = nVal;
2893 else
2895 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
2897 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
2899 if (!pMat->IsString(nMatCol,nMatRow))
2901 nVal = pMat->GetDouble(nMatCol,nMatRow);
2902 if (nMin > nVal) nMin = nVal;
2904 else if ( bTextAsZero )
2906 if ( nMin > 0.0 )
2907 nMin = 0.0;
2914 break;
2915 case svString :
2917 Pop();
2918 if ( bTextAsZero )
2920 if ( nMin > 0.0 )
2921 nMin = 0.0;
2923 else
2924 SetError(errIllegalParameter);
2926 break;
2927 default :
2928 Pop();
2929 SetError(errIllegalParameter);
2932 if ( nVal < nMin )
2933 PushDouble(0.0);
2934 else
2935 PushDouble(nMin);
2938 #if defined(WIN) && defined(MSC)
2939 #pragma optimize("",off)
2940 #endif
2942 void ScInterpreter::ScMax( BOOL bTextAsZero )
2944 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScMax" );
2945 short nParamCount = GetByte();
2946 if (!MustHaveParamCountMin( nParamCount, 1))
2947 return;
2948 double nMax = -(::std::numeric_limits<double>::max());
2949 double nVal = 0.0;
2950 ScAddress aAdr;
2951 ScRange aRange;
2952 size_t nRefInList = 0;
2953 while (nParamCount-- > 0)
2955 switch (GetStackType())
2957 case svDouble :
2959 nVal = GetDouble();
2960 if (nMax < nVal) nMax = nVal;
2961 nFuncFmtType = NUMBERFORMAT_NUMBER;
2963 break;
2964 case svSingleRef :
2966 PopSingleRef( aAdr );
2967 ScBaseCell* pCell = GetCell( aAdr );
2968 if (HasCellValueData(pCell))
2970 nVal = GetCellValue( aAdr, pCell );
2971 CurFmtToFuncFmt();
2972 if (nMax < nVal) nMax = nVal;
2974 else if ( bTextAsZero && HasCellStringData( pCell ) )
2976 if ( nMax < 0.0 )
2977 nMax = 0.0;
2980 break;
2981 case svDoubleRef :
2982 case svRefList :
2984 USHORT nErr = 0;
2985 PopDoubleRef( aRange, nParamCount, nRefInList);
2986 ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero );
2987 if (aValIter.GetFirst(nVal, nErr))
2989 if (nMax < nVal)
2990 nMax = nVal;
2991 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
2992 while ((nErr == 0) && aValIter.GetNext(nVal, nErr))
2994 if (nMax < nVal)
2995 nMax = nVal;
2997 SetError(nErr);
3000 break;
3001 case svMatrix :
3003 ScMatrixRef pMat = PopMatrix();
3004 if (pMat)
3006 nFuncFmtType = NUMBERFORMAT_NUMBER;
3007 SCSIZE nC, nR;
3008 pMat->GetDimensions(nC, nR);
3009 if (pMat->IsNumeric())
3011 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
3012 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
3014 nVal = pMat->GetDouble(nMatCol,nMatRow);
3015 if (nMax < nVal) nMax = nVal;
3018 else
3020 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
3022 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
3024 if (!pMat->IsString(nMatCol,nMatRow))
3026 nVal = pMat->GetDouble(nMatCol,nMatRow);
3027 if (nMax < nVal) nMax = nVal;
3029 else if ( bTextAsZero )
3031 if ( nMax < 0.0 )
3032 nMax = 0.0;
3039 break;
3040 case svString :
3042 Pop();
3043 if ( bTextAsZero )
3045 if ( nMax < 0.0 )
3046 nMax = 0.0;
3048 else
3049 SetError(errIllegalParameter);
3051 break;
3052 default :
3053 Pop();
3054 SetError(errIllegalParameter);
3057 if ( nVal > nMax )
3058 PushDouble(0.0);
3059 else
3060 PushDouble(nMax);
3062 #if defined(WIN) && defined(MSC)
3063 #pragma optimize("",on)
3064 #endif
3067 double ScInterpreter::IterateParameters( ScIterFunc eFunc, BOOL bTextAsZero )
3069 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::IterateParameters" );
3070 short nParamCount = GetByte();
3071 double fRes = ( eFunc == ifPRODUCT ) ? 1.0 : 0.0;
3072 double fVal = 0.0;
3073 double fMem = 0.0;
3074 BOOL bNull = TRUE;
3075 ULONG nCount = 0;
3076 ScAddress aAdr;
3077 ScRange aRange;
3078 size_t nRefInList = 0;
3079 if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) )
3080 nGlobalError = 0;
3081 while (nParamCount-- > 0)
3083 StackVar eStackType = GetStackType();
3084 switch (eStackType)
3086 case svDouble:
3087 case svString:
3089 if( eFunc == ifCOUNT && eStackType == svString )
3091 String aStr( PopString() );
3092 sal_uInt32 nFIndex = 0; // damit default Land/Spr.
3093 if ( bTextAsZero || pFormatter->IsNumberFormat(aStr, nFIndex, fVal))
3094 nCount++;
3096 else if (eFunc == ifCOUNT2)
3097 // COUNTA - we should count both number and string.
3098 ++nCount;
3099 else
3101 if ( bTextAsZero && eStackType == svString )
3103 Pop();
3104 nCount++;
3105 if ( eFunc == ifPRODUCT )
3106 fRes = 0.0;
3107 fVal = 0;
3109 else
3111 fVal = GetDouble();
3112 nCount++;
3114 switch ( eFunc )
3116 case ifAVERAGE:
3117 case ifSUM:
3118 if ( bNull && fVal != 0.0 )
3120 bNull = FALSE;
3121 fMem = fVal;
3123 else
3124 fRes += fVal;
3125 break;
3126 case ifSUMSQ: fRes += fVal * fVal; break;
3127 case ifPRODUCT: fRes *= fVal; break;
3129 default:;
3132 nFuncFmtType = NUMBERFORMAT_NUMBER;
3134 break;
3135 case svSingleRef :
3137 PopSingleRef( aAdr );
3138 if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) )
3140 nGlobalError = 0;
3141 if ( eFunc == ifCOUNT2 )
3142 ++nCount;
3143 break;
3145 ScBaseCell* pCell = GetCell( aAdr );
3146 if ( pCell )
3148 if( eFunc == ifCOUNT2 )
3150 CellType eCellType = pCell->GetCellType();
3151 if (eCellType != CELLTYPE_NONE && eCellType != CELLTYPE_NOTE)
3152 nCount++;
3153 if ( nGlobalError )
3154 nGlobalError = 0;
3156 else if ( pCell->HasValueData() )
3158 nCount++;
3159 fVal = GetCellValue( aAdr, pCell );
3160 CurFmtToFuncFmt();
3161 switch( eFunc )
3163 case ifAVERAGE:
3164 case ifSUM:
3165 if ( bNull && fVal != 0.0 )
3167 bNull = FALSE;
3168 fMem = fVal;
3170 else
3171 fRes += fVal;
3172 break;
3173 case ifSUMSQ: fRes += fVal * fVal; break;
3174 case ifPRODUCT: fRes *= fVal; break;
3175 case ifCOUNT:
3176 if ( nGlobalError )
3178 nGlobalError = 0;
3179 nCount--;
3181 break;
3182 default: ; // nothing
3185 else if ( bTextAsZero && pCell->HasStringData() )
3187 nCount++;
3188 if ( eFunc == ifPRODUCT )
3189 fRes = 0.0;
3193 break;
3194 case svDoubleRef :
3195 case svRefList :
3197 USHORT nErr = 0;
3198 PopDoubleRef( aRange, nParamCount, nRefInList);
3199 if ( nGlobalError && ( eFunc == ifCOUNT2 || eFunc == ifCOUNT ) )
3201 nGlobalError = 0;
3202 if ( eFunc == ifCOUNT2 )
3203 ++nCount;
3204 break;
3206 if( eFunc == ifCOUNT2 )
3208 ScBaseCell* pCell;
3209 ScCellIterator aIter( pDok, aRange, glSubTotal );
3210 if ( (pCell = aIter.GetFirst()) != NULL )
3214 CellType eType = pCell->GetCellType();
3215 if( eType != CELLTYPE_NONE && eType != CELLTYPE_NOTE )
3216 nCount++;
3218 while ( (pCell = aIter.GetNext()) != NULL );
3220 if ( nGlobalError )
3221 nGlobalError = 0;
3223 else
3225 ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero );
3226 if (aValIter.GetFirst(fVal, nErr))
3228 // Schleife aus Performance-Gruenden nach innen verlegt:
3229 aValIter.GetCurNumFmtInfo( nFuncFmtType, nFuncFmtIndex );
3230 switch( eFunc )
3232 case ifAVERAGE:
3233 case ifSUM:
3236 SetError(nErr);
3237 if ( bNull && fVal != 0.0 )
3239 bNull = FALSE;
3240 fMem = fVal;
3242 else
3243 fRes += fVal;
3244 nCount++;
3246 while (aValIter.GetNext(fVal, nErr));
3247 break;
3248 case ifSUMSQ:
3251 SetError(nErr);
3252 fRes += fVal * fVal;
3253 nCount++;
3255 while (aValIter.GetNext(fVal, nErr));
3256 break;
3257 case ifPRODUCT:
3260 SetError(nErr);
3261 fRes *= fVal;
3262 nCount++;
3264 while (aValIter.GetNext(fVal, nErr));
3265 break;
3266 case ifCOUNT:
3269 if ( !nErr )
3270 nCount++;
3272 while (aValIter.GetNext(fVal, nErr));
3273 break;
3274 default: ; // nothing
3276 SetError( nErr );
3280 break;
3281 case svMatrix :
3283 ScMatrixRef pMat = PopMatrix();
3284 if (pMat)
3286 SCSIZE nC, nR;
3287 nFuncFmtType = NUMBERFORMAT_NUMBER;
3288 pMat->GetDimensions(nC, nR);
3289 if( eFunc == ifCOUNT2 )
3290 nCount += (ULONG) nC * nR;
3291 else
3293 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
3295 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
3297 if (!pMat->IsString(nMatCol,nMatRow))
3299 nCount++;
3300 fVal = pMat->GetDouble(nMatCol,nMatRow);
3301 switch( eFunc )
3303 case ifAVERAGE:
3304 case ifSUM:
3305 if ( bNull && fVal != 0.0 )
3307 bNull = FALSE;
3308 fMem = fVal;
3310 else
3311 fRes += fVal;
3312 break;
3313 case ifSUMSQ: fRes += fVal * fVal; break;
3314 case ifPRODUCT: fRes *= fVal; break;
3315 default: ; // nothing
3318 else if ( bTextAsZero )
3320 nCount++;
3321 if ( eFunc == ifPRODUCT )
3322 fRes = 0.0;
3329 break;
3330 case svError:
3332 Pop();
3333 if ( eFunc == ifCOUNT )
3335 nGlobalError = 0;
3337 else if ( eFunc == ifCOUNT2 )
3339 nCount++;
3340 nGlobalError = 0;
3343 break;
3344 default :
3345 while (nParamCount-- > 0)
3346 PopError();
3347 SetError(errIllegalParameter);
3350 switch( eFunc )
3352 case ifSUM: fRes = ::rtl::math::approxAdd( fRes, fMem ); break;
3353 case ifAVERAGE: fRes = div(::rtl::math::approxAdd( fRes, fMem ), nCount); break;
3354 case ifCOUNT2:
3355 case ifCOUNT: fRes = nCount; break;
3356 case ifPRODUCT: if ( !nCount ) fRes = 0.0; break;
3357 default: ; // nothing
3359 // Bei Summen etc. macht ein BOOL-Ergebnis keinen Sinn
3360 // und Anzahl ist immer Number (#38345#)
3361 if( eFunc == ifCOUNT || nFuncFmtType == NUMBERFORMAT_LOGICAL )
3362 nFuncFmtType = NUMBERFORMAT_NUMBER;
3363 return fRes;
3367 void ScInterpreter::ScSumSQ()
3369 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSumSQ" );
3370 PushDouble( IterateParameters( ifSUMSQ ) );
3374 void ScInterpreter::ScSum()
3376 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSum" );
3377 PushDouble( IterateParameters( ifSUM ) );
3381 void ScInterpreter::ScProduct()
3383 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScProduct" );
3384 PushDouble( IterateParameters( ifPRODUCT ) );
3388 void ScInterpreter::ScAverage( BOOL bTextAsZero )
3390 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScAverage" );
3391 PushDouble( IterateParameters( ifAVERAGE, bTextAsZero ) );
3395 void ScInterpreter::ScCount()
3397 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCount" );
3398 PushDouble( IterateParameters( ifCOUNT ) );
3402 void ScInterpreter::ScCount2()
3404 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCount2" );
3405 PushDouble( IterateParameters( ifCOUNT2 ) );
3409 void ScInterpreter::GetStVarParams( double& rVal, double& rValCount,
3410 BOOL bTextAsZero )
3412 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::GetStVarParams" );
3413 short nParamCount = GetByte();
3415 std::vector<double> values;
3416 double fSum = 0.0;
3417 double vSum = 0.0;
3418 double vMean = 0.0;
3419 double fVal = 0.0;
3420 rValCount = 0.0;
3421 ScAddress aAdr;
3422 ScRange aRange;
3423 size_t nRefInList = 0;
3424 while (nParamCount-- > 0)
3426 switch (GetStackType())
3428 case svDouble :
3430 fVal = GetDouble();
3431 values.push_back(fVal);
3432 fSum += fVal;
3433 rValCount++;
3435 break;
3436 case svSingleRef :
3438 PopSingleRef( aAdr );
3439 ScBaseCell* pCell = GetCell( aAdr );
3440 if (HasCellValueData(pCell))
3442 fVal = GetCellValue( aAdr, pCell );
3443 values.push_back(fVal);
3444 fSum += fVal;
3445 rValCount++;
3447 else if ( bTextAsZero && HasCellStringData( pCell ) )
3449 values.push_back(0.0);
3450 rValCount++;
3453 break;
3454 case svDoubleRef :
3455 case svRefList :
3457 USHORT nErr = 0;
3458 PopDoubleRef( aRange, nParamCount, nRefInList);
3459 ScValueIterator aValIter( pDok, aRange, glSubTotal, bTextAsZero );
3460 if (aValIter.GetFirst(fVal, nErr))
3464 values.push_back(fVal);
3465 fSum += fVal;
3466 rValCount++;
3468 while ((nErr == 0) && aValIter.GetNext(fVal, nErr));
3471 break;
3472 case svMatrix :
3474 ScMatrixRef pMat = PopMatrix();
3475 if (pMat)
3477 SCSIZE nC, nR;
3478 pMat->GetDimensions(nC, nR);
3479 for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++)
3481 for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++)
3483 if (!pMat->IsString(nMatCol,nMatRow))
3485 fVal= pMat->GetDouble(nMatCol,nMatRow);
3486 values.push_back(fVal);
3487 fSum += fVal;
3488 rValCount++;
3490 else if ( bTextAsZero )
3492 values.push_back(0.0);
3493 rValCount++;
3499 break;
3500 case svString :
3502 Pop();
3503 if ( bTextAsZero )
3505 values.push_back(0.0);
3506 rValCount++;
3508 else
3509 SetError(errIllegalParameter);
3511 break;
3512 default :
3513 Pop();
3514 SetError(errIllegalParameter);
3518 ::std::vector<double>::size_type n = values.size();
3519 vMean = fSum / n;
3520 for (::std::vector<double>::size_type i = 0; i < n; i++)
3521 vSum += (values[i] - vMean) * (values[i] - vMean);
3523 rVal = vSum;
3527 void ScInterpreter::ScVar( BOOL bTextAsZero )
3529 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScVar" );
3530 double nVal;
3531 double nValCount;
3532 GetStVarParams( nVal, nValCount, bTextAsZero );
3534 if (nValCount <= 1.0)
3535 PushError( errDivisionByZero );
3536 else
3537 PushDouble( nVal / (nValCount - 1.0));
3541 void ScInterpreter::ScVarP( BOOL bTextAsZero )
3543 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScVarP" );
3544 double nVal;
3545 double nValCount;
3546 GetStVarParams( nVal, nValCount, bTextAsZero );
3548 PushDouble( div( nVal, nValCount));
3552 void ScInterpreter::ScStDev( BOOL bTextAsZero )
3554 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScStDev" );
3555 double nVal;
3556 double nValCount;
3557 GetStVarParams( nVal, nValCount, bTextAsZero );
3558 if (nValCount <= 1.0)
3559 PushError( errDivisionByZero );
3560 else
3561 PushDouble( sqrt( nVal / (nValCount - 1.0)));
3565 void ScInterpreter::ScStDevP( BOOL bTextAsZero )
3567 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScStDevP" );
3568 double nVal;
3569 double nValCount;
3570 GetStVarParams( nVal, nValCount, bTextAsZero );
3571 if (nValCount == 0.0)
3572 PushError( errDivisionByZero );
3573 else
3574 PushDouble( sqrt( nVal / nValCount));
3576 /* this was: PushDouble( sqrt( div( nVal, nValCount)));
3578 * Besides that the special NAN gets lost in the call through sqrt(),
3579 * unxlngi6.pro then looped back and forth somewhere between div() and
3580 * ::rtl::math::setNan(). Tests showed that
3582 * sqrt( div( 1, 0));
3584 * produced a loop, but
3586 * double f1 = div( 1, 0);
3587 * sqrt( f1 );
3589 * was fine. There seems to be some compiler optimization problem. It does
3590 * not occur when compiled with debug=t.
3595 void ScInterpreter::ScColumns()
3597 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScColumns" );
3598 BYTE nParamCount = GetByte();
3599 ULONG nVal = 0;
3600 SCCOL nCol1;
3601 SCROW nRow1;
3602 SCTAB nTab1;
3603 SCCOL nCol2;
3604 SCROW nRow2;
3605 SCTAB nTab2;
3606 while (nParamCount-- > 0)
3608 switch ( GetStackType() )
3610 case svSingleRef:
3611 PopError();
3612 nVal++;
3613 break;
3614 case svDoubleRef:
3615 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3616 nVal += static_cast<ULONG>(nTab2 - nTab1 + 1) *
3617 static_cast<ULONG>(nCol2 - nCol1 + 1);
3618 break;
3619 case svMatrix:
3621 ScMatrixRef pMat = PopMatrix();
3622 if (pMat)
3624 SCSIZE nC, nR;
3625 pMat->GetDimensions(nC, nR);
3626 nVal += nC;
3629 break;
3630 default:
3631 PopError();
3632 SetError(errIllegalParameter);
3635 PushDouble((double)nVal);
3639 void ScInterpreter::ScRows()
3641 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRows" );
3642 BYTE nParamCount = GetByte();
3643 ULONG nVal = 0;
3644 SCCOL nCol1;
3645 SCROW nRow1;
3646 SCTAB nTab1;
3647 SCCOL nCol2;
3648 SCROW nRow2;
3649 SCTAB nTab2;
3650 while (nParamCount-- > 0)
3652 switch ( GetStackType() )
3654 case svSingleRef:
3655 PopError();
3656 nVal++;
3657 break;
3658 case svDoubleRef:
3659 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3660 nVal += static_cast<ULONG>(nTab2 - nTab1 + 1) *
3661 static_cast<ULONG>(nRow2 - nRow1 + 1);
3662 break;
3663 case svMatrix:
3665 ScMatrixRef pMat = PopMatrix();
3666 if (pMat)
3668 SCSIZE nC, nR;
3669 pMat->GetDimensions(nC, nR);
3670 nVal += nR;
3673 break;
3674 default:
3675 PopError();
3676 SetError(errIllegalParameter);
3679 PushDouble((double)nVal);
3682 void ScInterpreter::ScTables()
3684 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScTables" );
3685 BYTE nParamCount = GetByte();
3686 ULONG nVal;
3687 if ( nParamCount == 0 )
3688 nVal = pDok->GetTableCount();
3689 else
3691 nVal = 0;
3692 SCCOL nCol1;
3693 SCROW nRow1;
3694 SCTAB nTab1;
3695 SCCOL nCol2;
3696 SCROW nRow2;
3697 SCTAB nTab2;
3698 while (nParamCount-- > 0)
3700 switch ( GetStackType() )
3702 case svSingleRef:
3703 PopError();
3704 nVal++;
3705 break;
3706 case svDoubleRef:
3707 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
3708 nVal += static_cast<ULONG>(nTab2 - nTab1 + 1);
3709 break;
3710 case svMatrix:
3711 PopError();
3712 nVal++;
3713 break;
3714 default:
3715 PopError();
3716 SetError( errIllegalParameter );
3720 PushDouble( (double) nVal );
3724 void ScInterpreter::ScColumn()
3726 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScColumn" );
3727 BYTE nParamCount = GetByte();
3728 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
3730 double nVal = 0;
3731 if (nParamCount == 0)
3733 nVal = aPos.Col() + 1;
3734 if (bMatrixFormula)
3736 SCCOL nCols;
3737 SCROW nRows;
3738 pMyFormulaCell->GetMatColsRows( nCols, nRows);
3739 ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1);
3740 if (pResMat)
3742 for (SCCOL i=0; i < nCols; ++i)
3743 pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0);
3744 PushMatrix( pResMat);
3745 return;
3749 else
3751 switch ( GetStackType() )
3753 case svSingleRef :
3755 SCCOL nCol1;
3756 SCROW nRow1;
3757 SCTAB nTab1;
3758 PopSingleRef( nCol1, nRow1, nTab1 );
3759 nVal = (double) (nCol1 + 1);
3761 break;
3762 case svDoubleRef :
3764 SCCOL nCol1;
3765 SCROW nRow1;
3766 SCTAB nTab1;
3767 SCCOL nCol2;
3768 SCROW nRow2;
3769 SCTAB nTab2;
3770 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
3771 if (nCol2 > nCol1)
3773 ScMatrixRef pResMat = GetNewMat(
3774 static_cast<SCSIZE>(nCol2-nCol1+1), 1);
3775 if (pResMat)
3777 for (SCCOL i = nCol1; i <= nCol2; i++)
3778 pResMat->PutDouble((double)(i+1),
3779 static_cast<SCSIZE>(i-nCol1), 0);
3780 PushMatrix(pResMat);
3781 return;
3783 else
3784 nVal = 0.0;
3786 else
3787 nVal = (double) (nCol1 + 1);
3789 break;
3790 default:
3791 SetError( errIllegalParameter );
3792 nVal = 0.0;
3795 PushDouble( nVal );
3800 void ScInterpreter::ScRow()
3802 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRow" );
3803 BYTE nParamCount = GetByte();
3804 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
3806 double nVal = 0;
3807 if (nParamCount == 0)
3809 nVal = aPos.Row() + 1;
3810 if (bMatrixFormula)
3812 SCCOL nCols;
3813 SCROW nRows;
3814 pMyFormulaCell->GetMatColsRows( nCols, nRows);
3815 ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows));
3816 if (pResMat)
3818 for (SCROW i=0; i < nRows; i++)
3819 pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i));
3820 PushMatrix( pResMat);
3821 return;
3825 else
3827 switch ( GetStackType() )
3829 case svSingleRef :
3831 SCCOL nCol1;
3832 SCROW nRow1;
3833 SCTAB nTab1;
3834 PopSingleRef( nCol1, nRow1, nTab1 );
3835 nVal = (double) (nRow1 + 1);
3837 break;
3838 case svDoubleRef :
3840 SCCOL nCol1;
3841 SCROW nRow1;
3842 SCTAB nTab1;
3843 SCCOL nCol2;
3844 SCROW nRow2;
3845 SCTAB nTab2;
3846 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
3847 if (nRow2 > nRow1)
3849 ScMatrixRef pResMat = GetNewMat( 1,
3850 static_cast<SCSIZE>(nRow2-nRow1+1));
3851 if (pResMat)
3853 for (SCROW i = nRow1; i <= nRow2; i++)
3854 pResMat->PutDouble((double)(i+1), 0,
3855 static_cast<SCSIZE>(i-nRow1));
3856 PushMatrix(pResMat);
3857 return;
3859 else
3860 nVal = 0.0;
3862 else
3863 nVal = (double) (nRow1 + 1);
3865 break;
3866 default:
3867 SetError( errIllegalParameter );
3868 nVal = 0.0;
3871 PushDouble( nVal );
3875 void ScInterpreter::ScTable()
3877 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScTable" );
3878 BYTE nParamCount = GetByte();
3879 if ( MustHaveParamCount( nParamCount, 0, 1 ) )
3881 SCTAB nVal = 0;
3882 if ( nParamCount == 0 )
3883 nVal = aPos.Tab() + 1;
3884 else
3886 switch ( GetStackType() )
3888 case svString :
3890 String aStr( PopString() );
3891 if ( pDok->GetTable( aStr, nVal ) )
3892 ++nVal;
3893 else
3894 SetError( errIllegalArgument );
3896 break;
3897 case svSingleRef :
3899 SCCOL nCol1;
3900 SCROW nRow1;
3901 SCTAB nTab1;
3902 PopSingleRef( nCol1, nRow1, nTab1 );
3903 nVal = nTab1 + 1;
3905 break;
3906 case svDoubleRef :
3908 SCCOL nCol1;
3909 SCROW nRow1;
3910 SCTAB nTab1;
3911 SCCOL nCol2;
3912 SCROW nRow2;
3913 SCTAB nTab2;
3914 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
3915 nVal = nTab1 + 1;
3917 break;
3918 default:
3919 SetError( errIllegalParameter );
3921 if ( nGlobalError )
3922 nVal = 0;
3924 PushDouble( (double) nVal );
3928 /** returns -1 when the matrix value is smaller than the query value, 0 when
3929 they are equal, and 1 when the matrix value is larger than the query
3930 value. */
3931 static sal_Int32 lcl_CompareMatrix2Query( SCSIZE i, const ScMatrix& rMat,
3932 const ScQueryEntry& rEntry)
3934 if (rMat.IsEmpty(i))
3936 /* TODO: in case we introduced query for real empty this would have to
3937 * be changed! */
3938 return -1; // empty always less than anything else
3941 /* FIXME: what is an empty path (result of IF(false;true_path) in
3942 * comparisons? */
3944 if (rMat.IsValue(i))
3946 if (rEntry.bQueryByString)
3947 return -1; // numeric always less than string
3949 const double nVal1 = rMat.GetDouble(i);
3950 const double nVal2 = rEntry.nVal;
3951 if (nVal1 == nVal2)
3952 return 0;
3954 return nVal1 < nVal2 ? -1 : 1;
3957 if (!rEntry.bQueryByString)
3958 return 1; // string always greater than numeric
3960 if (!rEntry.pStr)
3961 // this should not happen!
3962 return 1;
3964 const String& rStr1 = rMat.GetString(i);
3965 const String& rStr2 = *rEntry.pStr;
3967 return ScGlobal::pCollator->compareString( rStr1, rStr2); // case-insensitive
3970 /** returns the last item with the identical value as the original item
3971 value. */
3972 static void lcl_GetLastMatch( SCSIZE& rIndex, const ScMatrix& rMat,
3973 SCSIZE nMatCount, bool bReverse)
3975 if (rMat.IsValue(rIndex))
3977 double nVal = rMat.GetDouble(rIndex);
3978 if (bReverse)
3979 while (rIndex > 0 && rMat.IsValue(rIndex-1) &&
3980 nVal == rMat.GetDouble(rIndex-1))
3981 --rIndex;
3982 else
3983 while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) &&
3984 nVal == rMat.GetDouble(rIndex+1))
3985 ++rIndex;
3987 //! Order of IsEmptyPath, IsEmpty, IsString is significant!
3988 else if (rMat.IsEmptyPath(rIndex))
3990 if (bReverse)
3991 while (rIndex > 0 && rMat.IsEmptyPath(rIndex-1))
3992 --rIndex;
3993 else
3994 while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1))
3995 ++rIndex;
3997 else if (rMat.IsEmpty(rIndex))
3999 if (bReverse)
4000 while (rIndex > 0 && rMat.IsEmpty(rIndex-1))
4001 --rIndex;
4002 else
4003 while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1))
4004 ++rIndex;
4006 else if (rMat.IsString(rIndex))
4008 String aStr( rMat.GetString(rIndex));
4009 if (bReverse)
4010 while (rIndex > 0 && rMat.IsString(rIndex-1) &&
4011 aStr == rMat.GetString(rIndex-1))
4012 --rIndex;
4013 else
4014 while (rIndex < nMatCount-1 && rMat.IsString(rIndex+1) &&
4015 aStr == rMat.GetString(rIndex+1))
4016 ++rIndex;
4018 else
4020 DBG_ERRORFILE("lcl_GetLastMatch: unhandled matrix type");
4024 void ScInterpreter::ScMatch()
4026 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScMatch" );
4027 ScMatrixRef pMatSrc = NULL;
4029 BYTE nParamCount = GetByte();
4030 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
4032 double fTyp;
4033 if (nParamCount == 3)
4034 fTyp = GetDouble();
4035 else
4036 fTyp = 1.0;
4037 SCCOL nCol1 = 0;
4038 SCROW nRow1 = 0;
4039 SCTAB nTab1 = 0;
4040 SCCOL nCol2 = 0;
4041 SCROW nRow2 = 0;
4042 SCTAB nTab2 = 0;
4043 if (GetStackType() == svDoubleRef)
4045 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4046 if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2))
4048 PushIllegalParameter();
4049 return;
4052 else if (GetStackType() == svMatrix)
4054 pMatSrc = PopMatrix();
4055 if (!pMatSrc)
4057 PushIllegalParameter();
4058 return;
4061 else
4063 PushIllegalParameter();
4064 return;
4066 if (nGlobalError == 0)
4068 double fVal;
4069 String sStr;
4070 ScQueryParam rParam;
4071 rParam.nCol1 = nCol1;
4072 rParam.nRow1 = nRow1;
4073 rParam.nCol2 = nCol2;
4074 rParam.nTab = nTab1;
4075 rParam.bMixedComparison = TRUE;
4077 ScQueryEntry& rEntry = rParam.GetEntry(0);
4078 rEntry.bDoQuery = TRUE;
4079 if (fTyp < 0.0)
4080 rEntry.eOp = SC_GREATER_EQUAL;
4081 else if (fTyp > 0.0)
4082 rEntry.eOp = SC_LESS_EQUAL;
4083 switch ( GetStackType() )
4085 case svDouble:
4087 fVal = GetDouble();
4088 rEntry.bQueryByString = FALSE;
4089 rEntry.nVal = fVal;
4091 break;
4092 case svString:
4094 sStr = GetString();
4095 rEntry.bQueryByString = TRUE;
4096 *rEntry.pStr = sStr;
4098 break;
4099 case svDoubleRef :
4100 case svSingleRef :
4102 ScAddress aAdr;
4103 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4105 PushInt(0);
4106 return ;
4108 ScBaseCell* pCell = GetCell( aAdr );
4109 if (HasCellValueData(pCell))
4111 fVal = GetCellValue( aAdr, pCell );
4112 rEntry.bQueryByString = FALSE;
4113 rEntry.nVal = fVal;
4115 else
4117 GetCellString(sStr, pCell);
4118 rEntry.bQueryByString = TRUE;
4119 *rEntry.pStr = sStr;
4122 break;
4123 case svMatrix :
4125 ScMatValType nType = GetDoubleOrStringFromMatrix(
4126 rEntry.nVal, *rEntry.pStr);
4127 rEntry.bQueryByString = ScMatrix::IsNonValueType( nType);
4129 break;
4130 default:
4132 PushIllegalParameter();
4133 return;
4136 if ( rEntry.bQueryByString )
4138 BOOL bIsVBAMode = FALSE;
4139 if ( pDok )
4141 SfxObjectShell* pDocSh = pDok->GetDocumentShell();
4142 if ( pDocSh )
4143 bIsVBAMode = pDocSh->GetBasic()->isVBAEnabled();
4145 // #TODO handle MSO wildcards
4146 if ( bIsVBAMode )
4147 rParam.bRegExp = FALSE;
4148 else
4149 rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok );
4152 if (pMatSrc) // The source data is matrix array.
4154 SCSIZE nC, nR;
4155 pMatSrc->GetDimensions( nC, nR);
4156 if (nC > 1 && nR > 1)
4158 // The source matrix must be a vector.
4159 PushIllegalParameter();
4160 return;
4162 SCSIZE nMatCount = (nC == 1) ? nR : nC;
4164 // simple serial search for equality mode (source data doesn't
4165 // need to be sorted).
4167 if (rEntry.eOp == SC_EQUAL)
4169 for (SCSIZE i = 0; i < nMatCount; ++i)
4171 if (lcl_CompareMatrix2Query( i, *pMatSrc, rEntry) == 0)
4173 PushDouble(i+1); // found !
4174 return;
4177 PushNA(); // not found
4178 return;
4181 // binary search for non-equality mode (the source data is
4182 // assumed to be sorted).
4184 bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL);
4185 SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0;
4186 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
4188 SCSIZE nMid = nFirst + nLen/2;
4189 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, *pMatSrc, rEntry);
4190 if (nCmp == 0)
4192 // exact match. find the last item with the same value.
4193 lcl_GetLastMatch( nMid, *pMatSrc, nMatCount, !bAscOrder);
4194 PushDouble( nMid+1);
4195 return;
4198 if (nLen == 1) // first and last items are next to each other.
4200 if (nCmp < 0)
4201 nHitIndex = bAscOrder ? nLast : nFirst;
4202 else
4203 nHitIndex = bAscOrder ? nFirst : nLast;
4204 break;
4207 if (nCmp < 0)
4209 if (bAscOrder)
4210 nFirst = nMid;
4211 else
4212 nLast = nMid;
4214 else
4216 if (bAscOrder)
4217 nLast = nMid;
4218 else
4219 nFirst = nMid;
4223 if (nHitIndex == nMatCount-1) // last item
4225 sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, *pMatSrc, rEntry);
4226 if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0))
4228 // either the last item is an exact match or the real
4229 // hit is beyond the last item.
4230 PushDouble( nHitIndex+1);
4231 return;
4235 if (nHitIndex > 0) // valid hit must be 2nd item or higher
4237 PushDouble( nHitIndex); // non-exact match
4238 return;
4241 PushNA();
4242 return;
4245 SCCOLROW nDelta = 0;
4246 if (nCol1 == nCol2)
4247 { // search row in column
4248 rParam.nRow2 = nRow2;
4249 rEntry.nField = nCol1;
4250 ScAddress aResultPos( nCol1, nRow1, nTab1);
4251 if (!LookupQueryWithCache( aResultPos, rParam))
4253 PushNA();
4254 return;
4256 nDelta = aResultPos.Row() - nRow1;
4258 else
4259 { // search column in row
4260 SCCOL nC;
4261 rParam.bByRow = FALSE;
4262 rParam.nRow2 = nRow1;
4263 rEntry.nField = nCol1;
4264 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, FALSE);
4265 // Advance Entry.nField in Iterator if column changed
4266 aCellIter.SetAdvanceQueryParamEntryField( TRUE );
4267 if (fTyp == 0.0)
4268 { // EQUAL
4269 if ( aCellIter.GetFirst() )
4270 nC = aCellIter.GetCol();
4271 else
4273 PushNA();
4274 return;
4277 else
4278 { // <= or >=
4279 SCROW nR;
4280 if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) )
4282 PushNA();
4283 return;
4286 nDelta = nC - nCol1;
4288 PushDouble((double) (nDelta + 1));
4290 else
4291 PushIllegalParameter();
4296 void ScInterpreter::ScCountEmptyCells()
4298 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCountEmptyCells" );
4299 if ( MustHaveParamCount( GetByte(), 1 ) )
4301 ULONG nMaxCount = 0, nCount = 0;
4302 CellType eCellType;
4303 switch (GetStackType())
4305 case svSingleRef :
4307 nMaxCount = 1;
4308 ScAddress aAdr;
4309 PopSingleRef( aAdr );
4310 eCellType = GetCellType( GetCell( aAdr ) );
4311 if (eCellType != CELLTYPE_NONE && eCellType != CELLTYPE_NOTE)
4312 nCount = 1;
4314 break;
4315 case svDoubleRef :
4316 case svRefList :
4318 ScRange aRange;
4319 short nParam = 1;
4320 size_t nRefInList = 0;
4321 while (nParam-- > 0)
4323 PopDoubleRef( aRange, nParam, nRefInList);
4324 nMaxCount +=
4325 static_cast<ULONG>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) *
4326 static_cast<ULONG>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) *
4327 static_cast<ULONG>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1);
4328 ScBaseCell* pCell;
4329 ScCellIterator aDocIter( pDok, aRange, glSubTotal);
4330 if ( (pCell = aDocIter.GetFirst()) != NULL )
4334 if ((eCellType = pCell->GetCellType()) != CELLTYPE_NONE
4335 && eCellType != CELLTYPE_NOTE)
4336 nCount++;
4337 } while ( (pCell = aDocIter.GetNext()) != NULL );
4341 break;
4342 default : SetError(errIllegalParameter); break;
4344 PushDouble(nMaxCount - nCount);
4349 void ScInterpreter::ScCountIf()
4351 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCountIf" );
4352 if ( MustHaveParamCount( GetByte(), 2 ) )
4354 String rString;
4355 double fVal = 0.0;
4356 BOOL bIsString = TRUE;
4357 switch ( GetStackType() )
4359 case svDoubleRef :
4360 case svSingleRef :
4362 ScAddress aAdr;
4363 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4365 PushInt(0);
4366 return ;
4368 ScBaseCell* pCell = GetCell( aAdr );
4369 switch ( GetCellType( pCell ) )
4371 case CELLTYPE_VALUE :
4372 fVal = GetCellValue( aAdr, pCell );
4373 bIsString = FALSE;
4374 break;
4375 case CELLTYPE_FORMULA :
4376 if( ((ScFormulaCell*)pCell)->IsValue() )
4378 fVal = GetCellValue( aAdr, pCell );
4379 bIsString = FALSE;
4381 else
4382 GetCellString(rString, pCell);
4383 break;
4384 case CELLTYPE_STRING :
4385 case CELLTYPE_EDIT :
4386 GetCellString(rString, pCell);
4387 break;
4388 default:
4389 fVal = 0.0;
4390 bIsString = FALSE;
4393 break;
4394 case svMatrix :
4396 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
4397 rString);
4398 bIsString = ScMatrix::IsNonValueType( nType);
4400 break;
4401 case svString:
4402 rString = GetString();
4403 break;
4404 default:
4406 fVal = GetDouble();
4407 bIsString = FALSE;
4410 double fSum = 0.0;
4411 short nParam = 1;
4412 size_t nRefInList = 0;
4413 while (nParam-- > 0)
4415 SCCOL nCol1;
4416 SCROW nRow1;
4417 SCTAB nTab1;
4418 SCCOL nCol2;
4419 SCROW nRow2;
4420 SCTAB nTab2;
4421 ScMatrixRef pQueryMatrix;
4422 switch ( GetStackType() )
4424 case svDoubleRef :
4425 case svRefList :
4427 ScRange aRange;
4428 PopDoubleRef( aRange, nParam, nRefInList);
4429 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4431 break;
4432 case svSingleRef :
4433 PopSingleRef( nCol1, nRow1, nTab1 );
4434 nCol2 = nCol1;
4435 nRow2 = nRow1;
4436 nTab2 = nTab1;
4437 break;
4438 case svMatrix:
4440 pQueryMatrix = PopMatrix();
4441 if (!pQueryMatrix)
4443 PushIllegalParameter();
4444 return;
4446 nCol1 = 0;
4447 nRow1 = 0;
4448 nTab1 = 0;
4449 SCSIZE nC, nR;
4450 pQueryMatrix->GetDimensions( nC, nR);
4451 nCol2 = static_cast<SCCOL>(nC - 1);
4452 nRow2 = static_cast<SCROW>(nR - 1);
4453 nTab2 = 0;
4455 break;
4456 default:
4457 PushIllegalParameter();
4458 return ;
4460 if ( nTab1 != nTab2 )
4462 PushIllegalParameter();
4463 return;
4465 if (nCol1 > nCol2)
4467 PushIllegalParameter();
4468 return;
4470 if (nGlobalError == 0)
4472 ScQueryParam rParam;
4473 rParam.nRow1 = nRow1;
4474 rParam.nRow2 = nRow2;
4476 ScQueryEntry& rEntry = rParam.GetEntry(0);
4477 rEntry.bDoQuery = TRUE;
4478 if (!bIsString)
4480 rEntry.bQueryByString = FALSE;
4481 rEntry.nVal = fVal;
4482 rEntry.eOp = SC_EQUAL;
4484 else
4486 rParam.FillInExcelSyntax(rString, 0);
4487 sal_uInt32 nIndex = 0;
4488 rEntry.bQueryByString =
4489 !(pFormatter->IsNumberFormat(
4490 *rEntry.pStr, nIndex, rEntry.nVal));
4491 if ( rEntry.bQueryByString )
4492 rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok );
4494 rParam.nCol1 = nCol1;
4495 rParam.nCol2 = nCol2;
4496 rEntry.nField = nCol1;
4497 if (pQueryMatrix)
4499 // Never case-sensitive.
4500 ScCompareOptions aOptions( pDok, rEntry, rParam.bRegExp);
4501 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
4502 if (nGlobalError || !pResultMatrix)
4504 PushIllegalParameter();
4505 return;
4508 SCSIZE nSize = pResultMatrix->GetElementCount();
4509 for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex)
4511 if (pResultMatrix->IsValue( nIndex) &&
4512 pResultMatrix->GetDouble( nIndex))
4513 ++fSum;
4516 else
4518 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, FALSE);
4519 // Entry.nField im Iterator bei Spaltenwechsel weiterschalten
4520 aCellIter.SetAdvanceQueryParamEntryField( TRUE );
4521 if ( aCellIter.GetFirst() )
4525 fSum++;
4526 } while ( aCellIter.GetNext() );
4530 else
4532 PushIllegalParameter();
4533 return;
4536 PushDouble(fSum);
4541 void ScInterpreter::ScSumIf()
4543 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSumIf" );
4544 BYTE nParamCount = GetByte();
4545 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
4547 SCCOL nCol3 = 0;
4548 SCROW nRow3 = 0;
4549 SCTAB nTab3 = 0;
4551 ScMatrixRef pSumExtraMatrix;
4552 bool bSumExtraRange = (nParamCount == 3);
4553 if (bSumExtraRange)
4555 // Save only the upperleft cell in case of cell range. The geometry
4556 // of the 3rd parameter is taken from the 1st parameter.
4558 switch ( GetStackType() )
4560 case svDoubleRef :
4562 SCCOL nColJunk = 0;
4563 SCROW nRowJunk = 0;
4564 SCTAB nTabJunk = 0;
4565 PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk );
4566 if ( nTabJunk != nTab3 )
4568 PushIllegalParameter();
4569 return;
4572 break;
4573 case svSingleRef :
4574 PopSingleRef( nCol3, nRow3, nTab3 );
4575 break;
4576 case svMatrix:
4577 pSumExtraMatrix = PopMatrix();
4578 //! nCol3, nRow3, nTab3 remain 0
4579 break;
4580 default:
4581 PushIllegalParameter();
4582 return ;
4585 String rString;
4586 double fVal = 0.0;
4587 BOOL bIsString = TRUE;
4588 switch ( GetStackType() )
4590 case svDoubleRef :
4591 case svSingleRef :
4593 ScAddress aAdr;
4594 if ( !PopDoubleRefOrSingleRef( aAdr ) )
4596 PushInt(0);
4597 return ;
4599 ScBaseCell* pCell = GetCell( aAdr );
4600 switch ( GetCellType( pCell ) )
4602 case CELLTYPE_VALUE :
4603 fVal = GetCellValue( aAdr, pCell );
4604 bIsString = FALSE;
4605 break;
4606 case CELLTYPE_FORMULA :
4607 if( ((ScFormulaCell*)pCell)->IsValue() )
4609 fVal = GetCellValue( aAdr, pCell );
4610 bIsString = FALSE;
4612 else
4613 GetCellString(rString, pCell);
4614 break;
4615 case CELLTYPE_STRING :
4616 case CELLTYPE_EDIT :
4617 GetCellString(rString, pCell);
4618 break;
4619 default:
4620 fVal = 0.0;
4621 bIsString = FALSE;
4624 break;
4625 case svString:
4626 rString = GetString();
4627 break;
4628 case svMatrix :
4630 ScMatValType nType = GetDoubleOrStringFromMatrix( fVal,
4631 rString);
4632 bIsString = ScMatrix::IsNonValueType( nType);
4634 break;
4635 default:
4637 fVal = GetDouble();
4638 bIsString = FALSE;
4642 double fSum = 0.0;
4643 double fMem = 0.0;
4644 BOOL bNull = TRUE;
4645 short nParam = 1;
4646 size_t nRefInList = 0;
4647 while (nParam-- > 0)
4649 SCCOL nCol1;
4650 SCROW nRow1;
4651 SCTAB nTab1;
4652 SCCOL nCol2;
4653 SCROW nRow2;
4654 SCTAB nTab2;
4655 ScMatrixRef pQueryMatrix;
4656 switch ( GetStackType() )
4658 case svRefList :
4659 if (bSumExtraRange)
4661 PushIllegalParameter();
4662 return;
4664 else
4666 ScRange aRange;
4667 PopDoubleRef( aRange, nParam, nRefInList);
4668 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
4670 break;
4671 case svDoubleRef :
4672 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 );
4673 break;
4674 case svSingleRef :
4675 PopSingleRef( nCol1, nRow1, nTab1 );
4676 nCol2 = nCol1;
4677 nRow2 = nRow1;
4678 nTab2 = nTab1;
4679 break;
4680 case svMatrix:
4682 pQueryMatrix = PopMatrix();
4683 if (!pQueryMatrix)
4685 PushIllegalParameter();
4686 return;
4688 nCol1 = 0;
4689 nRow1 = 0;
4690 nTab1 = 0;
4691 SCSIZE nC, nR;
4692 pQueryMatrix->GetDimensions( nC, nR);
4693 nCol2 = static_cast<SCCOL>(nC - 1);
4694 nRow2 = static_cast<SCROW>(nR - 1);
4695 nTab2 = 0;
4697 break;
4698 default:
4699 PushIllegalParameter();
4700 return ;
4702 if ( nTab1 != nTab2 )
4704 PushIllegalArgument();
4705 return;
4708 if (bSumExtraRange)
4710 // Take the range geometry of the 1st parameter and apply it to
4711 // the 3rd. If parts of the resulting range would point outside
4712 // the sheet, don't complain but silently ignore and simply cut
4713 // them away, this is what Xcl does :-/
4715 // For the cut-away part we also don't need to determine the
4716 // criteria match, so shrink the source range accordingly,
4717 // instead of the result range.
4718 SCCOL nColDelta = nCol2 - nCol1;
4719 SCROW nRowDelta = nRow2 - nRow1;
4720 SCCOL nMaxCol;
4721 SCROW nMaxRow;
4722 if (pSumExtraMatrix)
4724 SCSIZE nC, nR;
4725 pSumExtraMatrix->GetDimensions( nC, nR);
4726 nMaxCol = static_cast<SCCOL>(nC - 1);
4727 nMaxRow = static_cast<SCROW>(nR - 1);
4729 else
4731 nMaxCol = MAXCOL;
4732 nMaxRow = MAXROW;
4734 if (nCol3 + nColDelta > nMaxCol)
4736 SCCOL nNewDelta = nMaxCol - nCol3;
4737 nCol2 = nCol1 + nNewDelta;
4740 if (nRow3 + nRowDelta > nMaxRow)
4742 SCROW nNewDelta = nMaxRow - nRow3;
4743 nRow2 = nRow1 + nNewDelta;
4746 else
4748 nCol3 = nCol1;
4749 nRow3 = nRow1;
4750 nTab3 = nTab1;
4753 if (nGlobalError == 0)
4755 ScQueryParam rParam;
4756 rParam.nRow1 = nRow1;
4757 rParam.nRow2 = nRow2;
4759 ScQueryEntry& rEntry = rParam.GetEntry(0);
4760 rEntry.bDoQuery = TRUE;
4761 if (!bIsString)
4763 rEntry.bQueryByString = FALSE;
4764 rEntry.nVal = fVal;
4765 rEntry.eOp = SC_EQUAL;
4767 else
4769 rParam.FillInExcelSyntax(rString, 0);
4770 sal_uInt32 nIndex = 0;
4771 rEntry.bQueryByString =
4772 !(pFormatter->IsNumberFormat(
4773 *rEntry.pStr, nIndex, rEntry.nVal));
4774 if ( rEntry.bQueryByString )
4775 rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok );
4777 ScAddress aAdr;
4778 aAdr.SetTab( nTab3 );
4779 rParam.nCol1 = nCol1;
4780 rParam.nCol2 = nCol2;
4781 rEntry.nField = nCol1;
4782 SCsCOL nColDiff = nCol3 - nCol1;
4783 SCsROW nRowDiff = nRow3 - nRow1;
4784 if (pQueryMatrix)
4786 // Never case-sensitive.
4787 ScCompareOptions aOptions( pDok, rEntry, rParam.bRegExp);
4788 ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions);
4789 if (nGlobalError || !pResultMatrix)
4791 PushIllegalParameter();
4792 return;
4795 if (pSumExtraMatrix)
4797 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
4799 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
4801 if (pResultMatrix->IsValue( nCol, nRow) &&
4802 pResultMatrix->GetDouble( nCol, nRow))
4804 SCSIZE nC = nCol + nColDiff;
4805 SCSIZE nR = nRow + nRowDiff;
4806 if (pSumExtraMatrix->IsValue( nC, nR))
4808 fVal = pSumExtraMatrix->GetDouble( nC, nR);
4809 if ( bNull && fVal != 0.0 )
4811 bNull = FALSE;
4812 fMem = fVal;
4814 else
4815 fSum += fVal;
4821 else
4823 for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
4825 for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
4827 if (pResultMatrix->GetDouble( nCol, nRow))
4829 aAdr.SetCol( nCol + nColDiff);
4830 aAdr.SetRow( nRow + nRowDiff);
4831 ScBaseCell* pCell = GetCell( aAdr );
4832 if ( HasCellValueData(pCell) )
4834 fVal = GetCellValue( aAdr, pCell );
4835 if ( bNull && fVal != 0.0 )
4837 bNull = FALSE;
4838 fMem = fVal;
4840 else
4841 fSum += fVal;
4848 else
4850 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, FALSE);
4851 // Increment Entry.nField in iterator when switching to next column.
4852 aCellIter.SetAdvanceQueryParamEntryField( TRUE );
4853 if ( aCellIter.GetFirst() )
4855 if (pSumExtraMatrix)
4859 SCSIZE nC = aCellIter.GetCol() + nColDiff;
4860 SCSIZE nR = aCellIter.GetRow() + nRowDiff;
4861 if (pSumExtraMatrix->IsValue( nC, nR))
4863 fVal = pSumExtraMatrix->GetDouble( nC, nR);
4864 if ( bNull && fVal != 0.0 )
4866 bNull = FALSE;
4867 fMem = fVal;
4869 else
4870 fSum += fVal;
4872 } while ( aCellIter.GetNext() );
4874 else
4878 aAdr.SetCol( aCellIter.GetCol() + nColDiff);
4879 aAdr.SetRow( aCellIter.GetRow() + nRowDiff);
4880 ScBaseCell* pCell = GetCell( aAdr );
4881 if ( HasCellValueData(pCell) )
4883 fVal = GetCellValue( aAdr, pCell );
4884 if ( bNull && fVal != 0.0 )
4886 bNull = FALSE;
4887 fMem = fVal;
4889 else
4890 fSum += fVal;
4892 } while ( aCellIter.GetNext() );
4897 else
4899 PushIllegalParameter();
4900 return;
4903 PushDouble( ::rtl::math::approxAdd( fSum, fMem ) );
4908 void ScInterpreter::ScLookup()
4910 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLookup" );
4911 BYTE nParamCount = GetByte();
4912 if ( !MustHaveParamCount( nParamCount, 2, 3 ) )
4913 return ;
4915 ScMatrixRef pDataMat = NULL, pResMat = NULL;
4916 SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0;
4917 SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0;
4918 SCTAB nTab1 = 0, nResTab = 0;
4919 SCSIZE nLenMajor = 0; // length of major direction
4920 bool bVertical = true; // whether to lookup vertically or horizontally
4922 if (nParamCount == 3)
4924 switch (GetStackType())
4926 case svDoubleRef:
4928 SCTAB nTabJunk;
4929 PopDoubleRef(nResCol1, nResRow1, nResTab,
4930 nResCol2, nResRow2, nTabJunk);
4931 if (nResTab != nTabJunk ||
4932 ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0))
4934 // The result array must be a vector.
4935 PushIllegalParameter();
4936 return;
4939 break;
4940 case svMatrix:
4942 pResMat = PopMatrix();
4943 if (!pResMat)
4945 PushIllegalParameter();
4946 return;
4948 SCSIZE nC, nR;
4949 pResMat->GetDimensions(nC, nR);
4950 if (nC != 1 && nR != 1)
4952 // Result matrix must be a vector.
4953 PushIllegalParameter();
4954 return;
4957 break;
4958 default:
4959 PushIllegalParameter();
4960 return;
4964 // Get the data-result range and also determine whether this is vertical
4965 // lookup or horizontal lookup.
4967 switch (GetStackType())
4969 case svDoubleRef:
4971 SCTAB nTabJunk;
4972 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk);
4973 if (nTab1 != nTabJunk)
4975 PushIllegalParameter();
4976 return;
4978 bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1);
4979 nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1;
4981 break;
4982 case svMatrix:
4984 pDataMat = PopMatrix();
4985 if (!pDataMat)
4987 PushIllegalParameter();
4988 return;
4991 SCSIZE nC, nR;
4992 pDataMat->GetDimensions(nC, nR);
4993 bVertical = (nR >= nC);
4994 nLenMajor = bVertical ? nR : nC;
4996 break;
4997 default:
4998 PushIllegalParameter();
4999 return;
5003 if (nGlobalError)
5005 PushIllegalParameter();
5006 return;
5009 // Get the lookup value.
5011 ScQueryParam aParam;
5012 ScQueryEntry& rEntry = aParam.GetEntry(0);
5013 if ( !FillEntry(rEntry) )
5014 return;
5016 // Now, perform the search to compute the delta position (nDelta).
5018 if (pDataMat)
5020 // Data array is given as a matrix.
5021 rEntry.bDoQuery = true;
5022 rEntry.eOp = SC_LESS_EQUAL;
5023 bool bFound = false;
5025 SCSIZE nC, nR;
5026 pDataMat->GetDimensions(nC, nR);
5028 // In case of non-vector matrix, only search the first row or column.
5029 ScMatrixRef pDataMat2;
5030 if (bVertical)
5032 ScMatrixRef pTempMat(new ScMatrix(1, nR));
5033 for (SCSIZE i = 0; i < nR; ++i)
5034 if (pDataMat->IsValue(0, i))
5035 pTempMat->PutDouble(pDataMat->GetDouble(0, i), 0, i);
5036 else
5037 pTempMat->PutString(pDataMat->GetString(0, i), 0, i);
5038 pDataMat2 = pTempMat;
5040 else
5042 ScMatrixRef pTempMat(new ScMatrix(nC, 1));
5043 for (SCSIZE i = 0; i < nC; ++i)
5044 if (pDataMat->IsValue(i, 0))
5045 pTempMat->PutDouble(pDataMat->GetDouble(i, 0), i, 0);
5046 else
5047 pTempMat->PutString(pDataMat->GetString(i, 0), i, 0);
5048 pDataMat2 = pTempMat;
5051 // binary search for non-equality mode (the source data is
5052 // assumed to be sorted in ascending order).
5054 SCCOLROW nDelta = -1;
5056 SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0;
5057 for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst)
5059 SCSIZE nMid = nFirst + nLen/2;
5060 sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, *pDataMat2, rEntry);
5061 if (nCmp == 0)
5063 // exact match. find the last item with the same value.
5064 lcl_GetLastMatch( nMid, *pDataMat2, nLenMajor, false);
5065 nDelta = nMid;
5066 bFound = true;
5067 break;
5070 if (nLen == 1) // first and last items are next to each other.
5072 nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1;
5073 // If already the 1st item is greater there's nothing found.
5074 bFound = (nDelta >= 0);
5075 break;
5078 if (nCmp < 0)
5079 nFirst = nMid;
5080 else
5081 nLast = nMid;
5084 if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item
5086 sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, *pDataMat2, rEntry);
5087 if (nCmp <= 0)
5089 // either the last item is an exact match or the real
5090 // hit is beyond the last item.
5091 nDelta += 1;
5092 bFound = true;
5095 else if (nDelta > 0) // valid hit must be 2nd item or higher
5097 // non-exact match
5098 bFound = true;
5101 if (!bFound)
5103 PushNA();
5104 return;
5107 // Now that we've found the delta, push the result back to the cell.
5109 if (pResMat)
5111 // result array is matrix.
5112 if (static_cast<SCSIZE>(nDelta) >= pResMat->GetElementCount())
5114 PushNA();
5115 return;
5117 if (pResMat->IsValue(nDelta))
5118 PushDouble(pResMat->GetDouble(nDelta));
5119 else
5120 PushString(pResMat->GetString(nDelta));
5122 else if (nParamCount == 3)
5124 // result array is cell range.
5125 ScAddress aAdr;
5126 aAdr.SetTab(nResTab);
5127 bool bResVertical = (nResRow2 - nResRow1) > 0;
5128 if (bResVertical)
5130 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
5131 if (nTempRow > MAXROW)
5133 PushDouble(0);
5134 return;
5136 aAdr.SetCol(nResCol1);
5137 aAdr.SetRow(nTempRow);
5139 else
5141 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
5142 if (nTempCol > MAXCOL)
5144 PushDouble(0);
5145 return;
5147 aAdr.SetCol(nTempCol);
5148 aAdr.SetRow(nResRow1);
5150 PushCellResultToken(true, aAdr, NULL, NULL);
5152 else
5154 // no result array. Use the data array to get the final value from.
5155 if (bVertical)
5157 if (pDataMat->IsValue(nC-1, nDelta))
5158 PushDouble(pDataMat->GetDouble(nC-1, nDelta));
5159 else
5160 PushString(pDataMat->GetString(nC-1, nDelta));
5162 else
5164 if (pDataMat->IsValue(nDelta, nR-1))
5165 PushDouble(pDataMat->GetDouble(nDelta, nR-1));
5166 else
5167 PushString(pDataMat->GetString(nDelta, nR-1));
5171 return;
5174 // Perform cell range search.
5176 aParam.nCol1 = nCol1;
5177 aParam.nRow1 = nRow1;
5178 aParam.nCol2 = bVertical ? nCol1 : nCol2;
5179 aParam.nRow2 = bVertical ? nRow2 : nRow1;
5180 aParam.bByRow = bVertical;
5181 aParam.bMixedComparison = true;
5183 rEntry.bDoQuery = TRUE;
5184 rEntry.eOp = SC_LESS_EQUAL;
5185 rEntry.nField = nCol1;
5186 if ( rEntry.bQueryByString )
5187 aParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok );
5189 ScQueryCellIterator aCellIter(pDok, nTab1, aParam, FALSE);
5190 SCCOL nC;
5191 SCROW nR;
5192 // Advance Entry.nField in iterator upon switching columns if
5193 // lookup in row.
5194 aCellIter.SetAdvanceQueryParamEntryField(!bVertical);
5195 if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) )
5197 PushNA();
5198 return;
5201 SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1);
5203 if (pResMat)
5205 // Use the matrix result array.
5206 if (pResMat->IsValue(nDelta))
5207 PushDouble(pResMat->GetDouble(nDelta));
5208 else
5209 PushString(pResMat->GetString(nDelta));
5211 else if (nParamCount == 3)
5213 // Use the result array vector. Note that the result array is assumed
5214 // to be a vector (i.e. 1-dimensinoal array).
5216 ScAddress aAdr;
5217 aAdr.SetTab(nResTab);
5218 bool bResVertical = (nResRow2 - nResRow1) > 0;
5219 if (bResVertical)
5221 SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta);
5222 if (nTempRow > MAXROW)
5224 PushDouble(0);
5225 return;
5227 aAdr.SetCol(nResCol1);
5228 aAdr.SetRow(nTempRow);
5230 else
5232 SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta);
5233 if (nTempCol > MAXCOL)
5235 PushDouble(0);
5236 return;
5238 aAdr.SetCol(nTempCol);
5239 aAdr.SetRow(nResRow1);
5241 PushCellResultToken(true, aAdr, NULL, NULL);
5243 else
5245 // Regardless of whether or not the result array exists, the last
5246 // array is always used as the "result" array.
5248 ScAddress aAdr;
5249 aAdr.SetTab(nTab1);
5250 if (bVertical)
5252 SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta);
5253 if (nTempRow > MAXROW)
5255 PushDouble(0);
5256 return;
5258 aAdr.SetCol(nCol2);
5259 aAdr.SetRow(nTempRow);
5261 else
5263 SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta);
5264 if (nTempCol > MAXCOL)
5266 PushDouble(0);
5267 return;
5269 aAdr.SetCol(nTempCol);
5270 aAdr.SetRow(nRow2);
5272 PushCellResultToken(true, aAdr, NULL, NULL);
5277 void ScInterpreter::ScHLookup()
5279 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScHLookup" );
5280 CalculateLookup(TRUE);
5282 void ScInterpreter::CalculateLookup(BOOL HLookup)
5284 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::CalculateLookup" );
5285 BYTE nParamCount = GetByte();
5286 if ( MustHaveParamCount( nParamCount, 3, 4 ) )
5288 BOOL bSorted;
5289 if (nParamCount == 4)
5290 bSorted = GetBool();
5291 else
5292 bSorted = TRUE;
5293 double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0;
5294 ScMatrixRef pMat = NULL;
5295 SCSIZE nC = 0, nR = 0;
5296 SCCOL nCol1 = 0;
5297 SCROW nRow1 = 0;
5298 SCTAB nTab1 = 0;
5299 SCCOL nCol2 = 0;
5300 SCROW nRow2 = 0;
5301 SCTAB nTab2;
5302 if (GetStackType() == svDoubleRef)
5304 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
5305 if (nTab1 != nTab2)
5307 PushIllegalParameter();
5308 return;
5311 else if (GetStackType() == svMatrix)
5313 pMat = PopMatrix();
5314 if (pMat)
5315 pMat->GetDimensions(nC, nR);
5316 else
5318 PushIllegalParameter();
5319 return;
5322 else
5324 PushIllegalParameter();
5325 return;
5327 if ( fIndex < 0.0 || (HLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) )
5329 PushIllegalArgument();
5330 return;
5332 SCROW nZIndex = static_cast<SCROW>(fIndex);
5333 SCCOL nSpIndex = static_cast<SCCOL>(fIndex);
5335 if (!pMat)
5337 nZIndex += nRow1; // Wertzeile
5338 nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column
5341 if (nGlobalError == 0)
5343 ScQueryParam rParam;
5344 rParam.nCol1 = nCol1;
5345 rParam.nRow1 = nRow1;
5346 if ( HLookup )
5348 rParam.nCol2 = nCol2;
5349 rParam.nRow2 = nRow1; // nur in der ersten Zeile suchen
5350 rParam.bByRow = FALSE;
5351 } // if ( HLookup )
5352 else
5354 rParam.nCol2 = nCol1; // nur in der ersten Spalte suchen
5355 rParam.nRow2 = nRow2;
5356 rParam.nTab = nTab1;
5358 rParam.bMixedComparison = TRUE;
5360 ScQueryEntry& rEntry = rParam.GetEntry(0);
5361 rEntry.bDoQuery = TRUE;
5362 if ( bSorted )
5363 rEntry.eOp = SC_LESS_EQUAL;
5364 if ( !FillEntry(rEntry) )
5365 return;
5366 if ( rEntry.bQueryByString )
5367 rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok );
5368 if (pMat)
5370 SCSIZE nMatCount = HLookup ? nC : nR;
5371 SCSIZE nDelta = SCSIZE_MAX;
5372 if (rEntry.bQueryByString)
5374 //!!!!!!!
5375 //! TODO: enable regex on matrix strings
5376 //!!!!!!!
5377 String aParamStr = *rEntry.pStr;
5378 if ( bSorted )
5380 for (SCSIZE i = 0; i < nMatCount; i++)
5382 if (HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i))
5384 sal_Int32 nRes =
5385 ScGlobal::pCollator->compareString( HLookup ? pMat->GetString(i,0) : pMat->GetString(0,i), aParamStr);
5386 if (nRes <= 0)
5387 nDelta = i;
5388 else if (i>0) // #i2168# ignore first mismatch
5389 i = nMatCount+1;
5391 else
5392 nDelta = i;
5395 else
5397 for (SCSIZE i = 0; i < nMatCount; i++)
5399 if (HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i))
5401 if ( ScGlobal::pTransliteration->isEqual(
5402 HLookup ? pMat->GetString(i,0) : pMat->GetString(0,i), aParamStr ) )
5404 nDelta = i;
5405 i = nMatCount + 1;
5411 else
5413 if ( bSorted )
5415 // #i2168# ignore strings
5416 for (SCSIZE i = 0; i < nMatCount; i++)
5418 if (!(HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i)))
5420 if ((HLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rEntry.nVal)
5421 nDelta = i;
5422 else
5423 i = nMatCount+1;
5427 else
5429 for (SCSIZE i = 0; i < nMatCount; i++)
5431 if (!(HLookup ? pMat->IsString(i, 0) : pMat->IsString(0, i)))
5433 if ((HLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) == rEntry.nVal)
5435 nDelta = i;
5436 i = nMatCount + 1;
5442 if ( nDelta != SCSIZE_MAX )
5444 SCSIZE nX = static_cast<SCSIZE>(nSpIndex);
5445 SCSIZE nY = nDelta;
5446 if ( HLookup )
5448 nX = nDelta;
5449 nY = static_cast<SCSIZE>(nZIndex);
5451 if ( pMat->IsString( nX, nY) )
5452 PushString(pMat->GetString( nX,nY));
5453 else
5454 PushDouble(pMat->GetDouble( nX,nY));
5456 else
5457 PushNA();
5459 else
5461 rEntry.nField = nCol1;
5462 BOOL bFound = FALSE;
5463 SCCOL nCol = 0;
5464 SCROW nRow = 0;
5465 if ( bSorted )
5466 rEntry.eOp = SC_LESS_EQUAL;
5467 if ( HLookup )
5469 ScQueryCellIterator aCellIter(pDok, nTab1, rParam, FALSE);
5470 // advance Entry.nField in Iterator upon switching columns
5471 aCellIter.SetAdvanceQueryParamEntryField( TRUE );
5472 if ( bSorted )
5474 SCROW nRow1_temp;
5475 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp );
5477 else if ( aCellIter.GetFirst() )
5479 bFound = TRUE;
5480 nCol = aCellIter.GetCol();
5482 nRow = nZIndex;
5483 } // if ( HLookup )
5484 else
5486 ScAddress aResultPos( nCol1, nRow1, nTab1);
5487 bFound = LookupQueryWithCache( aResultPos, rParam);
5488 nRow = aResultPos.Row();
5489 nCol = nSpIndex;
5491 if ( bFound )
5493 ScAddress aAdr( nCol, nRow, nTab1 );
5494 PushCellResultToken( true, aAdr, NULL, NULL);
5496 else
5497 PushNA();
5500 else
5501 PushIllegalParameter();
5505 bool ScInterpreter::FillEntry(ScQueryEntry& rEntry)
5507 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::FillEntry" );
5508 switch ( GetStackType() )
5510 case svDouble:
5512 rEntry.bQueryByString = FALSE;
5513 rEntry.nVal = GetDouble();
5515 break;
5516 case svString:
5518 const String sStr = GetString();
5519 rEntry.bQueryByString = TRUE;
5520 *rEntry.pStr = sStr;
5522 break;
5523 case svDoubleRef :
5524 case svSingleRef :
5526 ScAddress aAdr;
5527 if ( !PopDoubleRefOrSingleRef( aAdr ) )
5529 PushInt(0);
5530 return false;
5532 ScBaseCell* pCell = GetCell( aAdr );
5533 if (HasCellValueData(pCell))
5535 rEntry.bQueryByString = FALSE;
5536 rEntry.nVal = GetCellValue( aAdr, pCell );
5538 else
5540 if ( GetCellType( pCell ) == CELLTYPE_NOTE )
5542 rEntry.bQueryByString = FALSE;
5543 rEntry.nVal = 0.0;
5545 else
5547 String sStr;
5548 GetCellString(sStr, pCell);
5549 rEntry.bQueryByString = TRUE;
5550 *rEntry.pStr = sStr;
5554 break;
5555 case svMatrix :
5557 const ScMatValType nType = GetDoubleOrStringFromMatrix(rEntry.nVal, *rEntry.pStr);
5558 rEntry.bQueryByString = ScMatrix::IsNonValueType( nType);
5560 break;
5561 default:
5563 PushIllegalParameter();
5564 return false;
5566 } // switch ( GetStackType() )
5567 return true;
5569 void ScInterpreter::ScVLookup()
5571 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScVLookup" );
5572 CalculateLookup(FALSE);
5575 #if defined(WIN) && defined(MSC)
5576 #pragma optimize("",off)
5577 #endif
5579 void ScInterpreter::ScSubTotal()
5581 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSubTotal" );
5582 BYTE nParamCount = GetByte();
5583 if ( MustHaveParamCountMin( nParamCount, 2 ) )
5585 // We must fish the 1st parameter deep from the stack! And push it on top.
5586 const FormulaToken* p = pStack[ sp - nParamCount ];
5587 PushTempToken( *p );
5588 int nFunc = (int) ::rtl::math::approxFloor( GetDouble() );
5589 if( nFunc < 1 || nFunc > 11 )
5590 PushIllegalArgument(); // simulate return on stack, not SetError(...)
5591 else
5593 cPar = nParamCount - 1;
5594 glSubTotal = TRUE;
5595 switch( nFunc )
5597 case SUBTOTAL_FUNC_AVE : ScAverage(); break;
5598 case SUBTOTAL_FUNC_CNT : ScCount(); break;
5599 case SUBTOTAL_FUNC_CNT2 : ScCount2(); break;
5600 case SUBTOTAL_FUNC_MAX : ScMax(); break;
5601 case SUBTOTAL_FUNC_MIN : ScMin(); break;
5602 case SUBTOTAL_FUNC_PROD : ScProduct(); break;
5603 case SUBTOTAL_FUNC_STD : ScStDev(); break;
5604 case SUBTOTAL_FUNC_STDP : ScStDevP(); break;
5605 case SUBTOTAL_FUNC_SUM : ScSum(); break;
5606 case SUBTOTAL_FUNC_VAR : ScVar(); break;
5607 case SUBTOTAL_FUNC_VARP : ScVarP(); break;
5608 default : PushIllegalArgument(); break;
5610 glSubTotal = FALSE;
5612 // Get rid of the 1st (fished) parameter.
5613 double nVal = GetDouble();
5614 Pop();
5615 PushDouble( nVal );
5618 #if defined(WIN) && defined(MSC)
5619 #pragma optimize("",on)
5620 #endif
5623 BOOL ScInterpreter::GetDBParams(SCTAB& rTab, ScQueryParam& rParam,
5624 BOOL& rMissingField )
5626 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::GetDBParams" );
5627 BOOL bRet = FALSE;
5628 BOOL bAllowMissingField = FALSE;
5629 if ( rMissingField )
5631 bAllowMissingField = TRUE;
5632 rMissingField = FALSE;
5634 if ( GetByte() == 3 )
5637 SCCOL nQCol1;
5638 SCROW nQRow1;
5639 SCTAB nQTab1;
5640 SCCOL nQCol2;
5641 SCROW nQRow2;
5642 SCTAB nQTab2;
5643 PopDoubleRef(nQCol1, nQRow1, nQTab1, nQCol2, nQRow2, nQTab2);
5645 BOOL bByVal = TRUE;
5646 double nVal = 0.0;
5647 String aStr;
5648 ScRange aMissingRange;
5649 BOOL bRangeFake = FALSE;
5650 switch (GetStackType())
5652 case svDouble :
5653 nVal = ::rtl::math::approxFloor( GetDouble() );
5654 if ( bAllowMissingField && nVal == 0.0 )
5655 rMissingField = TRUE; // fake missing parameter
5656 break;
5657 case svString :
5658 bByVal = FALSE;
5659 aStr = GetString();
5660 break;
5661 case svSingleRef :
5663 ScAddress aAdr;
5664 PopSingleRef( aAdr );
5665 ScBaseCell* pCell = GetCell( aAdr );
5666 if (HasCellValueData(pCell))
5667 nVal = GetCellValue( aAdr, pCell );
5668 else
5670 bByVal = FALSE;
5671 GetCellString(aStr, pCell);
5674 break;
5675 case svDoubleRef :
5676 if ( bAllowMissingField )
5677 { // fake missing parameter for old SO compatibility
5678 bRangeFake = TRUE;
5679 PopDoubleRef( aMissingRange );
5681 else
5683 PopError();
5684 SetError( errIllegalParameter );
5686 break;
5687 case svMissing :
5688 PopError();
5689 if ( bAllowMissingField )
5690 rMissingField = TRUE;
5691 else
5692 SetError( errIllegalParameter );
5693 break;
5694 default:
5695 PopError();
5696 SetError( errIllegalParameter );
5699 SCCOL nDBCol1;
5700 SCROW nDBRow1;
5701 SCTAB nDBTab1;
5702 SCCOL nDBCol2;
5703 SCROW nDBRow2;
5704 SCTAB nDBTab2;
5705 PopDoubleRef(nDBCol1, nDBRow1, nDBTab1, nDBCol2, nDBRow2, nDBTab2);
5707 if ( nGlobalError == 0 && bRangeFake )
5709 // range parameter must match entire database range
5710 if ( aMissingRange == ScRange( nDBCol1, nDBRow1, nDBTab1, nDBCol2,
5711 nDBRow2, nDBTab2) )
5712 rMissingField = TRUE;
5713 else
5714 SetError( errIllegalParameter );
5717 if (nGlobalError == 0)
5719 SCCOL nField = nDBCol1;
5720 BOOL bFound = TRUE;
5721 if ( rMissingField )
5722 ; // special case
5723 else if ( bByVal )
5725 if ( nVal <= 0 || nVal > (nDBCol2 - nDBCol1 + 1) )
5726 bFound = FALSE;
5727 else
5728 nField = Min(nDBCol2, (SCCOL)(nDBCol1 + (SCCOL)nVal - 1));
5730 else
5732 bFound = FALSE;
5733 String aCellStr;
5734 ScAddress aLook( nDBCol1, nDBRow1, nDBTab1 );
5735 while (!bFound && (aLook.Col() <= nDBCol2))
5737 ScBaseCell* pCell = GetCell( aLook );
5738 GetCellString( aCellStr, pCell );
5739 bFound = ScGlobal::pTransliteration->isEqual( aCellStr, aStr );
5740 if (!bFound)
5741 aLook.IncCol();
5743 nField = aLook.Col();
5745 if (bFound)
5747 rParam.nCol1 = nDBCol1;
5748 rParam.nRow1 = nDBRow1;
5749 rParam.nCol2 = nDBCol2;
5750 rParam.nRow2 = nDBRow2;
5751 rParam.nTab = nDBTab1;
5752 rParam.bHasHeader = TRUE;
5753 rParam.bByRow = TRUE;
5754 rParam.bInplace = TRUE;
5755 rParam.bCaseSens = FALSE;
5756 rParam.bRegExp = FALSE;
5757 rParam.bDuplicate = TRUE;
5758 if (pDok->CreateQueryParam(nQCol1, nQRow1, nQCol2, nQRow2, nQTab1, rParam))
5760 // An allowed missing field parameter sets the result field
5761 // to any of the query fields, just to be able to return
5762 // some cell from the iterator.
5763 if ( rMissingField )
5764 nField = static_cast<SCCOL>(rParam.GetEntry(0).nField);
5766 rParam.nCol1 = nField;
5767 rParam.nCol2 = nField;
5768 rTab = nDBTab1;
5769 bRet = TRUE;
5770 SCSIZE nCount = rParam.GetEntryCount();
5771 for ( SCSIZE i=0; i < nCount; i++ )
5773 ScQueryEntry& rEntry = rParam.GetEntry(i);
5774 if ( rEntry.bDoQuery )
5776 sal_uInt32 nIndex = 0;
5777 rEntry.bQueryByString = !pFormatter->IsNumberFormat(
5778 *rEntry.pStr, nIndex, rEntry.nVal );
5779 if ( rEntry.bQueryByString && !rParam.bRegExp )
5780 rParam.bRegExp = MayBeRegExp( *rEntry.pStr, pDok );
5782 else
5783 break; // for
5789 return bRet;
5793 void ScInterpreter::DBIterator( ScIterFunc eFunc )
5795 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::DBIterator" );
5796 SCTAB nTab1;
5797 double nErg = 0.0;
5798 double fMem = 0.0;
5799 BOOL bNull = TRUE;
5800 ULONG nCount = 0;
5801 ScQueryParam aQueryParam;
5802 BOOL bMissingField = FALSE;
5803 if ( GetDBParams( nTab1, aQueryParam, bMissingField) )
5805 double nVal;
5806 USHORT nErr;
5807 ScQueryValueIterator aValIter(pDok, nTab1, aQueryParam);
5808 if ( aValIter.GetFirst(nVal, nErr) && !nErr )
5810 switch( eFunc )
5812 case ifPRODUCT: nErg = 1; break;
5813 case ifMAX: nErg = -MAXDOUBLE; break;
5814 case ifMIN: nErg = MAXDOUBLE; break;
5815 default: ; // nothing
5819 nCount++;
5820 switch( eFunc )
5822 case ifAVERAGE:
5823 case ifSUM:
5824 if ( bNull && nVal != 0.0 )
5826 bNull = FALSE;
5827 fMem = nVal;
5829 else
5830 nErg += nVal;
5831 break;
5832 case ifSUMSQ: nErg += nVal * nVal; break;
5833 case ifPRODUCT: nErg *= nVal; break;
5834 case ifMAX: if( nVal > nErg ) nErg = nVal; break;
5835 case ifMIN: if( nVal < nErg ) nErg = nVal; break;
5836 default: ; // nothing
5839 while ( aValIter.GetNext(nVal, nErr) && !nErr );
5841 SetError(nErr);
5843 else
5844 SetError( errIllegalParameter);
5845 switch( eFunc )
5847 case ifCOUNT: nErg = nCount; break;
5848 case ifSUM: nErg = ::rtl::math::approxAdd( nErg, fMem ); break;
5849 case ifAVERAGE: nErg = ::rtl::math::approxAdd( nErg, fMem ) / nCount; break;
5850 default: ; // nothing
5852 PushDouble( nErg );
5856 void ScInterpreter::ScDBSum()
5858 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBSum" );
5859 DBIterator( ifSUM );
5863 void ScInterpreter::ScDBCount()
5865 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBCount" );
5866 SCTAB nTab;
5867 ScQueryParam aQueryParam;
5868 BOOL bMissingField = TRUE;
5869 if ( GetDBParams( nTab, aQueryParam, bMissingField) )
5871 ULONG nCount = 0;
5872 if ( bMissingField )
5873 { // count all matching records
5874 // TODO: currently the QueryIterators only return cell pointers of
5875 // existing cells, so if a query matches an empty cell there's
5876 // nothing returned, and therefor not counted!
5877 // Since this has ever been the case and this code here only came
5878 // into existance to fix #i6899 and it never worked before we'll
5879 // have to live with it until we reimplement the iterators to also
5880 // return empty cells, which would mean to adapt all callers of
5881 // iterators.
5882 ScQueryCellIterator aCellIter( pDok, nTab, aQueryParam);
5883 if ( aCellIter.GetFirst() )
5887 nCount++;
5888 } while ( aCellIter.GetNext() );
5891 else
5892 { // count only matching records with a value in the "result" field
5893 double nVal;
5894 USHORT nErr = 0;
5895 ScQueryValueIterator aValIter( pDok, nTab, aQueryParam);
5896 if ( aValIter.GetFirst( nVal, nErr) && !nErr )
5900 nCount++;
5901 } while ( aValIter.GetNext( nVal, nErr) && !nErr );
5903 SetError( nErr );
5905 PushDouble( nCount );
5907 else
5908 PushIllegalParameter();
5912 void ScInterpreter::ScDBCount2()
5914 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBCount2" );
5915 SCTAB nTab;
5916 ScQueryParam aQueryParam;
5917 BOOL bMissingField = TRUE;
5918 if (GetDBParams( nTab, aQueryParam, bMissingField))
5920 ULONG nCount = 0;
5921 ScQueryCellIterator aCellIter(pDok, nTab, aQueryParam);
5922 if ( aCellIter.GetFirst() )
5926 nCount++;
5927 } while ( aCellIter.GetNext() );
5929 PushDouble(nCount);
5931 else
5932 PushIllegalParameter();
5936 void ScInterpreter::ScDBAverage()
5938 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBAverage" );
5939 DBIterator( ifAVERAGE );
5943 void ScInterpreter::ScDBMax()
5945 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBMax" );
5946 DBIterator( ifMAX );
5950 void ScInterpreter::ScDBMin()
5952 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBMin" );
5953 DBIterator( ifMIN );
5957 void ScInterpreter::ScDBProduct()
5959 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBProduct" );
5960 DBIterator( ifPRODUCT );
5964 void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount )
5966 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::GetDBStVarParams" );
5967 std::vector<double> values;
5968 double vSum = 0.0;
5969 double vMean = 0.0;
5971 rValCount = 0.0;
5972 double fSum = 0.0;
5973 SCTAB nTab;
5974 ScQueryParam aQueryParam;
5975 BOOL bMissingField = FALSE;
5976 if (GetDBParams( nTab, aQueryParam, bMissingField))
5978 double fVal;
5979 USHORT nErr;
5980 ScQueryValueIterator aValIter(pDok, nTab, aQueryParam);
5981 if (aValIter.GetFirst(fVal, nErr) && !nErr)
5985 rValCount++;
5986 values.push_back(fVal);
5987 fSum += fVal;
5989 while ((nErr == 0) && aValIter.GetNext(fVal, nErr));
5991 SetError(nErr);
5993 else
5994 SetError( errIllegalParameter);
5996 vMean = fSum / values.size();
5998 for (size_t i = 0; i < values.size(); i++)
5999 vSum += (values[i] - vMean) * (values[i] - vMean);
6001 rVal = vSum;
6005 void ScInterpreter::ScDBStdDev()
6007 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBStdDev" );
6008 double fVal, fCount;
6009 GetDBStVarParams( fVal, fCount );
6010 PushDouble( sqrt(fVal/(fCount-1)));
6014 void ScInterpreter::ScDBStdDevP()
6016 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBStdDevP" );
6017 double fVal, fCount;
6018 GetDBStVarParams( fVal, fCount );
6019 PushDouble( sqrt(fVal/fCount));
6023 void ScInterpreter::ScDBVar()
6025 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBVar" );
6026 double fVal, fCount;
6027 GetDBStVarParams( fVal, fCount );
6028 PushDouble(fVal/(fCount-1));
6032 void ScInterpreter::ScDBVarP()
6034 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScDBVarP" );
6035 double fVal, fCount;
6036 GetDBStVarParams( fVal, fCount );
6037 PushDouble(fVal/fCount);
6041 ScTokenArray* lcl_CreateExternalRefTokenArray( const ScAddress& rPos, ScDocument* pDoc,
6042 const ScAddress::ExternalInfo& rExtInfo, const ScRefAddress& rRefAd1,
6043 const ScRefAddress* pRefAd2 )
6045 ScExternalRefManager* pRefMgr = pDoc->GetExternalRefManager();
6046 size_t nSheets = 1;
6047 const String* pRealTab = pRefMgr->getRealTableName( rExtInfo.mnFileId, rExtInfo.maTabName);
6048 ScTokenArray* pTokenArray = new ScTokenArray;
6049 if (pRefAd2)
6051 ScComplexRefData aRef;
6052 aRef.InitRangeRel( ScRange( rRefAd1.GetAddress(), pRefAd2->GetAddress()), rPos);
6053 aRef.Ref1.SetColRel( rRefAd1.IsRelCol());
6054 aRef.Ref1.SetRowRel( rRefAd1.IsRelRow());
6055 aRef.Ref1.SetTabRel( rRefAd1.IsRelTab());
6056 aRef.Ref1.SetFlag3D( true);
6057 aRef.Ref2.SetColRel( pRefAd2->IsRelCol());
6058 aRef.Ref2.SetRowRel( pRefAd2->IsRelRow());
6059 aRef.Ref2.SetTabRel( pRefAd2->IsRelTab());
6060 nSheets = aRef.Ref2.nTab - aRef.Ref1.nTab + 1;
6061 aRef.Ref2.SetFlag3D( nSheets > 1 );
6062 pTokenArray->AddExternalDoubleReference( rExtInfo.mnFileId,
6063 (pRealTab ? *pRealTab : rExtInfo.maTabName), aRef);
6065 else
6067 ScSingleRefData aRef;
6068 aRef.InitAddressRel( rRefAd1.GetAddress(), rPos);
6069 aRef.SetColRel( rRefAd1.IsRelCol());
6070 aRef.SetRowRel( rRefAd1.IsRelRow());
6071 aRef.SetTabRel( rRefAd1.IsRelTab());
6072 aRef.SetFlag3D( true);
6073 pTokenArray->AddExternalSingleReference( rExtInfo.mnFileId,
6074 (pRealTab ? *pRealTab : rExtInfo.maTabName), aRef);
6076 // The indirect usage of the external table can't be detected during the
6077 // store-to-file cycle, mark it as permanently referenced so it gets stored
6078 // even if not directly referenced anywhere.
6079 pRefMgr->setCacheTableReferencedPermanently( rExtInfo.mnFileId,
6080 rExtInfo.maTabName, nSheets);
6081 ScCompiler aComp( pDoc, rPos, *pTokenArray);
6082 aComp.CompileTokenArray();
6083 return pTokenArray;
6087 void ScInterpreter::ScIndirect()
6089 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIndirect" );
6090 BYTE nParamCount = GetByte();
6091 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
6093 bool bTryXlA1 = true; // whether to try XL_A1 style as well.
6094 FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO;
6095 if (nParamCount == 2 && 0.0 == ::rtl::math::approxFloor( GetDouble()))
6097 eConv = FormulaGrammar::CONV_XL_R1C1;
6098 bTryXlA1 = false;
6100 const ScAddress::Details aDetails( eConv, aPos );
6101 const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos );
6102 SCTAB nTab = aPos.Tab();
6103 String sRefStr( GetString() );
6104 ScRefAddress aRefAd, aRefAd2;
6105 ScAddress::ExternalInfo aExtInfo;
6106 if ( ConvertDoubleRef( pDok, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) ||
6107 (bTryXlA1 && ConvertDoubleRef( pDok, sRefStr, nTab, aRefAd,
6108 aRefAd2, aDetailsXlA1, &aExtInfo)))
6110 if (aExtInfo.mbExternal)
6112 /* TODO: future versions should implement a proper subroutine
6113 * token. This procedure here is a minimally invasive fix for
6114 * #i101645# in OOo3.1.1 */
6115 // Push a subroutine on the instruction code stack that
6116 // resolves the external reference as the next instruction.
6117 aCode.Push( lcl_CreateExternalRefTokenArray( aPos, pDok,
6118 aExtInfo, aRefAd, &aRefAd2));
6119 // Signal subroutine call to interpreter.
6120 PushTempToken( new FormulaUnknownToken( ocCall));
6122 else
6123 PushDoubleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(),
6124 aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab() );
6126 else if ( ConvertSingleRef ( pDok, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) ||
6127 (bTryXlA1 && ConvertSingleRef ( pDok, sRefStr, nTab, aRefAd,
6128 aDetailsXlA1, &aExtInfo)))
6130 if (aExtInfo.mbExternal)
6132 /* TODO: future versions should implement a proper subroutine
6133 * token. This procedure here is a minimally invasive fix for
6134 * #i101645# in OOo3.1.1 */
6135 // Push a subroutine on the instruction code stack that
6136 // resolves the external reference as the next instruction.
6137 aCode.Push( lcl_CreateExternalRefTokenArray( aPos, pDok,
6138 aExtInfo, aRefAd, NULL));
6139 // Signal subroutine call to interpreter.
6140 PushTempToken( new FormulaUnknownToken( ocCall));
6142 else
6143 PushSingleRef( aRefAd.Col(), aRefAd.Row(), aRefAd.Tab() );
6145 else
6149 ScRangeName* pNames = pDok->GetRangeName();
6150 if (!pNames)
6151 break;
6153 USHORT nPos = 0;
6154 if (!pNames->SearchName( sRefStr, nPos))
6155 break;
6157 ScRangeData* rData = (*pNames)[nPos];
6158 if (!rData)
6159 break;
6161 // We need this in order to obtain a good range.
6162 rData->ValidateTabRefs();
6164 ScRange aRange;
6165 #if 0
6166 // This is some really odd Excel behavior and renders named
6167 // ranges containing relative references totally useless.
6168 if (!rData->IsReference(aRange, ScAddress( aPos.Tab(), 0, 0)))
6169 break;
6170 #else
6171 // This is the usual way to treat named ranges containing
6172 // relative references.
6173 if (!rData->IsReference( aRange, aPos))
6174 break;
6175 #endif
6177 if (aRange.aStart == aRange.aEnd)
6178 PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(),
6179 aRange.aStart.Tab());
6180 else
6181 PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(),
6182 aRange.aStart.Tab(), aRange.aEnd.Col(),
6183 aRange.aEnd.Row(), aRange.aEnd.Tab());
6185 // success!
6186 return;
6188 while (false);
6190 PushIllegalArgument();
6196 void ScInterpreter::ScAddressFunc()
6198 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScAddressFunc" );
6199 String sTabStr;
6201 BYTE nParamCount = GetByte();
6202 if( !MustHaveParamCount( nParamCount, 2, 5 ) )
6203 return;
6205 if( nParamCount >= 5 )
6206 sTabStr = GetString();
6208 FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default
6209 if( nParamCount >= 4 && 0.0 == ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0)))
6210 eConv = FormulaGrammar::CONV_XL_R1C1;
6212 USHORT nFlags = SCA_COL_ABSOLUTE | SCA_ROW_ABSOLUTE; // default
6213 if( nParamCount >= 3 )
6215 USHORT n = (USHORT) ::rtl::math::approxFloor( GetDoubleWithDefault( 1.0));
6216 switch ( n )
6218 default :
6219 PushNoValue();
6220 return;
6222 case 5:
6223 case 1 : break; // default
6224 case 6:
6225 case 2 : nFlags = SCA_ROW_ABSOLUTE; break;
6226 case 7:
6227 case 3 : nFlags = SCA_COL_ABSOLUTE; break;
6228 case 8:
6229 case 4 : nFlags = 0; break; // both relative
6232 nFlags |= SCA_VALID | SCA_VALID_ROW | SCA_VALID_COL;
6234 SCCOL nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble());
6235 SCROW nRow = (SCROW) ::rtl::math::approxFloor(GetDouble());
6236 if( eConv == FormulaGrammar::CONV_XL_R1C1 )
6238 // YUCK! The XL interface actually treats rel R1C1 refs differently
6239 // than A1
6240 if( !(nFlags & SCA_COL_ABSOLUTE) )
6241 nCol += aPos.Col() + 1;
6242 if( !(nFlags & SCA_ROW_ABSOLUTE) )
6243 nRow += aPos.Row() + 1;
6246 --nCol;
6247 --nRow;
6248 if(!ValidCol( nCol) || !ValidRow( nRow))
6250 PushIllegalArgument();
6251 return;
6254 String aRefStr;
6255 const ScAddress::Details aDetails( eConv, aPos );
6256 const ScAddress aAdr( nCol, nRow, 0);
6257 aAdr.Format( aRefStr, nFlags, pDok, aDetails );
6259 if( nParamCount >= 5 )
6261 ScCompiler::CheckTabQuotes( sTabStr, eConv);
6262 sTabStr += static_cast<sal_Unicode>(eConv == FormulaGrammar::CONV_XL_R1C1 ? '!' : '.');
6263 sTabStr += aRefStr;
6264 PushString( sTabStr );
6266 else
6267 PushString( aRefStr );
6271 void ScInterpreter::ScOffset()
6273 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScOffset" );
6274 BYTE nParamCount = GetByte();
6275 if ( MustHaveParamCount( nParamCount, 3, 5 ) )
6277 long nColNew = -1, nRowNew = -1, nColPlus, nRowPlus;
6278 if (nParamCount == 5)
6279 nColNew = (long) ::rtl::math::approxFloor(GetDouble());
6280 if (nParamCount >= 4)
6281 nRowNew = (long) ::rtl::math::approxFloor(GetDoubleWithDefault( -1.0 ));
6282 nColPlus = (long) ::rtl::math::approxFloor(GetDouble());
6283 nRowPlus = (long) ::rtl::math::approxFloor(GetDouble());
6284 SCCOL nCol1;
6285 SCROW nRow1;
6286 SCTAB nTab1;
6287 SCCOL nCol2;
6288 SCROW nRow2;
6289 SCTAB nTab2;
6290 if (nColNew == 0 || nRowNew == 0)
6292 PushIllegalArgument();
6293 return;
6295 if (GetStackType() == svSingleRef)
6297 PopSingleRef(nCol1, nRow1, nTab1);
6298 if (nParamCount == 3 || (nColNew < 0 && nRowNew < 0))
6300 nCol1 = (SCCOL)((long) nCol1 + nColPlus);
6301 nRow1 = (SCROW)((long) nRow1 + nRowPlus);
6302 if (!ValidCol(nCol1) || !ValidRow(nRow1))
6303 PushIllegalArgument();
6304 else
6305 PushSingleRef(nCol1, nRow1, nTab1);
6307 else
6309 if (nColNew < 0)
6310 nColNew = 1;
6311 if (nRowNew < 0)
6312 nRowNew = 1;
6313 nCol1 = (SCCOL)((long)nCol1+nColPlus); // ! nCol1 wird veraendert!
6314 nRow1 = (SCROW)((long)nRow1+nRowPlus);
6315 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
6316 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
6317 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
6318 !ValidCol(nCol2) || !ValidRow(nRow2))
6319 PushIllegalArgument();
6320 else
6321 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
6324 else if (GetStackType() == svDoubleRef)
6326 PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6327 if (nColNew < 0)
6328 nColNew = nCol2 - nCol1 + 1;
6329 if (nRowNew < 0)
6330 nRowNew = nRow2 - nRow1 + 1;
6331 nCol1 = (SCCOL)((long)nCol1+nColPlus);
6332 nRow1 = (SCROW)((long)nRow1+nRowPlus);
6333 nCol2 = (SCCOL)((long)nCol1+nColNew-1);
6334 nRow2 = (SCROW)((long)nRow1+nRowNew-1);
6335 if (!ValidCol(nCol1) || !ValidRow(nRow1) ||
6336 !ValidCol(nCol2) || !ValidRow(nRow2) || nTab1 != nTab2)
6337 PushIllegalArgument();
6338 else
6339 PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1);
6341 else
6342 PushIllegalParameter();
6347 void ScInterpreter::ScIndex()
6349 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScIndex" );
6350 BYTE nParamCount = GetByte();
6351 if ( MustHaveParamCount( nParamCount, 1, 4 ) )
6353 long nArea;
6354 size_t nAreaCount;
6355 SCCOL nCol;
6356 SCROW nRow;
6357 if (nParamCount == 4)
6358 nArea = (long) ::rtl::math::approxFloor(GetDouble());
6359 else
6360 nArea = 1;
6361 if (nParamCount >= 3)
6362 nCol = (SCCOL) ::rtl::math::approxFloor(GetDouble());
6363 else
6364 nCol = 0;
6365 if (nParamCount >= 2)
6366 nRow = (SCROW) ::rtl::math::approxFloor(GetDouble());
6367 else
6368 nRow = 0;
6369 if (GetStackType() == svRefList)
6370 nAreaCount = (sp ? static_cast<ScToken*>(pStack[sp-1])->GetRefList()->size() : 0);
6371 else
6372 nAreaCount = 1; // one reference or array or whatever
6373 if (nAreaCount == 0 || (size_t)nArea > nAreaCount)
6375 PushError( errNoRef);
6376 return;
6378 else if (nArea < 1 || nCol < 0 || nRow < 0)
6380 PushIllegalArgument();
6381 return;
6383 switch (GetStackType())
6385 case svMatrix:
6387 if (nArea != 1)
6388 SetError(errIllegalArgument);
6389 USHORT nOldSp = sp;
6390 ScMatrixRef pMat = GetMatrix();
6391 if (pMat)
6393 SCSIZE nC, nR;
6394 pMat->GetDimensions(nC, nR);
6395 // Access one element of a vector independent of col/row
6396 // orientation?
6397 bool bVector = ((nCol == 0 || nRow == 0) && (nC == 1 || nR == 1));
6398 SCSIZE nElement = ::std::max( static_cast<SCSIZE>(nCol),
6399 static_cast<SCSIZE>(nRow));
6400 if (nC == 0 || nR == 0 ||
6401 (!bVector && (static_cast<SCSIZE>(nCol) > nC ||
6402 static_cast<SCSIZE>(nRow) > nR)) ||
6403 (bVector && nElement > nC * nR))
6404 PushIllegalArgument();
6405 else if (nCol == 0 && nRow == 0)
6406 sp = nOldSp;
6407 else if (bVector)
6409 --nElement;
6410 if (pMat->IsString( nElement))
6411 PushString( pMat->GetString( nElement));
6412 else
6413 PushDouble( pMat->GetDouble( nElement));
6415 else if (nCol == 0)
6417 ScMatrixRef pResMat = GetNewMat(nC, 1);
6418 if (pResMat)
6420 SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1);
6421 for (SCSIZE i = 0; i < nC; i++)
6422 if (!pMat->IsString(i, nRowMinus1))
6423 pResMat->PutDouble(pMat->GetDouble(i,
6424 nRowMinus1), i, 0);
6425 else
6426 pResMat->PutString(pMat->GetString(i,
6427 nRowMinus1), i, 0);
6428 PushMatrix(pResMat);
6430 else
6431 PushIllegalArgument();
6433 else if (nRow == 0)
6435 ScMatrixRef pResMat = GetNewMat(1, nR);
6436 if (pResMat)
6438 SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1);
6439 for (SCSIZE i = 0; i < nR; i++)
6440 if (!pMat->IsString(nColMinus1, i))
6441 pResMat->PutDouble(pMat->GetDouble(nColMinus1,
6442 i), i);
6443 else
6444 pResMat->PutString(pMat->GetString(nColMinus1,
6445 i), i);
6446 PushMatrix(pResMat);
6448 else
6449 PushIllegalArgument();
6451 else
6453 if (!pMat->IsString( static_cast<SCSIZE>(nCol-1),
6454 static_cast<SCSIZE>(nRow-1)))
6455 PushDouble( pMat->GetDouble(
6456 static_cast<SCSIZE>(nCol-1),
6457 static_cast<SCSIZE>(nRow-1)));
6458 else
6459 PushString( pMat->GetString(
6460 static_cast<SCSIZE>(nCol-1),
6461 static_cast<SCSIZE>(nRow-1)));
6465 break;
6466 case svSingleRef:
6468 SCCOL nCol1 = 0;
6469 SCROW nRow1 = 0;
6470 SCTAB nTab1 = 0;
6471 PopSingleRef( nCol1, nRow1, nTab1);
6472 if (nCol > 1 || nRow > 1)
6473 PushIllegalArgument();
6474 else
6475 PushSingleRef( nCol1, nRow1, nTab1);
6477 break;
6478 case svDoubleRef:
6479 case svRefList:
6481 SCCOL nCol1 = 0;
6482 SCROW nRow1 = 0;
6483 SCTAB nTab1 = 0;
6484 SCCOL nCol2 = 0;
6485 SCROW nRow2 = 0;
6486 SCTAB nTab2 = 0;
6487 BOOL bRowArray = FALSE;
6488 if (GetStackType() == svRefList)
6490 FormulaTokenRef xRef = PopToken();
6491 if (nGlobalError || !xRef)
6493 PushIllegalParameter();
6494 return;
6496 ScRange aRange( ScAddress::UNINITIALIZED);
6497 DoubleRefToRange( (*(static_cast<ScToken*>(xRef.get())->GetRefList()))[nArea-1], aRange);
6498 aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6499 if ( nParamCount == 2 && nRow1 == nRow2 )
6500 bRowArray = TRUE;
6502 else
6504 PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2);
6505 if ( nParamCount == 2 && nRow1 == nRow2 )
6506 bRowArray = TRUE;
6508 if ( nTab1 != nTab2 ||
6509 (nCol > 0 && nCol1+nCol-1 > nCol2) ||
6510 (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) ||
6511 ( nRow > nCol2 - nCol1 + 1 && bRowArray ))
6512 PushIllegalArgument();
6513 else if (nCol == 0 && nRow == 0)
6515 if ( nCol1 == nCol2 && nRow1 == nRow2 )
6516 PushSingleRef( nCol1, nRow1, nTab1 );
6517 else
6518 PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 );
6520 else if (nRow == 0)
6522 if ( nRow1 == nRow2 )
6523 PushSingleRef( nCol1+nCol-1, nRow1, nTab1 );
6524 else
6525 PushDoubleRef( nCol1+nCol-1, nRow1, nTab1,
6526 nCol1+nCol-1, nRow2, nTab1 );
6528 else if (nCol == 0)
6530 if ( nCol1 == nCol2 )
6531 PushSingleRef( nCol1, nRow1+nRow-1, nTab1 );
6532 else if ( bRowArray )
6534 nCol =(SCCOL) nRow;
6535 nRow = 1;
6536 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
6538 else
6539 PushDoubleRef( nCol1, nRow1+nRow-1, nTab1,
6540 nCol2, nRow1+nRow-1, nTab1);
6542 else
6543 PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1);
6545 break;
6546 default:
6547 PushIllegalParameter();
6553 void ScInterpreter::ScMultiArea()
6555 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScMultiArea" );
6556 // Legacy support, convert to RefList
6557 BYTE nParamCount = GetByte();
6558 if (MustHaveParamCountMin( nParamCount, 1))
6560 while (!nGlobalError && nParamCount-- > 1)
6562 ScUnionFunc();
6568 void ScInterpreter::ScAreas()
6570 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScAreas" );
6571 BYTE nParamCount = GetByte();
6572 if (MustHaveParamCount( nParamCount, 1))
6574 size_t nCount = 0;
6575 switch (GetStackType())
6577 case svSingleRef:
6579 FormulaTokenRef xT = PopToken();
6580 ValidateRef( static_cast<ScToken*>(xT.get())->GetSingleRef());
6581 ++nCount;
6583 break;
6584 case svDoubleRef:
6586 FormulaTokenRef xT = PopToken();
6587 ValidateRef( static_cast<ScToken*>(xT.get())->GetDoubleRef());
6588 ++nCount;
6590 break;
6591 case svRefList:
6593 FormulaTokenRef xT = PopToken();
6594 ValidateRef( *(static_cast<ScToken*>(xT.get())->GetRefList()));
6595 nCount += static_cast<ScToken*>(xT.get())->GetRefList()->size();
6597 break;
6598 default:
6599 SetError( errIllegalParameter);
6601 PushDouble( double(nCount));
6606 void ScInterpreter::ScCurrency()
6608 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScCurrency" );
6609 BYTE nParamCount = GetByte();
6610 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
6612 String aStr;
6613 double fDec;
6614 if (nParamCount == 2)
6616 fDec = ::rtl::math::approxFloor(GetDouble());
6617 if (fDec < -15.0 || fDec > 15.0)
6619 PushIllegalArgument();
6620 return;
6623 else
6624 fDec = 2.0;
6625 double fVal = GetDouble();
6626 double fFac;
6627 if ( fDec != 0.0 )
6628 fFac = pow( (double)10, fDec );
6629 else
6630 fFac = 1.0;
6631 if (fVal < 0.0)
6632 fVal = ceil(fVal*fFac-0.5)/fFac;
6633 else
6634 fVal = floor(fVal*fFac+0.5)/fFac;
6635 Color* pColor = NULL;
6636 if ( fDec < 0.0 )
6637 fDec = 0.0;
6638 ULONG nIndex = pFormatter->GetStandardFormat(
6639 NUMBERFORMAT_CURRENCY,
6640 ScGlobal::eLnge);
6641 if ( (USHORT) fDec != pFormatter->GetFormatPrecision( nIndex ) )
6643 String sFormatString;
6644 pFormatter->GenerateFormat(sFormatString,
6645 nIndex,
6646 ScGlobal::eLnge,
6647 TRUE, // mit Tausenderpunkt
6648 FALSE, // nicht rot
6649 (USHORT) fDec,// Nachkommastellen
6650 1); // 1 Vorkommanull
6651 if (!pFormatter->GetPreviewString(sFormatString,
6652 fVal,
6653 aStr,
6654 &pColor,
6655 ScGlobal::eLnge))
6656 SetError(errIllegalArgument);
6658 else
6660 pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor);
6662 PushString(aStr);
6667 void ScInterpreter::ScReplace()
6669 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScReplace" );
6670 if ( MustHaveParamCount( GetByte(), 4 ) )
6672 String aNewStr( GetString() );
6673 short nCount = (short) GetDouble();
6674 short nPos = (short) GetDouble();
6675 String aOldStr( GetString() );
6676 if( nPos < 1 || nCount < 1 )
6677 PushIllegalArgument();
6678 else
6680 aOldStr.Erase( nPos-1, nCount );
6681 if ( CheckStringResultLen( aOldStr, aNewStr ) )
6682 aOldStr.Insert( aNewStr, nPos-1 );
6683 PushString( aOldStr );
6689 void ScInterpreter::ScFixed()
6691 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScFixed" );
6692 BYTE nParamCount = GetByte();
6693 if ( MustHaveParamCount( nParamCount, 1, 3 ) )
6695 String aStr;
6696 double fDec;
6697 BOOL bThousand;
6698 if (nParamCount == 3)
6699 bThousand = !GetBool(); // Param TRUE: keine Tausenderpunkte
6700 else
6701 bThousand = TRUE;
6702 if (nParamCount >= 2)
6704 fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 ));
6705 if (fDec < -15.0 || fDec > 15.0)
6707 PushIllegalArgument();
6708 return;
6711 else
6712 fDec = 2.0;
6713 double fVal = GetDouble();
6714 double fFac;
6715 if ( fDec != 0.0 )
6716 fFac = pow( (double)10, fDec );
6717 else
6718 fFac = 1.0;
6719 if (fVal < 0.0)
6720 fVal = ceil(fVal*fFac-0.5)/fFac;
6721 else
6722 fVal = floor(fVal*fFac+0.5)/fFac;
6723 Color* pColor = NULL;
6724 String sFormatString;
6725 if (fDec < 0.0)
6726 fDec = 0.0;
6727 ULONG nIndex = pFormatter->GetStandardFormat(
6728 NUMBERFORMAT_NUMBER,
6729 ScGlobal::eLnge);
6730 pFormatter->GenerateFormat(sFormatString,
6731 nIndex,
6732 ScGlobal::eLnge,
6733 bThousand, // mit Tausenderpunkt
6734 FALSE, // nicht rot
6735 (USHORT) fDec,// Nachkommastellen
6736 1); // 1 Vorkommanull
6737 if (!pFormatter->GetPreviewString(sFormatString,
6738 fVal,
6739 aStr,
6740 &pColor,
6741 ScGlobal::eLnge))
6742 PushIllegalArgument();
6743 else
6744 PushString(aStr);
6749 void ScInterpreter::ScFind()
6751 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScFind" );
6752 BYTE nParamCount = GetByte();
6753 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
6755 double fAnz;
6756 if (nParamCount == 3)
6757 fAnz = GetDouble();
6758 else
6759 fAnz = 1.0;
6760 String sStr = GetString();
6761 if( fAnz < 1.0 || fAnz > (double) sStr.Len() )
6762 PushNoValue();
6763 else
6765 xub_StrLen nPos = sStr.Search( GetString(), (xub_StrLen) fAnz - 1 );
6766 if (nPos == STRING_NOTFOUND)
6767 PushNoValue();
6768 else
6769 PushDouble((double)(nPos + 1));
6775 void ScInterpreter::ScExact()
6777 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScExact" );
6778 nFuncFmtType = NUMBERFORMAT_LOGICAL;
6779 if ( MustHaveParamCount( GetByte(), 2 ) )
6781 String s1( GetString() );
6782 String s2( GetString() );
6783 PushInt( s1 == s2 );
6788 void ScInterpreter::ScLeft()
6790 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScLeft" );
6791 BYTE nParamCount = GetByte();
6792 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
6794 xub_StrLen n;
6795 if (nParamCount == 2)
6797 double nVal = ::rtl::math::approxFloor(GetDouble());
6798 if ( nVal < 0.0 || nVal > STRING_MAXLEN )
6800 PushIllegalArgument();
6801 return ;
6803 else
6804 n = (xub_StrLen) nVal;
6806 else
6807 n = 1;
6808 String aStr( GetString() );
6809 aStr.Erase( n );
6810 PushString( aStr );
6815 void ScInterpreter::ScRight()
6817 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRight" );
6818 BYTE nParamCount = GetByte();
6819 if ( MustHaveParamCount( nParamCount, 1, 2 ) )
6821 xub_StrLen n;
6822 if (nParamCount == 2)
6824 double nVal = ::rtl::math::approxFloor(GetDouble());
6825 if ( nVal < 0.0 || nVal > STRING_MAXLEN )
6827 PushIllegalArgument();
6828 return ;
6830 else
6831 n = (xub_StrLen) nVal;
6833 else
6834 n = 1;
6835 String aStr( GetString() );
6836 if( n < aStr.Len() )
6837 aStr.Erase( 0, aStr.Len() - n );
6838 PushString( aStr );
6843 void ScInterpreter::ScSearch()
6845 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSearch" );
6846 double fAnz;
6847 BYTE nParamCount = GetByte();
6848 if ( MustHaveParamCount( nParamCount, 2, 3 ) )
6850 if (nParamCount == 3)
6852 fAnz = ::rtl::math::approxFloor(GetDouble());
6853 if (fAnz > double(STRING_MAXLEN))
6855 PushIllegalArgument();
6856 return;
6859 else
6860 fAnz = 1.0;
6861 String sStr = GetString();
6862 String SearchStr = GetString();
6863 xub_StrLen nPos = (xub_StrLen) fAnz - 1;
6864 xub_StrLen nEndPos = sStr.Len();
6865 if( nPos >= nEndPos )
6866 PushNoValue();
6867 else
6869 utl::SearchParam::SearchType eSearchType =
6870 (MayBeRegExp( SearchStr, pDok ) ?
6871 utl::SearchParam::SRCH_REGEXP : utl::SearchParam::SRCH_NORMAL);
6872 utl::SearchParam sPar(SearchStr, eSearchType, FALSE, FALSE, FALSE);
6873 utl::TextSearch sT( sPar, *ScGlobal::pCharClass );
6874 int nBool = sT.SearchFrwrd(sStr, &nPos, &nEndPos);
6875 if (!nBool)
6876 PushNoValue();
6877 else
6878 PushDouble((double)(nPos) + 1);
6884 void ScInterpreter::ScMid()
6886 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScMid" );
6887 if ( MustHaveParamCount( GetByte(), 3 ) )
6889 double fAnz = ::rtl::math::approxFloor(GetDouble());
6890 double fAnfang = ::rtl::math::approxFloor(GetDouble());
6891 const String& rStr = GetString();
6892 if (fAnfang < 1.0 || fAnz < 0.0 || fAnfang > double(STRING_MAXLEN) || fAnz > double(STRING_MAXLEN))
6893 PushIllegalArgument();
6894 else
6895 PushString(rStr.Copy( (xub_StrLen) fAnfang - 1, (xub_StrLen) fAnz ));
6900 void ScInterpreter::ScText()
6902 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScText" );
6903 if ( MustHaveParamCount( GetByte(), 2 ) )
6905 String sFormatString = GetString();
6906 double fVal = GetDouble();
6907 String aStr;
6908 Color* pColor = NULL;
6909 LanguageType eCellLang;
6910 const ScPatternAttr* pPattern = pDok->GetPattern(
6911 aPos.Col(), aPos.Row(), aPos.Tab() );
6912 if ( pPattern )
6913 eCellLang = ((const SvxLanguageItem&)
6914 pPattern->GetItem( ATTR_LANGUAGE_FORMAT )).GetValue();
6915 else
6916 eCellLang = ScGlobal::eLnge;
6917 if ( !pFormatter->GetPreviewStringGuess( sFormatString, fVal, aStr,
6918 &pColor, eCellLang ) )
6919 PushIllegalArgument();
6920 else
6921 PushString(aStr);
6926 void ScInterpreter::ScSubstitute()
6928 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScSubstitute" );
6929 BYTE nParamCount = GetByte();
6930 if ( MustHaveParamCount( nParamCount, 3, 4 ) )
6932 xub_StrLen nAnz;
6933 if (nParamCount == 4)
6935 double fAnz = ::rtl::math::approxFloor(GetDouble());
6936 if( fAnz < 1 || fAnz > STRING_MAXLEN )
6938 PushIllegalArgument();
6939 return;
6941 else
6942 nAnz = (xub_StrLen) fAnz;
6944 else
6945 nAnz = 0;
6946 String sNewStr = GetString();
6947 String sOldStr = GetString();
6948 String sStr = GetString();
6949 xub_StrLen nPos = 0;
6950 xub_StrLen nCount = 0;
6951 xub_StrLen nNewLen = sNewStr.Len();
6952 xub_StrLen nOldLen = sOldStr.Len();
6953 while( TRUE )
6955 nPos = sStr.Search( sOldStr, nPos );
6956 if (nPos != STRING_NOTFOUND)
6958 nCount++;
6959 if( !nAnz || nCount == nAnz )
6961 sStr.Erase(nPos,nOldLen);
6962 if ( CheckStringResultLen( sStr, sNewStr ) )
6964 sStr.Insert(sNewStr,nPos);
6965 nPos = sal::static_int_cast<xub_StrLen>( nPos + nNewLen );
6967 else
6968 break;
6970 else
6971 nPos++;
6973 else
6974 break;
6976 PushString( sStr );
6981 void ScInterpreter::ScRept()
6983 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScRept" );
6984 if ( MustHaveParamCount( GetByte(), 2 ) )
6986 double fAnz = ::rtl::math::approxFloor(GetDouble());
6987 String aStr( GetString() );
6988 if ( fAnz < 0.0 )
6989 PushIllegalArgument();
6990 else if ( fAnz * aStr.Len() > STRING_MAXLEN )
6992 PushError( errStringOverflow );
6994 else if ( fAnz == 0.0 )
6995 PushString( EMPTY_STRING );
6996 else
6998 xub_StrLen n = (xub_StrLen) fAnz;
6999 const xub_StrLen nLen = aStr.Len();
7000 String aRes;
7001 const sal_Unicode* const pSrc = aStr.GetBuffer();
7002 sal_Unicode* pDst = aRes.AllocBuffer( n * nLen );
7003 while( n-- )
7005 memcpy( pDst, pSrc, nLen * sizeof(sal_Unicode) );
7006 pDst += nLen;
7008 PushString( aRes );
7014 void ScInterpreter::ScConcat()
7016 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScConcat" );
7017 BYTE nParamCount = GetByte();
7018 String aRes;
7019 while( nParamCount-- > 0)
7021 const String& rStr = GetString();
7022 aRes.Insert( rStr, 0 );
7024 PushString( aRes );
7028 void ScInterpreter::ScErrorType()
7030 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::ScErrorType" );
7031 USHORT nErr;
7032 USHORT nOldError = nGlobalError;
7033 nGlobalError = 0;
7034 switch ( GetStackType() )
7036 case svRefList :
7038 FormulaTokenRef x = PopToken();
7039 if (nGlobalError)
7040 nErr = nGlobalError;
7041 else
7043 const ScRefList* pRefList = static_cast<ScToken*>(x.get())->GetRefList();
7044 size_t n = pRefList->size();
7045 if (!n)
7046 nErr = errNoRef;
7047 else if (n > 1)
7048 nErr = errNoValue;
7049 else
7051 ScRange aRange;
7052 DoubleRefToRange( (*pRefList)[0], aRange);
7053 if (nGlobalError)
7054 nErr = nGlobalError;
7055 else
7057 ScAddress aAdr;
7058 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
7059 nErr = pDok->GetErrCode( aAdr );
7060 else
7061 nErr = nGlobalError;
7066 break;
7067 case svDoubleRef :
7069 ScRange aRange;
7070 PopDoubleRef( aRange );
7071 if ( nGlobalError )
7072 nErr = nGlobalError;
7073 else
7075 ScAddress aAdr;
7076 if ( DoubleRefToPosSingleRef( aRange, aAdr ) )
7077 nErr = pDok->GetErrCode( aAdr );
7078 else
7079 nErr = nGlobalError;
7082 break;
7083 case svSingleRef :
7085 ScAddress aAdr;
7086 PopSingleRef( aAdr );
7087 if ( nGlobalError )
7088 nErr = nGlobalError;
7089 else
7090 nErr = pDok->GetErrCode( aAdr );
7092 break;
7093 default:
7094 PopError();
7095 nErr = nGlobalError;
7097 if ( nErr )
7099 nGlobalError = 0;
7100 PushDouble( nErr );
7102 else
7104 nGlobalError = nOldError;
7105 PushNA();
7110 BOOL ScInterpreter::MayBeRegExp( const String& rStr, const ScDocument* pDoc )
7112 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::MayBeRegExp" );
7113 if ( pDoc && !pDoc->GetDocOptions().IsFormulaRegexEnabled() )
7114 return FALSE;
7115 if ( !rStr.Len() || (rStr.Len() == 1 && rStr.GetChar(0) != '.') )
7116 return FALSE; // einzelnes Metazeichen kann keine RegExp sein
7117 static const sal_Unicode cre[] = { '.','*','+','?','[',']','^','$','\\','<','>','(',')','|', 0 };
7118 const sal_Unicode* p1 = rStr.GetBuffer();
7119 sal_Unicode c1;
7120 while ( ( c1 = *p1++ ) != 0 )
7122 const sal_Unicode* p2 = cre;
7123 while ( *p2 )
7125 if ( c1 == *p2++ )
7126 return TRUE;
7129 return FALSE;
7132 static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument * pDoc,
7133 const ScQueryParam & rParam, const ScQueryEntry & rEntry )
7135 bool bFound = false;
7136 ScQueryCellIterator aCellIter( pDoc, rParam.nTab, rParam, FALSE);
7137 if (rEntry.eOp != SC_EQUAL)
7139 // range lookup <= or >=
7140 SCCOL nCol;
7141 SCROW nRow;
7142 bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow);
7143 if (bFound)
7145 o_rResultPos.SetCol( nCol);
7146 o_rResultPos.SetRow( nRow);
7149 else if (aCellIter.GetFirst())
7151 // EQUAL
7152 bFound = true;
7153 o_rResultPos.SetCol( aCellIter.GetCol());
7154 o_rResultPos.SetRow( aCellIter.GetRow());
7156 return bFound;
7159 #define erDEBUG_LOOKUPCACHE 0
7160 #if erDEBUG_LOOKUPCACHE
7161 #include <cstdio>
7162 using ::std::fprintf;
7163 using ::std::fflush;
7164 static struct LookupCacheDebugCounter
7166 unsigned long nMiss;
7167 unsigned long nHit;
7168 LookupCacheDebugCounter() : nMiss(0), nHit(0) {}
7169 ~LookupCacheDebugCounter()
7171 fprintf( stderr, "\nmiss: %lu, hit: %lu, total: %lu, hit/miss: %lu, hit/total %lu%\n",
7172 nMiss, nHit, nHit+nMiss, (nMiss>0 ? nHit/nMiss : 0),
7173 ((nHit+nMiss)>0 ? (100*nHit)/(nHit+nMiss) : 0));
7174 fflush( stderr);
7176 } aLookupCacheDebugCounter;
7177 #endif
7179 bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos,
7180 const ScQueryParam & rParam ) const
7182 RTL_LOGFILE_CONTEXT_AUTHOR( aLogger, "sc", "Eike.Rathke@sun.com", "ScInterpreter::LookupQueryWithCache" );
7183 bool bFound = false;
7184 const ScQueryEntry& rEntry = rParam.GetEntry(0);
7185 bool bColumnsMatch = (rParam.nCol1 == rEntry.nField);
7186 DBG_ASSERT( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match");
7187 if (!bColumnsMatch)
7188 bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry);
7189 else
7191 ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab,
7192 rParam.nCol2, rParam.nRow2, rParam.nTab);
7193 ScLookupCache& rCache = pDok->GetLookupCache( aLookupRange);
7194 ScLookupCache::QueryCriteria aCriteria( rEntry);
7195 ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos,
7196 aCriteria, aPos);
7197 switch (eCacheResult)
7199 case ScLookupCache::NOT_CACHED :
7200 case ScLookupCache::CRITERIA_DIFFERENT :
7201 #if erDEBUG_LOOKUPCACHE
7202 ++aLookupCacheDebugCounter.nMiss;
7203 #if erDEBUG_LOOKUPCACHE > 1
7204 fprintf( stderr, "miss %d,%d,%d\n", (int)aPos.Col(), (int)aPos.Row(), (int)aPos.Tab());
7205 #endif
7206 #endif
7207 bFound = lcl_LookupQuery( o_rResultPos, pDok, rParam, rEntry);
7208 if (eCacheResult == ScLookupCache::NOT_CACHED)
7209 rCache.insert( o_rResultPos, aCriteria, aPos, bFound);
7210 break;
7211 case ScLookupCache::FOUND :
7212 #if erDEBUG_LOOKUPCACHE
7213 ++aLookupCacheDebugCounter.nHit;
7214 #if erDEBUG_LOOKUPCACHE > 1
7215 fprintf( stderr, "hit %d,%d,%d\n", (int)aPos.Col(), (int)aPos.Row(), (int)aPos.Tab());
7216 #endif
7217 #endif
7218 bFound = true;
7219 break;
7220 case ScLookupCache::NOT_AVAILABLE :
7221 ; // nothing, bFound remains FALSE
7222 break;
7225 return bFound;