File: | home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx |
Warning: | line 8061, column 20 The left operand of '/' is a garbage value |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ | |||
2 | /* | |||
3 | * This file is part of the LibreOffice project. | |||
4 | * | |||
5 | * This Source Code Form is subject to the terms of the Mozilla Public | |||
6 | * License, v. 2.0. If a copy of the MPL was not distributed with this | |||
7 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. | |||
8 | * | |||
9 | * This file incorporates work covered by the following license notice: | |||
10 | * | |||
11 | * Licensed to the Apache Software Foundation (ASF) under one or more | |||
12 | * contributor license agreements. See the NOTICE file distributed | |||
13 | * with this work for additional information regarding copyright | |||
14 | * ownership. The ASF licenses this file to you under the Apache | |||
15 | * License, Version 2.0 (the "License"); you may not use this file | |||
16 | * except in compliance with the License. You may obtain a copy of | |||
17 | * the License at http://www.apache.org/licenses/LICENSE-2.0 . | |||
18 | */ | |||
19 | ||||
20 | #include <interpre.hxx> | |||
21 | ||||
22 | #include <scitems.hxx> | |||
23 | #include <editeng/langitem.hxx> | |||
24 | #include <editeng/justifyitem.hxx> | |||
25 | #include <o3tl/safeint.hxx> | |||
26 | #include <osl/thread.h> | |||
27 | #include <svx/algitem.hxx> | |||
28 | #include <unotools/textsearch.hxx> | |||
29 | #include <svl/zforlist.hxx> | |||
30 | #include <svl/zformat.hxx> | |||
31 | #include <tools/urlobj.hxx> | |||
32 | #include <unotools/charclass.hxx> | |||
33 | #include <sfx2/docfile.hxx> | |||
34 | #include <sfx2/printer.hxx> | |||
35 | #include <unotools/collatorwrapper.hxx> | |||
36 | #include <unotools/transliterationwrapper.hxx> | |||
37 | #include <rtl/character.hxx> | |||
38 | #include <rtl/ustring.hxx> | |||
39 | #include <sal/log.hxx> | |||
40 | #include <osl/diagnose.h> | |||
41 | #include <unicode/uchar.h> | |||
42 | #include <unicode/regex.h> | |||
43 | #include <i18nlangtag/mslangid.hxx> | |||
44 | ||||
45 | #include <patattr.hxx> | |||
46 | #include <global.hxx> | |||
47 | #include <document.hxx> | |||
48 | #include <dociter.hxx> | |||
49 | #include <formulacell.hxx> | |||
50 | #include <scmatrix.hxx> | |||
51 | #include <docoptio.hxx> | |||
52 | #include <attrib.hxx> | |||
53 | #include <jumpmatrix.hxx> | |||
54 | #include <cellkeytranslator.hxx> | |||
55 | #include <lookupcache.hxx> | |||
56 | #include <rangenam.hxx> | |||
57 | #include <rangeutl.hxx> | |||
58 | #include <compiler.hxx> | |||
59 | #include <externalrefmgr.hxx> | |||
60 | #include <basic/sbstar.hxx> | |||
61 | #include <doubleref.hxx> | |||
62 | #include <queryparam.hxx> | |||
63 | #include <queryentry.hxx> | |||
64 | #include <tokenarray.hxx> | |||
65 | #include <compare.hxx> | |||
66 | ||||
67 | #include <com/sun/star/util/SearchResult.hpp> | |||
68 | #include <comphelper/processfactory.hxx> | |||
69 | #include <comphelper/random.hxx> | |||
70 | #include <comphelper/string.hxx> | |||
71 | #include <svl/sharedstringpool.hxx> | |||
72 | ||||
73 | #include <stdlib.h> | |||
74 | #include <string.h> | |||
75 | #include <math.h> | |||
76 | #include <vector> | |||
77 | #include <memory> | |||
78 | #include <limits> | |||
79 | ||||
80 | const sal_uInt64 n2power48 = SAL_CONST_UINT64( 281474976710656)281474976710656ul; // 2^48 | |||
81 | ||||
82 | ScCalcConfig *ScInterpreter::mpGlobalConfig = nullptr; | |||
83 | ||||
84 | using namespace formula; | |||
85 | using ::std::unique_ptr; | |||
86 | ||||
87 | void ScInterpreter::ScIfJump() | |||
88 | { | |||
89 | const short* pJump = pCur->GetJump(); | |||
90 | short nJumpCount = pJump[ 0 ]; | |||
91 | MatrixJumpConditionToMatrix(); | |||
92 | switch ( GetStackType() ) | |||
93 | { | |||
94 | case svMatrix: | |||
95 | { | |||
96 | ScMatrixRef pMat = PopMatrix(); | |||
97 | if ( !pMat ) | |||
98 | PushIllegalParameter(); | |||
99 | else | |||
100 | { | |||
101 | FormulaConstTokenRef xNew; | |||
102 | ScTokenMatrixMap::const_iterator aMapIter; | |||
103 | // DoubleError handled by JumpMatrix | |||
104 | pMat->SetErrorInterpreter( nullptr); | |||
105 | SCSIZE nCols, nRows; | |||
106 | pMat->GetDimensions( nCols, nRows ); | |||
107 | if ( nCols == 0 || nRows == 0 ) | |||
108 | { | |||
109 | PushIllegalArgument(); | |||
110 | return; | |||
111 | } | |||
112 | else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end())) | |||
113 | xNew = (*aMapIter).second; | |||
114 | else | |||
115 | { | |||
116 | std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>( | |||
117 | pCur->GetOpCode(), nCols, nRows)); | |||
118 | for ( SCSIZE nC=0; nC < nCols; ++nC ) | |||
119 | { | |||
120 | for ( SCSIZE nR=0; nR < nRows; ++nR ) | |||
121 | { | |||
122 | double fVal; | |||
123 | bool bTrue; | |||
124 | bool bIsValue = pMat->IsValue(nC, nR); | |||
125 | if (bIsValue) | |||
126 | { | |||
127 | fVal = pMat->GetDouble(nC, nR); | |||
128 | bIsValue = std::isfinite(fVal); | |||
129 | bTrue = bIsValue && (fVal != 0.0); | |||
130 | if (bTrue) | |||
131 | fVal = 1.0; | |||
132 | } | |||
133 | else | |||
134 | { | |||
135 | // Treat empty and empty path as 0, but string | |||
136 | // as error. ScMatrix::IsValueOrEmpty() returns | |||
137 | // true for any empty, empty path, empty cell, | |||
138 | // empty result. | |||
139 | bIsValue = pMat->IsValueOrEmpty(nC, nR); | |||
140 | bTrue = false; | |||
141 | fVal = (bIsValue ? 0.0 : CreateDoubleError( FormulaError::NoValue)); | |||
142 | } | |||
143 | if ( bTrue ) | |||
144 | { // TRUE | |||
145 | if( nJumpCount >= 2 ) | |||
146 | { // THEN path | |||
147 | pJumpMat->SetJump( nC, nR, fVal, | |||
148 | pJump[ 1 ], | |||
149 | pJump[ nJumpCount ]); | |||
150 | } | |||
151 | else | |||
152 | { // no parameter given for THEN | |||
153 | pJumpMat->SetJump( nC, nR, fVal, | |||
154 | pJump[ nJumpCount ], | |||
155 | pJump[ nJumpCount ]); | |||
156 | } | |||
157 | } | |||
158 | else | |||
159 | { // FALSE | |||
160 | if( nJumpCount == 3 && bIsValue ) | |||
161 | { // ELSE path | |||
162 | pJumpMat->SetJump( nC, nR, fVal, | |||
163 | pJump[ 2 ], | |||
164 | pJump[ nJumpCount ]); | |||
165 | } | |||
166 | else | |||
167 | { // no parameter given for ELSE, | |||
168 | // or DoubleError | |||
169 | pJumpMat->SetJump( nC, nR, fVal, | |||
170 | pJump[ nJumpCount ], | |||
171 | pJump[ nJumpCount ]); | |||
172 | } | |||
173 | } | |||
174 | } | |||
175 | } | |||
176 | xNew = new ScJumpMatrixToken( pJumpMat ); | |||
177 | GetTokenMatrixMap().emplace(pCur, xNew); | |||
178 | } | |||
179 | if (!xNew) | |||
180 | { | |||
181 | PushIllegalArgument(); | |||
182 | return; | |||
183 | } | |||
184 | PushTokenRef( xNew); | |||
185 | // set endpoint of path for main code line | |||
186 | aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
187 | } | |||
188 | } | |||
189 | break; | |||
190 | default: | |||
191 | { | |||
192 | if ( GetBool() ) | |||
193 | { // TRUE | |||
194 | if( nJumpCount >= 2 ) | |||
195 | { // THEN path | |||
196 | aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] ); | |||
197 | } | |||
198 | else | |||
199 | { // no parameter given for THEN | |||
200 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
201 | PushInt(1); | |||
202 | aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
203 | } | |||
204 | } | |||
205 | else | |||
206 | { // FALSE | |||
207 | if( nJumpCount == 3 ) | |||
208 | { // ELSE path | |||
209 | aCode.Jump( pJump[ 2 ], pJump[ nJumpCount ] ); | |||
210 | } | |||
211 | else | |||
212 | { // no parameter given for ELSE | |||
213 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
214 | PushInt(0); | |||
215 | aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
216 | } | |||
217 | } | |||
218 | } | |||
219 | } | |||
220 | } | |||
221 | ||||
222 | /** Store a matrix value in another matrix in the context of that other matrix | |||
223 | is the result matrix of a jump matrix. All arguments must be valid and are | |||
224 | not checked. */ | |||
225 | static void lcl_storeJumpMatResult( | |||
226 | const ScMatrix* pMat, ScJumpMatrix* pJumpMat, SCSIZE nC, SCSIZE nR ) | |||
227 | { | |||
228 | if ( pMat->IsValue( nC, nR ) ) | |||
229 | { | |||
230 | double fVal = pMat->GetDouble( nC, nR ); | |||
231 | pJumpMat->PutResultDouble( fVal, nC, nR ); | |||
232 | } | |||
233 | else if ( pMat->IsEmpty( nC, nR ) ) | |||
234 | { | |||
235 | pJumpMat->PutResultEmpty( nC, nR ); | |||
236 | } | |||
237 | else | |||
238 | { | |||
239 | pJumpMat->PutResultString(pMat->GetString(nC, nR), nC, nR); | |||
240 | } | |||
241 | } | |||
242 | ||||
243 | void ScInterpreter::ScIfError( bool bNAonly ) | |||
244 | { | |||
245 | const short* pJump = pCur->GetJump(); | |||
246 | short nJumpCount = pJump[ 0 ]; | |||
247 | if (!sp || nJumpCount != 2) | |||
248 | { | |||
249 | // Reset nGlobalError here to not propagate the old error, if any. | |||
250 | nGlobalError = (sp ? FormulaError::ParameterExpected : FormulaError::UnknownStackVariable); | |||
251 | PushError( nGlobalError); | |||
252 | aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
253 | return; | |||
254 | } | |||
255 | ||||
256 | FormulaConstTokenRef xToken( pStack[ sp - 1 ] ); | |||
257 | bool bError = false; | |||
258 | FormulaError nOldGlobalError = nGlobalError; | |||
259 | nGlobalError = FormulaError::NONE; | |||
260 | ||||
261 | MatrixJumpConditionToMatrix(); | |||
262 | switch (GetStackType()) | |||
263 | { | |||
264 | default: | |||
265 | Pop(); | |||
266 | // Act on implicitly propagated error, if any. | |||
267 | if (nOldGlobalError != FormulaError::NONE) | |||
268 | nGlobalError = nOldGlobalError; | |||
269 | if (nGlobalError != FormulaError::NONE) | |||
270 | bError = true; | |||
271 | break; | |||
272 | case svError: | |||
273 | PopError(); | |||
274 | bError = true; | |||
275 | break; | |||
276 | case svDoubleRef: | |||
277 | case svSingleRef: | |||
278 | { | |||
279 | ScAddress aAdr; | |||
280 | if (!PopDoubleRefOrSingleRef( aAdr)) | |||
281 | bError = true; | |||
282 | else | |||
283 | { | |||
284 | ||||
285 | ScRefCellValue aCell(mrDoc, aAdr); | |||
286 | nGlobalError = GetCellErrCode(aCell); | |||
287 | if (nGlobalError != FormulaError::NONE) | |||
288 | bError = true; | |||
289 | } | |||
290 | } | |||
291 | break; | |||
292 | case svExternalSingleRef: | |||
293 | case svExternalDoubleRef: | |||
294 | { | |||
295 | double fVal; | |||
296 | svl::SharedString aStr; | |||
297 | // Handles also existing jump matrix case and sets error on | |||
298 | // elements. | |||
299 | GetDoubleOrStringFromMatrix( fVal, aStr); | |||
300 | if (nGlobalError != FormulaError::NONE) | |||
301 | bError = true; | |||
302 | } | |||
303 | break; | |||
304 | case svMatrix: | |||
305 | { | |||
306 | const ScMatrixRef pMat = PopMatrix(); | |||
307 | if (!pMat || (nGlobalError != FormulaError::NONE && (!bNAonly || nGlobalError == FormulaError::NotAvailable))) | |||
308 | { | |||
309 | bError = true; | |||
310 | break; // switch | |||
311 | } | |||
312 | // If the matrix has no queried error at all we can simply use | |||
313 | // it as result and don't need to bother with jump matrix. | |||
314 | SCSIZE nErrorCol = ::std::numeric_limits<SCSIZE>::max(), | |||
315 | nErrorRow = ::std::numeric_limits<SCSIZE>::max(); | |||
316 | SCSIZE nCols, nRows; | |||
317 | pMat->GetDimensions( nCols, nRows ); | |||
318 | if (nCols == 0 || nRows == 0) | |||
319 | { | |||
320 | bError = true; | |||
321 | break; // switch | |||
322 | } | |||
323 | for (SCSIZE nC=0; nC < nCols && !bError; ++nC) | |||
324 | { | |||
325 | for (SCSIZE nR=0; nR < nRows && !bError; ++nR) | |||
326 | { | |||
327 | FormulaError nErr = pMat->GetError( nC, nR ); | |||
328 | if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable)) | |||
329 | { | |||
330 | bError = true; | |||
331 | nErrorCol = nC; | |||
332 | nErrorRow = nR; | |||
333 | } | |||
334 | } | |||
335 | } | |||
336 | if (!bError) | |||
337 | break; // switch, we're done and have the result | |||
338 | ||||
339 | FormulaConstTokenRef xNew; | |||
340 | ScTokenMatrixMap::const_iterator aMapIter; | |||
341 | if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( pCur)) != pTokenMatrixMap->end())) | |||
342 | { | |||
343 | xNew = (*aMapIter).second; | |||
344 | } | |||
345 | else | |||
346 | { | |||
347 | const ScMatrix* pMatPtr = pMat.get(); | |||
348 | std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>( | |||
349 | pCur->GetOpCode(), nCols, nRows)); | |||
350 | // Init all jumps to no error to save single calls. Error | |||
351 | // is the exceptional condition. | |||
352 | const double fFlagResult = CreateDoubleError( FormulaError::JumpMatHasResult); | |||
353 | pJumpMat->SetAllJumps( fFlagResult, pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
354 | // Up to first error position simply store results, no need | |||
355 | // to evaluate error conditions again. | |||
356 | SCSIZE nC = 0, nR = 0; | |||
357 | for ( ; nC < nCols && (nC != nErrorCol || nR != nErrorRow); /*nop*/ ) | |||
358 | { | |||
359 | for (nR = 0 ; nR < nRows && (nC != nErrorCol || nR != nErrorRow); ++nR) | |||
360 | { | |||
361 | lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR); | |||
362 | } | |||
363 | if (nC != nErrorCol && nR != nErrorRow) | |||
364 | ++nC; | |||
365 | } | |||
366 | // Now the mixed cases. | |||
367 | for ( ; nC < nCols; ++nC) | |||
368 | { | |||
369 | for ( ; nR < nRows; ++nR) | |||
370 | { | |||
371 | FormulaError nErr = pMat->GetError( nC, nR ); | |||
372 | if (nErr != FormulaError::NONE && (!bNAonly || nErr == FormulaError::NotAvailable)) | |||
373 | { // TRUE, THEN path | |||
374 | pJumpMat->SetJump( nC, nR, 1.0, pJump[ 1 ], pJump[ nJumpCount ] ); | |||
375 | } | |||
376 | else | |||
377 | { // FALSE, EMPTY path, store result instead | |||
378 | lcl_storeJumpMatResult(pMatPtr, pJumpMat.get(), nC, nR); | |||
379 | } | |||
380 | } | |||
381 | nR = 0; | |||
382 | } | |||
383 | xNew = new ScJumpMatrixToken( pJumpMat ); | |||
384 | GetTokenMatrixMap().emplace( pCur, xNew ); | |||
385 | } | |||
386 | nGlobalError = nOldGlobalError; | |||
387 | PushTokenRef( xNew ); | |||
388 | // set endpoint of path for main code line | |||
389 | aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
390 | return; | |||
391 | } | |||
392 | break; | |||
393 | } | |||
394 | ||||
395 | if (bError && (!bNAonly || nGlobalError == FormulaError::NotAvailable)) | |||
396 | { | |||
397 | // error, calculate 2nd argument | |||
398 | nGlobalError = FormulaError::NONE; | |||
399 | aCode.Jump( pJump[ 1 ], pJump[ nJumpCount ] ); | |||
400 | } | |||
401 | else | |||
402 | { | |||
403 | // no error, push 1st argument and continue | |||
404 | nGlobalError = nOldGlobalError; | |||
405 | PushTokenRef( xToken); | |||
406 | aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
407 | } | |||
408 | } | |||
409 | ||||
410 | void ScInterpreter::ScChooseJump() | |||
411 | { | |||
412 | // We have to set a jump, if there was none chosen because of an error set | |||
413 | // it to endpoint. | |||
414 | bool bHaveJump = false; | |||
415 | const short* pJump = pCur->GetJump(); | |||
416 | short nJumpCount = pJump[ 0 ]; | |||
417 | MatrixJumpConditionToMatrix(); | |||
418 | switch ( GetStackType() ) | |||
419 | { | |||
420 | case svMatrix: | |||
421 | { | |||
422 | ScMatrixRef pMat = PopMatrix(); | |||
423 | if ( !pMat ) | |||
424 | PushIllegalParameter(); | |||
425 | else | |||
426 | { | |||
427 | FormulaConstTokenRef xNew; | |||
428 | ScTokenMatrixMap::const_iterator aMapIter; | |||
429 | // DoubleError handled by JumpMatrix | |||
430 | pMat->SetErrorInterpreter( nullptr); | |||
431 | SCSIZE nCols, nRows; | |||
432 | pMat->GetDimensions( nCols, nRows ); | |||
433 | if ( nCols == 0 || nRows == 0 ) | |||
434 | PushIllegalParameter(); | |||
435 | else if (pTokenMatrixMap && ((aMapIter = pTokenMatrixMap->find( | |||
436 | pCur)) != pTokenMatrixMap->end())) | |||
437 | xNew = (*aMapIter).second; | |||
438 | else | |||
439 | { | |||
440 | std::shared_ptr<ScJumpMatrix> pJumpMat( std::make_shared<ScJumpMatrix>( | |||
441 | pCur->GetOpCode(), nCols, nRows)); | |||
442 | for ( SCSIZE nC=0; nC < nCols; ++nC ) | |||
443 | { | |||
444 | for ( SCSIZE nR=0; nR < nRows; ++nR ) | |||
445 | { | |||
446 | double fVal; | |||
447 | bool bIsValue = pMat->IsValue(nC, nR); | |||
448 | if ( bIsValue ) | |||
449 | { | |||
450 | fVal = pMat->GetDouble(nC, nR); | |||
451 | bIsValue = std::isfinite( fVal ); | |||
452 | if ( bIsValue ) | |||
453 | { | |||
454 | fVal = ::rtl::math::approxFloor( fVal); | |||
455 | if ( (fVal < 1) || (fVal >= nJumpCount)) | |||
456 | { | |||
457 | bIsValue = false; | |||
458 | fVal = CreateDoubleError( | |||
459 | FormulaError::IllegalArgument); | |||
460 | } | |||
461 | } | |||
462 | } | |||
463 | else | |||
464 | { | |||
465 | fVal = CreateDoubleError( FormulaError::NoValue); | |||
466 | } | |||
467 | if ( bIsValue ) | |||
468 | { | |||
469 | pJumpMat->SetJump( nC, nR, fVal, | |||
470 | pJump[ static_cast<short>(fVal) ], | |||
471 | pJump[ nJumpCount ]); | |||
472 | } | |||
473 | else | |||
474 | { | |||
475 | pJumpMat->SetJump( nC, nR, fVal, | |||
476 | pJump[ nJumpCount ], | |||
477 | pJump[ nJumpCount ]); | |||
478 | } | |||
479 | } | |||
480 | } | |||
481 | xNew = new ScJumpMatrixToken( pJumpMat ); | |||
482 | GetTokenMatrixMap().emplace(pCur, xNew); | |||
483 | } | |||
484 | if (xNew) | |||
485 | { | |||
486 | PushTokenRef( xNew); | |||
487 | // set endpoint of path for main code line | |||
488 | aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
489 | bHaveJump = true; | |||
490 | } | |||
491 | } | |||
492 | } | |||
493 | break; | |||
494 | default: | |||
495 | { | |||
496 | sal_Int16 nJumpIndex = GetInt16(); | |||
497 | if (nGlobalError == FormulaError::NONE && (nJumpIndex >= 1) && (nJumpIndex < nJumpCount)) | |||
498 | { | |||
499 | aCode.Jump( pJump[ static_cast<short>(nJumpIndex) ], pJump[ nJumpCount ] ); | |||
500 | bHaveJump = true; | |||
501 | } | |||
502 | else | |||
503 | PushIllegalArgument(); | |||
504 | } | |||
505 | } | |||
506 | if (!bHaveJump) | |||
507 | aCode.Jump( pJump[ nJumpCount ], pJump[ nJumpCount ] ); | |||
508 | } | |||
509 | ||||
510 | static void lcl_AdjustJumpMatrix( ScJumpMatrix* pJumpM, SCSIZE nParmCols, SCSIZE nParmRows ) | |||
511 | { | |||
512 | SCSIZE nJumpCols, nJumpRows; | |||
513 | SCSIZE nResCols, nResRows; | |||
514 | SCSIZE nAdjustCols, nAdjustRows; | |||
515 | pJumpM->GetDimensions( nJumpCols, nJumpRows ); | |||
516 | pJumpM->GetResMatDimensions( nResCols, nResRows ); | |||
517 | if (!(( nJumpCols == 1 && nParmCols > nResCols ) || | |||
518 | ( nJumpRows == 1 && nParmRows > nResRows ))) | |||
519 | return; | |||
520 | ||||
521 | if ( nJumpCols == 1 && nJumpRows == 1 ) | |||
522 | { | |||
523 | nAdjustCols = std::max(nParmCols, nResCols); | |||
524 | nAdjustRows = std::max(nParmRows, nResRows); | |||
525 | } | |||
526 | else if ( nJumpCols == 1 ) | |||
527 | { | |||
528 | nAdjustCols = nParmCols; | |||
529 | nAdjustRows = nResRows; | |||
530 | } | |||
531 | else | |||
532 | { | |||
533 | nAdjustCols = nResCols; | |||
534 | nAdjustRows = nParmRows; | |||
535 | } | |||
536 | pJumpM->SetNewResMat( nAdjustCols, nAdjustRows ); | |||
537 | } | |||
538 | ||||
539 | bool ScInterpreter::JumpMatrix( short nStackLevel ) | |||
540 | { | |||
541 | pJumpMatrix = pStack[sp-nStackLevel]->GetJumpMatrix(); | |||
542 | bool bHasResMat = pJumpMatrix->HasResultMatrix(); | |||
543 | SCSIZE nC, nR; | |||
544 | if ( nStackLevel == 2 ) | |||
545 | { | |||
546 | if ( aCode.HasStacked() ) | |||
547 | aCode.Pop(); // pop what Jump() pushed | |||
548 | else | |||
549 | { | |||
550 | assert(!"pop goes the weasel")(static_cast <bool> (!"pop goes the weasel") ? void (0) : __assert_fail ("!\"pop goes the weasel\"", "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" , 550, __extension__ __PRETTY_FUNCTION__)); | |||
551 | } | |||
552 | ||||
553 | if ( !bHasResMat ) | |||
554 | { | |||
555 | Pop(); | |||
556 | SetError( FormulaError::UnknownStackVariable ); | |||
557 | } | |||
558 | else | |||
559 | { | |||
560 | pJumpMatrix->GetPos( nC, nR ); | |||
561 | switch ( GetStackType() ) | |||
562 | { | |||
563 | case svDouble: | |||
564 | { | |||
565 | double fVal = GetDouble(); | |||
566 | if ( nGlobalError != FormulaError::NONE ) | |||
567 | { | |||
568 | fVal = CreateDoubleError( nGlobalError ); | |||
569 | nGlobalError = FormulaError::NONE; | |||
570 | } | |||
571 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
572 | } | |||
573 | break; | |||
574 | case svString: | |||
575 | { | |||
576 | svl::SharedString aStr = GetString(); | |||
577 | if ( nGlobalError != FormulaError::NONE ) | |||
578 | { | |||
579 | pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), | |||
580 | nC, nR); | |||
581 | nGlobalError = FormulaError::NONE; | |||
582 | } | |||
583 | else | |||
584 | pJumpMatrix->PutResultString(aStr, nC, nR); | |||
585 | } | |||
586 | break; | |||
587 | case svSingleRef: | |||
588 | { | |||
589 | FormulaConstTokenRef xRef = pStack[sp-1]; | |||
590 | ScAddress aAdr; | |||
591 | PopSingleRef( aAdr ); | |||
592 | if ( nGlobalError != FormulaError::NONE ) | |||
593 | { | |||
594 | pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), | |||
595 | nC, nR); | |||
596 | nGlobalError = FormulaError::NONE; | |||
597 | } | |||
598 | else | |||
599 | { | |||
600 | ScRefCellValue aCell(mrDoc, aAdr); | |||
601 | if (aCell.hasEmptyValue()) | |||
602 | pJumpMatrix->PutResultEmpty( nC, nR ); | |||
603 | else if (aCell.hasNumeric()) | |||
604 | { | |||
605 | double fVal = GetCellValue(aAdr, aCell); | |||
606 | if ( nGlobalError != FormulaError::NONE ) | |||
607 | { | |||
608 | fVal = CreateDoubleError( | |||
609 | nGlobalError); | |||
610 | nGlobalError = FormulaError::NONE; | |||
611 | } | |||
612 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
613 | } | |||
614 | else | |||
615 | { | |||
616 | svl::SharedString aStr; | |||
617 | GetCellString(aStr, aCell); | |||
618 | if ( nGlobalError != FormulaError::NONE ) | |||
619 | { | |||
620 | pJumpMatrix->PutResultDouble( CreateDoubleError( | |||
621 | nGlobalError), nC, nR); | |||
622 | nGlobalError = FormulaError::NONE; | |||
623 | } | |||
624 | else | |||
625 | pJumpMatrix->PutResultString(aStr, nC, nR); | |||
626 | } | |||
627 | } | |||
628 | ||||
629 | formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16((sal_uInt16) 0xFFFF)); | |||
630 | if (eReturnType == ParamClass::Reference) | |||
631 | { | |||
632 | /* TODO: What about error handling and do we actually | |||
633 | * need the result matrix above at all in this case? */ | |||
634 | ScComplexRefData aRef; | |||
635 | aRef.Ref1 = aRef.Ref2 = *(xRef->GetSingleRef()); | |||
636 | pJumpMatrix->GetRefList().push_back( aRef); | |||
637 | } | |||
638 | } | |||
639 | break; | |||
640 | case svDoubleRef: | |||
641 | { // upper left plus offset within matrix | |||
642 | FormulaConstTokenRef xRef = pStack[sp-1]; | |||
643 | double fVal; | |||
644 | ScRange aRange; | |||
645 | PopDoubleRef( aRange ); | |||
646 | if ( nGlobalError != FormulaError::NONE ) | |||
647 | { | |||
648 | fVal = CreateDoubleError( nGlobalError ); | |||
649 | nGlobalError = FormulaError::NONE; | |||
650 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
651 | } | |||
652 | else | |||
653 | { | |||
654 | // Do not modify the original range because we use it | |||
655 | // to adjust the size of the result matrix if necessary. | |||
656 | ScAddress aAdr( aRange.aStart); | |||
657 | sal_uLong nCol = static_cast<sal_uLong>(aAdr.Col()) + nC; | |||
658 | sal_uLong nRow = static_cast<sal_uLong>(aAdr.Row()) + nR; | |||
659 | if ((nCol > o3tl::make_unsigned(aRange.aEnd.Col()) && | |||
660 | aRange.aEnd.Col() != aRange.aStart.Col()) | |||
661 | || (nRow > o3tl::make_unsigned(aRange.aEnd.Row()) && | |||
662 | aRange.aEnd.Row() != aRange.aStart.Row())) | |||
663 | { | |||
664 | fVal = CreateDoubleError( FormulaError::NotAvailable ); | |||
665 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
666 | } | |||
667 | else | |||
668 | { | |||
669 | // Replicate column and/or row of a vector if it is | |||
670 | // one. Note that this could be a range reference | |||
671 | // that in fact consists of only one cell, e.g. A1:A1 | |||
672 | if (aRange.aEnd.Col() == aRange.aStart.Col()) | |||
673 | nCol = aRange.aStart.Col(); | |||
674 | if (aRange.aEnd.Row() == aRange.aStart.Row()) | |||
675 | nRow = aRange.aStart.Row(); | |||
676 | aAdr.SetCol( static_cast<SCCOL>(nCol) ); | |||
677 | aAdr.SetRow( static_cast<SCROW>(nRow) ); | |||
678 | ScRefCellValue aCell(mrDoc, aAdr); | |||
679 | if (aCell.hasEmptyValue()) | |||
680 | pJumpMatrix->PutResultEmpty( nC, nR ); | |||
681 | else if (aCell.hasNumeric()) | |||
682 | { | |||
683 | double fCellVal = GetCellValue(aAdr, aCell); | |||
684 | if ( nGlobalError != FormulaError::NONE ) | |||
685 | { | |||
686 | fCellVal = CreateDoubleError( | |||
687 | nGlobalError); | |||
688 | nGlobalError = FormulaError::NONE; | |||
689 | } | |||
690 | pJumpMatrix->PutResultDouble( fCellVal, nC, nR ); | |||
691 | } | |||
692 | else | |||
693 | { | |||
694 | svl::SharedString aStr; | |||
695 | GetCellString(aStr, aCell); | |||
696 | if ( nGlobalError != FormulaError::NONE ) | |||
697 | { | |||
698 | pJumpMatrix->PutResultDouble( CreateDoubleError( | |||
699 | nGlobalError), nC, nR); | |||
700 | nGlobalError = FormulaError::NONE; | |||
701 | } | |||
702 | else | |||
703 | pJumpMatrix->PutResultString(aStr, nC, nR); | |||
704 | } | |||
705 | } | |||
706 | SCSIZE nParmCols = aRange.aEnd.Col() - aRange.aStart.Col() + 1; | |||
707 | SCSIZE nParmRows = aRange.aEnd.Row() - aRange.aStart.Row() + 1; | |||
708 | lcl_AdjustJumpMatrix( pJumpMatrix, nParmCols, nParmRows ); | |||
709 | } | |||
710 | ||||
711 | formula::ParamClass eReturnType = ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16((sal_uInt16) 0xFFFF)); | |||
712 | if (eReturnType == ParamClass::Reference) | |||
713 | { | |||
714 | /* TODO: What about error handling and do we actually | |||
715 | * need the result matrix above at all in this case? */ | |||
716 | pJumpMatrix->GetRefList().push_back( *(xRef->GetDoubleRef())); | |||
717 | } | |||
718 | } | |||
719 | break; | |||
720 | case svExternalSingleRef: | |||
721 | { | |||
722 | ScExternalRefCache::TokenRef pToken; | |||
723 | PopExternalSingleRef(pToken); | |||
724 | if (nGlobalError != FormulaError::NONE) | |||
725 | { | |||
726 | pJumpMatrix->PutResultDouble( CreateDoubleError( nGlobalError), nC, nR ); | |||
727 | nGlobalError = FormulaError::NONE; | |||
728 | } | |||
729 | else | |||
730 | { | |||
731 | switch (pToken->GetType()) | |||
732 | { | |||
733 | case svDouble: | |||
734 | pJumpMatrix->PutResultDouble( pToken->GetDouble(), nC, nR ); | |||
735 | break; | |||
736 | case svString: | |||
737 | pJumpMatrix->PutResultString( pToken->GetString(), nC, nR ); | |||
738 | break; | |||
739 | case svEmptyCell: | |||
740 | pJumpMatrix->PutResultEmpty( nC, nR ); | |||
741 | break; | |||
742 | default: | |||
743 | // svError was already handled (set by | |||
744 | // PopExternalSingleRef()) with nGlobalError | |||
745 | // above. | |||
746 | assert(!"unhandled svExternalSingleRef case")(static_cast <bool> (!"unhandled svExternalSingleRef case" ) ? void (0) : __assert_fail ("!\"unhandled svExternalSingleRef case\"" , "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" , 746, __extension__ __PRETTY_FUNCTION__)); | |||
747 | pJumpMatrix->PutResultDouble( CreateDoubleError( | |||
748 | FormulaError::UnknownStackVariable), nC, nR ); | |||
749 | } | |||
750 | } | |||
751 | } | |||
752 | break; | |||
753 | case svExternalDoubleRef: | |||
754 | case svMatrix: | |||
755 | { // match matrix offsets | |||
756 | double fVal; | |||
757 | ScMatrixRef pMat = GetMatrix(); | |||
758 | if ( nGlobalError != FormulaError::NONE ) | |||
759 | { | |||
760 | fVal = CreateDoubleError( nGlobalError ); | |||
761 | nGlobalError = FormulaError::NONE; | |||
762 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
763 | } | |||
764 | else if ( !pMat ) | |||
765 | { | |||
766 | fVal = CreateDoubleError( FormulaError::UnknownVariable ); | |||
767 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
768 | } | |||
769 | else | |||
770 | { | |||
771 | SCSIZE nCols, nRows; | |||
772 | pMat->GetDimensions( nCols, nRows ); | |||
773 | if ((nCols <= nC && nCols != 1) || | |||
774 | (nRows <= nR && nRows != 1)) | |||
775 | { | |||
776 | fVal = CreateDoubleError( FormulaError::NotAvailable ); | |||
777 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
778 | } | |||
779 | else | |||
780 | { | |||
781 | // GetMatrix() does SetErrorInterpreter() at the | |||
782 | // matrix, do not propagate an error from | |||
783 | // matrix->GetValue() as global error. | |||
784 | pMat->SetErrorInterpreter(nullptr); | |||
785 | lcl_storeJumpMatResult(pMat.get(), pJumpMatrix, nC, nR); | |||
786 | } | |||
787 | lcl_AdjustJumpMatrix( pJumpMatrix, nCols, nRows ); | |||
788 | } | |||
789 | } | |||
790 | break; | |||
791 | case svError: | |||
792 | { | |||
793 | PopError(); | |||
794 | double fVal = CreateDoubleError( nGlobalError); | |||
795 | nGlobalError = FormulaError::NONE; | |||
796 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
797 | } | |||
798 | break; | |||
799 | default: | |||
800 | { | |||
801 | Pop(); | |||
802 | double fVal = CreateDoubleError( FormulaError::IllegalArgument); | |||
803 | pJumpMatrix->PutResultDouble( fVal, nC, nR ); | |||
804 | } | |||
805 | } | |||
806 | } | |||
807 | } | |||
808 | bool bCont = pJumpMatrix->Next( nC, nR ); | |||
809 | if ( bCont ) | |||
810 | { | |||
811 | double fBool; | |||
812 | short nStart, nNext, nStop; | |||
813 | pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); | |||
814 | while ( bCont && nStart == nNext ) | |||
815 | { // push all results that have no jump path | |||
816 | if ( bHasResMat && (GetDoubleErrorValue( fBool) != FormulaError::JumpMatHasResult) ) | |||
817 | { | |||
818 | // a false without path results in an empty path value | |||
819 | if ( fBool == 0.0 ) | |||
820 | pJumpMatrix->PutResultEmptyPath( nC, nR ); | |||
821 | else | |||
822 | pJumpMatrix->PutResultDouble( fBool, nC, nR ); | |||
823 | } | |||
824 | bCont = pJumpMatrix->Next( nC, nR ); | |||
825 | if ( bCont ) | |||
826 | pJumpMatrix->GetJump( nC, nR, fBool, nStart, nNext, nStop ); | |||
827 | } | |||
828 | if ( bCont && nStart != nNext ) | |||
829 | { | |||
830 | const ScTokenVec & rParams = pJumpMatrix->GetJumpParameters(); | |||
831 | for ( auto const & i : rParams ) | |||
832 | { | |||
833 | // This is not the current state of the interpreter, so | |||
834 | // push without error, and elements' errors are coded into | |||
835 | // double. | |||
836 | PushWithoutError(*i); | |||
837 | } | |||
838 | aCode.Jump( nStart, nNext, nStop ); | |||
839 | } | |||
840 | } | |||
841 | if ( !bCont ) | |||
842 | { // We're done with it, throw away jump matrix, keep result. | |||
843 | // For an intermediate result of Reference use the array of references | |||
844 | // if there are more than one reference and the current ForceArray | |||
845 | // context is ReferenceOrRefArray. | |||
846 | // Else (also for a final result of Reference) use the matrix. | |||
847 | // Treat the result of a jump command as final and use the matrix (see | |||
848 | // tdf#115493 for why). | |||
849 | if (pCur->GetInForceArray() == ParamClass::ReferenceOrRefArray && | |||
850 | pJumpMatrix->GetRefList().size() > 1 && | |||
851 | ScParameterClassification::GetParameterType( pCur, SAL_MAX_UINT16((sal_uInt16) 0xFFFF)) == ParamClass::Reference && | |||
852 | !FormulaCompiler::IsOpCodeJumpCommand( pJumpMatrix->GetOpCode()) && | |||
853 | aCode.PeekNextOperator()) | |||
854 | { | |||
855 | FormulaTokenRef xRef = new ScRefListToken(true); | |||
856 | *(xRef->GetRefList()) = pJumpMatrix->GetRefList(); | |||
857 | pJumpMatrix = nullptr; | |||
858 | Pop(); | |||
859 | PushTokenRef( xRef); | |||
860 | if (pTokenMatrixMap) | |||
861 | { | |||
862 | pTokenMatrixMap->erase( pCur); | |||
863 | // There's no result matrix to remember in this case. | |||
864 | } | |||
865 | } | |||
866 | else | |||
867 | { | |||
868 | ScMatrix* pResMat = pJumpMatrix->GetResultMatrix(); | |||
869 | pJumpMatrix = nullptr; | |||
870 | Pop(); | |||
871 | PushMatrix( pResMat ); | |||
872 | // Remove jump matrix from map and remember result matrix in case it | |||
873 | // could be reused in another path of the same condition. | |||
874 | if (pTokenMatrixMap) | |||
875 | { | |||
876 | pTokenMatrixMap->erase( pCur); | |||
877 | pTokenMatrixMap->emplace(pCur, pStack[sp-1]); | |||
878 | } | |||
879 | } | |||
880 | return true; | |||
881 | } | |||
882 | return false; | |||
883 | } | |||
884 | ||||
885 | double ScInterpreter::Compare( ScQueryOp eOp ) | |||
886 | { | |||
887 | sc::Compare aComp; | |||
888 | aComp.meOp = eOp; | |||
889 | aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase(); | |||
890 | for( short i = 1; i >= 0; i-- ) | |||
891 | { | |||
892 | sc::Compare::Cell& rCell = aComp.maCells[i]; | |||
893 | ||||
894 | switch ( GetRawStackType() ) | |||
895 | { | |||
896 | case svEmptyCell: | |||
897 | Pop(); | |||
898 | rCell.mbEmpty = true; | |||
899 | break; | |||
900 | case svMissing: | |||
901 | case svDouble: | |||
902 | rCell.mfValue = GetDouble(); | |||
903 | rCell.mbValue = true; | |||
904 | break; | |||
905 | case svString: | |||
906 | rCell.maStr = GetString(); | |||
907 | rCell.mbValue = false; | |||
908 | break; | |||
909 | case svDoubleRef : | |||
910 | case svSingleRef : | |||
911 | { | |||
912 | ScAddress aAdr; | |||
913 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
914 | break; | |||
915 | ScRefCellValue aCell(mrDoc, aAdr); | |||
916 | if (aCell.hasEmptyValue()) | |||
917 | rCell.mbEmpty = true; | |||
918 | else if (aCell.hasString()) | |||
919 | { | |||
920 | svl::SharedString aStr; | |||
921 | GetCellString(aStr, aCell); | |||
922 | rCell.maStr = aStr; | |||
923 | rCell.mbValue = false; | |||
924 | } | |||
925 | else | |||
926 | { | |||
927 | rCell.mfValue = GetCellValue(aAdr, aCell); | |||
928 | rCell.mbValue = true; | |||
929 | } | |||
930 | } | |||
931 | break; | |||
932 | case svExternalSingleRef: | |||
933 | { | |||
934 | ScMatrixRef pMat = GetMatrix(); | |||
935 | if (!pMat) | |||
936 | { | |||
937 | SetError( FormulaError::IllegalParameter); | |||
938 | break; | |||
939 | } | |||
940 | ||||
941 | SCSIZE nC, nR; | |||
942 | pMat->GetDimensions(nC, nR); | |||
943 | if (!nC || !nR) | |||
944 | { | |||
945 | SetError( FormulaError::IllegalParameter); | |||
946 | break; | |||
947 | } | |||
948 | if (pMat->IsEmpty(0, 0)) | |||
949 | rCell.mbEmpty = true; | |||
950 | else if (pMat->IsStringOrEmpty(0, 0)) | |||
951 | { | |||
952 | rCell.maStr = pMat->GetString(0, 0); | |||
953 | rCell.mbValue = false; | |||
954 | } | |||
955 | else | |||
956 | { | |||
957 | rCell.mfValue = pMat->GetDouble(0, 0); | |||
958 | rCell.mbValue = true; | |||
959 | } | |||
960 | } | |||
961 | break; | |||
962 | case svExternalDoubleRef: | |||
963 | // TODO: Find out how to handle this... | |||
964 | // Xcl generates a position dependent intersection using | |||
965 | // col/row, as it seems to do for all range references, not | |||
966 | // only in compare context. We'd need a general implementation | |||
967 | // for that behavior similar to svDoubleRef in scalar and array | |||
968 | // mode. Which also means we'd have to change all places where | |||
969 | // it currently is handled along with svMatrix. | |||
970 | default: | |||
971 | PopError(); | |||
972 | SetError( FormulaError::IllegalParameter); | |||
973 | break; | |||
974 | } | |||
975 | } | |||
976 | if( nGlobalError != FormulaError::NONE ) | |||
977 | return 0; | |||
978 | nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; | |||
979 | return sc::CompareFunc(aComp); | |||
980 | } | |||
981 | ||||
982 | sc::RangeMatrix ScInterpreter::CompareMat( ScQueryOp eOp, sc::CompareOptions* pOptions ) | |||
983 | { | |||
984 | sc::Compare aComp; | |||
985 | aComp.meOp = eOp; | |||
986 | aComp.mbIgnoreCase = mrDoc.GetDocOptions().IsIgnoreCase(); | |||
987 | sc::RangeMatrix aMat[2]; | |||
988 | ScAddress aAdr; | |||
989 | for( short i = 1; i >= 0; i-- ) | |||
990 | { | |||
991 | sc::Compare::Cell& rCell = aComp.maCells[i]; | |||
992 | ||||
993 | switch (GetRawStackType()) | |||
994 | { | |||
995 | case svEmptyCell: | |||
996 | Pop(); | |||
997 | rCell.mbEmpty = true; | |||
998 | break; | |||
999 | case svMissing: | |||
1000 | case svDouble: | |||
1001 | rCell.mfValue = GetDouble(); | |||
1002 | rCell.mbValue = true; | |||
1003 | break; | |||
1004 | case svString: | |||
1005 | rCell.maStr = GetString(); | |||
1006 | rCell.mbValue = false; | |||
1007 | break; | |||
1008 | case svSingleRef: | |||
1009 | { | |||
1010 | PopSingleRef( aAdr ); | |||
1011 | ScRefCellValue aCell(mrDoc, aAdr); | |||
1012 | if (aCell.hasEmptyValue()) | |||
1013 | rCell.mbEmpty = true; | |||
1014 | else if (aCell.hasString()) | |||
1015 | { | |||
1016 | svl::SharedString aStr; | |||
1017 | GetCellString(aStr, aCell); | |||
1018 | rCell.maStr = aStr; | |||
1019 | rCell.mbValue = false; | |||
1020 | } | |||
1021 | else | |||
1022 | { | |||
1023 | rCell.mfValue = GetCellValue(aAdr, aCell); | |||
1024 | rCell.mbValue = true; | |||
1025 | } | |||
1026 | } | |||
1027 | break; | |||
1028 | case svExternalSingleRef: | |||
1029 | case svExternalDoubleRef: | |||
1030 | case svDoubleRef: | |||
1031 | case svMatrix: | |||
1032 | aMat[i] = GetRangeMatrix(); | |||
1033 | if (!aMat[i].mpMat) | |||
1034 | SetError( FormulaError::IllegalParameter); | |||
1035 | else | |||
1036 | aMat[i].mpMat->SetErrorInterpreter(nullptr); | |||
1037 | // errors are transported as DoubleError inside matrix | |||
1038 | break; | |||
1039 | default: | |||
1040 | PopError(); | |||
1041 | SetError( FormulaError::IllegalParameter); | |||
1042 | break; | |||
1043 | } | |||
1044 | } | |||
1045 | ||||
1046 | sc::RangeMatrix aRes; | |||
1047 | ||||
1048 | if (nGlobalError != FormulaError::NONE) | |||
1049 | { | |||
1050 | nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1051 | return aRes; | |||
1052 | } | |||
1053 | ||||
1054 | if (aMat[0].mpMat && aMat[1].mpMat) | |||
1055 | { | |||
1056 | SCSIZE nC0, nC1; | |||
1057 | SCSIZE nR0, nR1; | |||
1058 | aMat[0].mpMat->GetDimensions(nC0, nR0); | |||
1059 | aMat[1].mpMat->GetDimensions(nC1, nR1); | |||
1060 | SCSIZE nC = std::max( nC0, nC1 ); | |||
1061 | SCSIZE nR = std::max( nR0, nR1 ); | |||
1062 | aRes.mpMat = GetNewMat( nC, nR); | |||
1063 | if (!aRes.mpMat) | |||
1064 | return aRes; | |||
1065 | for ( SCSIZE j=0; j<nC; j++ ) | |||
1066 | { | |||
1067 | for ( SCSIZE k=0; k<nR; k++ ) | |||
1068 | { | |||
1069 | SCSIZE nCol = j, nRow = k; | |||
1070 | if (aMat[0].mpMat->ValidColRowOrReplicated(nCol, nRow) && | |||
1071 | aMat[1].mpMat->ValidColRowOrReplicated(nCol, nRow)) | |||
1072 | { | |||
1073 | for ( short i=1; i>=0; i-- ) | |||
1074 | { | |||
1075 | sc::Compare::Cell& rCell = aComp.maCells[i]; | |||
1076 | ||||
1077 | if (aMat[i].mpMat->IsStringOrEmpty(j, k)) | |||
1078 | { | |||
1079 | rCell.mbValue = false; | |||
1080 | rCell.maStr = aMat[i].mpMat->GetString(j, k); | |||
1081 | rCell.mbEmpty = aMat[i].mpMat->IsEmpty(j, k); | |||
1082 | } | |||
1083 | else | |||
1084 | { | |||
1085 | rCell.mbValue = true; | |||
1086 | rCell.mfValue = aMat[i].mpMat->GetDouble(j, k); | |||
1087 | rCell.mbEmpty = false; | |||
1088 | } | |||
1089 | } | |||
1090 | aRes.mpMat->PutDouble( sc::CompareFunc( aComp, pOptions), j, k); | |||
1091 | } | |||
1092 | else | |||
1093 | aRes.mpMat->PutError( FormulaError::NoValue, j, k); | |||
1094 | } | |||
1095 | } | |||
1096 | ||||
1097 | switch (eOp) | |||
1098 | { | |||
1099 | case SC_EQUAL: | |||
1100 | aRes.mpMat->CompareEqual(); | |||
1101 | break; | |||
1102 | case SC_LESS: | |||
1103 | aRes.mpMat->CompareLess(); | |||
1104 | break; | |||
1105 | case SC_GREATER: | |||
1106 | aRes.mpMat->CompareGreater(); | |||
1107 | break; | |||
1108 | case SC_LESS_EQUAL: | |||
1109 | aRes.mpMat->CompareLessEqual(); | |||
1110 | break; | |||
1111 | case SC_GREATER_EQUAL: | |||
1112 | aRes.mpMat->CompareGreaterEqual(); | |||
1113 | break; | |||
1114 | case SC_NOT_EQUAL: | |||
1115 | aRes.mpMat->CompareNotEqual(); | |||
1116 | break; | |||
1117 | default: | |||
1118 | SAL_WARN("sc", "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp))do { if (true) { switch (sal_detail_log_report(::SAL_DETAIL_LOG_LEVEL_WARN , "sc")) { case SAL_DETAIL_LOG_ACTION_IGNORE: break; case SAL_DETAIL_LOG_ACTION_LOG : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp)) == 1) { ::sal_detail_log ( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "1118" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp); ::sal::detail::log( (:: SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "1118" ": "), sal_detail_stream, 0); }; break; case SAL_DETAIL_LOG_ACTION_FATAL : if (sizeof ::sal::detail::getResult( ::sal::detail::StreamStart () << "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp)) == 1) { ::sal_detail_log ( (::SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "1118" ": "), ::sal::detail::unwrapStream( ::sal::detail ::StreamStart() << "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp)), 0); } else { ::std::ostringstream sal_detail_stream; sal_detail_stream << "ScInterpreter::QueryMat: unhandled comparison operator: " << static_cast<int>(eOp); ::sal::detail::log( (:: SAL_DETAIL_LOG_LEVEL_WARN), ("sc"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "1118" ": "), sal_detail_stream, 0); }; std::abort(); break ; } } } while (false); | |||
1119 | aRes.mpMat.reset(); | |||
1120 | return aRes; | |||
1121 | } | |||
1122 | } | |||
1123 | else if (aMat[0].mpMat || aMat[1].mpMat) | |||
1124 | { | |||
1125 | size_t i = ( aMat[0].mpMat ? 0 : 1); | |||
1126 | ||||
1127 | aRes.mnCol1 = aMat[i].mnCol1; | |||
1128 | aRes.mnRow1 = aMat[i].mnRow1; | |||
1129 | aRes.mnTab1 = aMat[i].mnTab1; | |||
1130 | aRes.mnCol2 = aMat[i].mnCol2; | |||
1131 | aRes.mnRow2 = aMat[i].mnRow2; | |||
1132 | aRes.mnTab2 = aMat[i].mnTab2; | |||
1133 | ||||
1134 | ScMatrix& rMat = *aMat[i].mpMat; | |||
1135 | aRes.mpMat = rMat.CompareMatrix(aComp, i, pOptions); | |||
1136 | if (!aRes.mpMat) | |||
1137 | return aRes; | |||
1138 | } | |||
1139 | ||||
1140 | nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1141 | return aRes; | |||
1142 | } | |||
1143 | ||||
1144 | ScMatrixRef ScInterpreter::QueryMat( const ScMatrixRef& pMat, sc::CompareOptions& rOptions ) | |||
1145 | { | |||
1146 | SvNumFormatType nSaveCurFmtType = nCurFmtType; | |||
1147 | SvNumFormatType nSaveFuncFmtType = nFuncFmtType; | |||
1148 | PushMatrix( pMat); | |||
1149 | const ScQueryEntry::Item& rItem = rOptions.aQueryEntry.GetQueryItem(); | |||
1150 | if (rItem.meType == ScQueryEntry::ByString) | |||
1151 | PushString(rItem.maString.getString()); | |||
1152 | else | |||
1153 | PushDouble(rItem.mfVal); | |||
1154 | ScMatrixRef pResultMatrix = CompareMat(rOptions.aQueryEntry.eOp, &rOptions).mpMat; | |||
1155 | nCurFmtType = nSaveCurFmtType; | |||
1156 | nFuncFmtType = nSaveFuncFmtType; | |||
1157 | if (nGlobalError != FormulaError::NONE || !pResultMatrix) | |||
1158 | { | |||
1159 | SetError( FormulaError::IllegalParameter); | |||
1160 | return pResultMatrix; | |||
1161 | } | |||
1162 | ||||
1163 | return pResultMatrix; | |||
1164 | } | |||
1165 | ||||
1166 | void ScInterpreter::ScEqual() | |||
1167 | { | |||
1168 | if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) | |||
1169 | { | |||
1170 | sc::RangeMatrix aMat = CompareMat(SC_EQUAL); | |||
1171 | if (!aMat.mpMat) | |||
1172 | { | |||
1173 | PushIllegalParameter(); | |||
1174 | return; | |||
1175 | } | |||
1176 | ||||
1177 | PushMatrix(aMat); | |||
1178 | } | |||
1179 | else | |||
1180 | PushInt( int(Compare( SC_EQUAL) == 0) ); | |||
1181 | } | |||
1182 | ||||
1183 | void ScInterpreter::ScNotEqual() | |||
1184 | { | |||
1185 | if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) | |||
1186 | { | |||
1187 | sc::RangeMatrix aMat = CompareMat(SC_NOT_EQUAL); | |||
1188 | if (!aMat.mpMat) | |||
1189 | { | |||
1190 | PushIllegalParameter(); | |||
1191 | return; | |||
1192 | } | |||
1193 | ||||
1194 | PushMatrix(aMat); | |||
1195 | } | |||
1196 | else | |||
1197 | PushInt( int(Compare( SC_NOT_EQUAL) != 0) ); | |||
1198 | } | |||
1199 | ||||
1200 | void ScInterpreter::ScLess() | |||
1201 | { | |||
1202 | if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) | |||
1203 | { | |||
1204 | sc::RangeMatrix aMat = CompareMat(SC_LESS); | |||
1205 | if (!aMat.mpMat) | |||
1206 | { | |||
1207 | PushIllegalParameter(); | |||
1208 | return; | |||
1209 | } | |||
1210 | ||||
1211 | PushMatrix(aMat); | |||
1212 | } | |||
1213 | else | |||
1214 | PushInt( int(Compare( SC_LESS) < 0) ); | |||
1215 | } | |||
1216 | ||||
1217 | void ScInterpreter::ScGreater() | |||
1218 | { | |||
1219 | if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) | |||
1220 | { | |||
1221 | sc::RangeMatrix aMat = CompareMat(SC_GREATER); | |||
1222 | if (!aMat.mpMat) | |||
1223 | { | |||
1224 | PushIllegalParameter(); | |||
1225 | return; | |||
1226 | } | |||
1227 | ||||
1228 | PushMatrix(aMat); | |||
1229 | } | |||
1230 | else | |||
1231 | PushInt( int(Compare( SC_GREATER) > 0) ); | |||
1232 | } | |||
1233 | ||||
1234 | void ScInterpreter::ScLessEqual() | |||
1235 | { | |||
1236 | if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) | |||
1237 | { | |||
1238 | sc::RangeMatrix aMat = CompareMat(SC_LESS_EQUAL); | |||
1239 | if (!aMat.mpMat) | |||
1240 | { | |||
1241 | PushIllegalParameter(); | |||
1242 | return; | |||
1243 | } | |||
1244 | ||||
1245 | PushMatrix(aMat); | |||
1246 | } | |||
1247 | else | |||
1248 | PushInt( int(Compare( SC_LESS_EQUAL) <= 0) ); | |||
1249 | } | |||
1250 | ||||
1251 | void ScInterpreter::ScGreaterEqual() | |||
1252 | { | |||
1253 | if ( GetStackType(1) == svMatrix || GetStackType(2) == svMatrix ) | |||
1254 | { | |||
1255 | sc::RangeMatrix aMat = CompareMat(SC_GREATER_EQUAL); | |||
1256 | if (!aMat.mpMat) | |||
1257 | { | |||
1258 | PushIllegalParameter(); | |||
1259 | return; | |||
1260 | } | |||
1261 | ||||
1262 | PushMatrix(aMat); | |||
1263 | } | |||
1264 | else | |||
1265 | PushInt( int(Compare( SC_GREATER_EQUAL) >= 0) ); | |||
1266 | } | |||
1267 | ||||
1268 | void ScInterpreter::ScAnd() | |||
1269 | { | |||
1270 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1271 | short nParamCount = GetByte(); | |||
1272 | if ( !MustHaveParamCountMin( nParamCount, 1 ) ) | |||
1273 | return; | |||
1274 | ||||
1275 | bool bHaveValue = false; | |||
1276 | bool bRes = true; | |||
1277 | size_t nRefInList = 0; | |||
1278 | while( nParamCount-- > 0) | |||
1279 | { | |||
1280 | if ( nGlobalError == FormulaError::NONE ) | |||
1281 | { | |||
1282 | switch ( GetStackType() ) | |||
1283 | { | |||
1284 | case svDouble : | |||
1285 | bHaveValue = true; | |||
1286 | bRes &= ( PopDouble() != 0.0 ); | |||
1287 | break; | |||
1288 | case svString : | |||
1289 | Pop(); | |||
1290 | SetError( FormulaError::NoValue ); | |||
1291 | break; | |||
1292 | case svSingleRef : | |||
1293 | { | |||
1294 | ScAddress aAdr; | |||
1295 | PopSingleRef( aAdr ); | |||
1296 | if ( nGlobalError == FormulaError::NONE ) | |||
1297 | { | |||
1298 | ScRefCellValue aCell(mrDoc, aAdr); | |||
1299 | if (aCell.hasNumeric()) | |||
1300 | { | |||
1301 | bHaveValue = true; | |||
1302 | bRes &= ( GetCellValue(aAdr, aCell) != 0.0 ); | |||
1303 | } | |||
1304 | // else: Xcl raises no error here | |||
1305 | } | |||
1306 | } | |||
1307 | break; | |||
1308 | case svDoubleRef: | |||
1309 | case svRefList: | |||
1310 | { | |||
1311 | ScRange aRange; | |||
1312 | PopDoubleRef( aRange, nParamCount, nRefInList); | |||
1313 | if ( nGlobalError == FormulaError::NONE ) | |||
1314 | { | |||
1315 | double fVal; | |||
1316 | FormulaError nErr = FormulaError::NONE; | |||
1317 | ScValueIterator aValIter( mrDoc, aRange ); | |||
1318 | if ( aValIter.GetFirst( fVal, nErr ) && nErr == FormulaError::NONE ) | |||
1319 | { | |||
1320 | bHaveValue = true; | |||
1321 | do | |||
1322 | { | |||
1323 | bRes &= ( fVal != 0.0 ); | |||
1324 | } while ( (nErr == FormulaError::NONE) && | |||
1325 | aValIter.GetNext( fVal, nErr ) ); | |||
1326 | } | |||
1327 | SetError( nErr ); | |||
1328 | } | |||
1329 | } | |||
1330 | break; | |||
1331 | case svExternalSingleRef: | |||
1332 | case svExternalDoubleRef: | |||
1333 | case svMatrix: | |||
1334 | { | |||
1335 | ScMatrixRef pMat = GetMatrix(); | |||
1336 | if ( pMat ) | |||
1337 | { | |||
1338 | bHaveValue = true; | |||
1339 | double fVal = pMat->And(); | |||
1340 | FormulaError nErr = GetDoubleErrorValue( fVal ); | |||
1341 | if ( nErr != FormulaError::NONE ) | |||
1342 | { | |||
1343 | SetError( nErr ); | |||
1344 | bRes = false; | |||
1345 | } | |||
1346 | else | |||
1347 | bRes &= (fVal != 0.0); | |||
1348 | } | |||
1349 | // else: GetMatrix did set FormulaError::IllegalParameter | |||
1350 | } | |||
1351 | break; | |||
1352 | default: | |||
1353 | Pop(); | |||
1354 | SetError( FormulaError::IllegalParameter); | |||
1355 | } | |||
1356 | } | |||
1357 | else | |||
1358 | Pop(); | |||
1359 | } | |||
1360 | if ( bHaveValue ) | |||
1361 | PushInt( int(bRes) ); | |||
1362 | else | |||
1363 | PushNoValue(); | |||
1364 | } | |||
1365 | ||||
1366 | void ScInterpreter::ScOr() | |||
1367 | { | |||
1368 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1369 | short nParamCount = GetByte(); | |||
1370 | if ( !MustHaveParamCountMin( nParamCount, 1 ) ) | |||
1371 | return; | |||
1372 | ||||
1373 | bool bHaveValue = false; | |||
1374 | bool bRes = false; | |||
1375 | size_t nRefInList = 0; | |||
1376 | while( nParamCount-- > 0) | |||
1377 | { | |||
1378 | if ( nGlobalError == FormulaError::NONE ) | |||
1379 | { | |||
1380 | switch ( GetStackType() ) | |||
1381 | { | |||
1382 | case svDouble : | |||
1383 | bHaveValue = true; | |||
1384 | bRes |= ( PopDouble() != 0.0 ); | |||
1385 | break; | |||
1386 | case svString : | |||
1387 | Pop(); | |||
1388 | SetError( FormulaError::NoValue ); | |||
1389 | break; | |||
1390 | case svSingleRef : | |||
1391 | { | |||
1392 | ScAddress aAdr; | |||
1393 | PopSingleRef( aAdr ); | |||
1394 | if ( nGlobalError == FormulaError::NONE ) | |||
1395 | { | |||
1396 | ScRefCellValue aCell(mrDoc, aAdr); | |||
1397 | if (aCell.hasNumeric()) | |||
1398 | { | |||
1399 | bHaveValue = true; | |||
1400 | bRes |= ( GetCellValue(aAdr, aCell) != 0.0 ); | |||
1401 | } | |||
1402 | // else: Xcl raises no error here | |||
1403 | } | |||
1404 | } | |||
1405 | break; | |||
1406 | case svDoubleRef: | |||
1407 | case svRefList: | |||
1408 | { | |||
1409 | ScRange aRange; | |||
1410 | PopDoubleRef( aRange, nParamCount, nRefInList); | |||
1411 | if ( nGlobalError == FormulaError::NONE ) | |||
1412 | { | |||
1413 | double fVal; | |||
1414 | FormulaError nErr = FormulaError::NONE; | |||
1415 | ScValueIterator aValIter( mrDoc, aRange ); | |||
1416 | if ( aValIter.GetFirst( fVal, nErr ) ) | |||
1417 | { | |||
1418 | bHaveValue = true; | |||
1419 | do | |||
1420 | { | |||
1421 | bRes |= ( fVal != 0.0 ); | |||
1422 | } while ( (nErr == FormulaError::NONE) && | |||
1423 | aValIter.GetNext( fVal, nErr ) ); | |||
1424 | } | |||
1425 | SetError( nErr ); | |||
1426 | } | |||
1427 | } | |||
1428 | break; | |||
1429 | case svExternalSingleRef: | |||
1430 | case svExternalDoubleRef: | |||
1431 | case svMatrix: | |||
1432 | { | |||
1433 | bHaveValue = true; | |||
1434 | ScMatrixRef pMat = GetMatrix(); | |||
1435 | if ( pMat ) | |||
1436 | { | |||
1437 | bHaveValue = true; | |||
1438 | double fVal = pMat->Or(); | |||
1439 | FormulaError nErr = GetDoubleErrorValue( fVal ); | |||
1440 | if ( nErr != FormulaError::NONE ) | |||
1441 | { | |||
1442 | SetError( nErr ); | |||
1443 | bRes = false; | |||
1444 | } | |||
1445 | else | |||
1446 | bRes |= (fVal != 0.0); | |||
1447 | } | |||
1448 | // else: GetMatrix did set FormulaError::IllegalParameter | |||
1449 | } | |||
1450 | break; | |||
1451 | default: | |||
1452 | Pop(); | |||
1453 | SetError( FormulaError::IllegalParameter); | |||
1454 | } | |||
1455 | } | |||
1456 | else | |||
1457 | Pop(); | |||
1458 | } | |||
1459 | if ( bHaveValue ) | |||
1460 | PushInt( int(bRes) ); | |||
1461 | else | |||
1462 | PushNoValue(); | |||
1463 | } | |||
1464 | ||||
1465 | void ScInterpreter::ScXor() | |||
1466 | { | |||
1467 | ||||
1468 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1469 | short nParamCount = GetByte(); | |||
1470 | if ( !MustHaveParamCountMin( nParamCount, 1 ) ) | |||
1471 | return; | |||
1472 | ||||
1473 | bool bHaveValue = false; | |||
1474 | bool bRes = false; | |||
1475 | size_t nRefInList = 0; | |||
1476 | while( nParamCount-- > 0) | |||
1477 | { | |||
1478 | if ( nGlobalError == FormulaError::NONE ) | |||
1479 | { | |||
1480 | switch ( GetStackType() ) | |||
1481 | { | |||
1482 | case svDouble : | |||
1483 | bHaveValue = true; | |||
1484 | bRes ^= ( PopDouble() != 0.0 ); | |||
1485 | break; | |||
1486 | case svString : | |||
1487 | Pop(); | |||
1488 | SetError( FormulaError::NoValue ); | |||
1489 | break; | |||
1490 | case svSingleRef : | |||
1491 | { | |||
1492 | ScAddress aAdr; | |||
1493 | PopSingleRef( aAdr ); | |||
1494 | if ( nGlobalError == FormulaError::NONE ) | |||
1495 | { | |||
1496 | ScRefCellValue aCell(mrDoc, aAdr); | |||
1497 | if (aCell.hasNumeric()) | |||
1498 | { | |||
1499 | bHaveValue = true; | |||
1500 | bRes ^= ( GetCellValue(aAdr, aCell) != 0.0 ); | |||
1501 | } | |||
1502 | /* TODO: set error? Excel doesn't have XOR, but | |||
1503 | * doesn't set an error in this case for AND and | |||
1504 | * OR. */ | |||
1505 | } | |||
1506 | } | |||
1507 | break; | |||
1508 | case svDoubleRef: | |||
1509 | case svRefList: | |||
1510 | { | |||
1511 | ScRange aRange; | |||
1512 | PopDoubleRef( aRange, nParamCount, nRefInList); | |||
1513 | if ( nGlobalError == FormulaError::NONE ) | |||
1514 | { | |||
1515 | double fVal; | |||
1516 | FormulaError nErr = FormulaError::NONE; | |||
1517 | ScValueIterator aValIter( mrDoc, aRange ); | |||
1518 | if ( aValIter.GetFirst( fVal, nErr ) ) | |||
1519 | { | |||
1520 | bHaveValue = true; | |||
1521 | do | |||
1522 | { | |||
1523 | bRes ^= ( fVal != 0.0 ); | |||
1524 | } while ( (nErr == FormulaError::NONE) && | |||
1525 | aValIter.GetNext( fVal, nErr ) ); | |||
1526 | } | |||
1527 | SetError( nErr ); | |||
1528 | } | |||
1529 | } | |||
1530 | break; | |||
1531 | case svExternalSingleRef: | |||
1532 | case svExternalDoubleRef: | |||
1533 | case svMatrix: | |||
1534 | { | |||
1535 | bHaveValue = true; | |||
1536 | ScMatrixRef pMat = GetMatrix(); | |||
1537 | if ( pMat ) | |||
1538 | { | |||
1539 | bHaveValue = true; | |||
1540 | double fVal = pMat->Xor(); | |||
1541 | FormulaError nErr = GetDoubleErrorValue( fVal ); | |||
1542 | if ( nErr != FormulaError::NONE ) | |||
1543 | { | |||
1544 | SetError( nErr ); | |||
1545 | bRes = false; | |||
1546 | } | |||
1547 | else | |||
1548 | bRes ^= ( fVal != 0.0 ); | |||
1549 | } | |||
1550 | // else: GetMatrix did set FormulaError::IllegalParameter | |||
1551 | } | |||
1552 | break; | |||
1553 | default: | |||
1554 | Pop(); | |||
1555 | SetError( FormulaError::IllegalParameter); | |||
1556 | } | |||
1557 | } | |||
1558 | else | |||
1559 | Pop(); | |||
1560 | } | |||
1561 | if ( bHaveValue ) | |||
1562 | PushInt( int(bRes) ); | |||
1563 | else | |||
1564 | PushNoValue(); | |||
1565 | } | |||
1566 | ||||
1567 | void ScInterpreter::ScNeg() | |||
1568 | { | |||
1569 | // Simple negation doesn't change current format type to number, keep | |||
1570 | // current type. | |||
1571 | nFuncFmtType = nCurFmtType; | |||
1572 | switch ( GetStackType() ) | |||
1573 | { | |||
1574 | case svMatrix : | |||
1575 | { | |||
1576 | ScMatrixRef pMat = GetMatrix(); | |||
1577 | if ( !pMat ) | |||
1578 | PushIllegalParameter(); | |||
1579 | else | |||
1580 | { | |||
1581 | SCSIZE nC, nR; | |||
1582 | pMat->GetDimensions( nC, nR ); | |||
1583 | ScMatrixRef pResMat = GetNewMat( nC, nR); | |||
1584 | if ( !pResMat ) | |||
1585 | PushIllegalArgument(); | |||
1586 | else | |||
1587 | { | |||
1588 | pMat->NegOp( *pResMat); | |||
1589 | PushMatrix( pResMat ); | |||
1590 | } | |||
1591 | } | |||
1592 | } | |||
1593 | break; | |||
1594 | default: | |||
1595 | PushDouble( -GetDouble() ); | |||
1596 | } | |||
1597 | } | |||
1598 | ||||
1599 | void ScInterpreter::ScPercentSign() | |||
1600 | { | |||
1601 | nFuncFmtType = SvNumFormatType::PERCENT; | |||
1602 | const FormulaToken* pSaveCur = pCur; | |||
1603 | sal_uInt8 nSavePar = cPar; | |||
1604 | PushInt( 100 ); | |||
1605 | cPar = 2; | |||
1606 | FormulaByteToken aDivOp( ocDiv, cPar ); | |||
1607 | pCur = &aDivOp; | |||
1608 | ScDiv(); | |||
1609 | pCur = pSaveCur; | |||
1610 | cPar = nSavePar; | |||
1611 | } | |||
1612 | ||||
1613 | void ScInterpreter::ScNot() | |||
1614 | { | |||
1615 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1616 | switch ( GetStackType() ) | |||
1617 | { | |||
1618 | case svMatrix : | |||
1619 | { | |||
1620 | ScMatrixRef pMat = GetMatrix(); | |||
1621 | if ( !pMat ) | |||
1622 | PushIllegalParameter(); | |||
1623 | else | |||
1624 | { | |||
1625 | SCSIZE nC, nR; | |||
1626 | pMat->GetDimensions( nC, nR ); | |||
1627 | ScMatrixRef pResMat = GetNewMat( nC, nR); | |||
1628 | if ( !pResMat ) | |||
1629 | PushIllegalArgument(); | |||
1630 | else | |||
1631 | { | |||
1632 | pMat->NotOp( *pResMat); | |||
1633 | PushMatrix( pResMat ); | |||
1634 | } | |||
1635 | } | |||
1636 | } | |||
1637 | break; | |||
1638 | default: | |||
1639 | PushInt( int(GetDouble() == 0.0) ); | |||
1640 | } | |||
1641 | } | |||
1642 | ||||
1643 | void ScInterpreter::ScBitAnd() | |||
1644 | { | |||
1645 | ||||
1646 | if ( !MustHaveParamCount( GetByte(), 2 ) ) | |||
1647 | return; | |||
1648 | ||||
1649 | double num1 = ::rtl::math::approxFloor( GetDouble()); | |||
1650 | double num2 = ::rtl::math::approxFloor( GetDouble()); | |||
1651 | if ( (num1 >= n2power48) || (num1 < 0) || | |||
1652 | (num2 >= n2power48) || (num2 < 0)) | |||
1653 | PushIllegalArgument(); | |||
1654 | else | |||
1655 | PushDouble (static_cast<sal_uInt64>(num1) & static_cast<sal_uInt64>(num2)); | |||
1656 | } | |||
1657 | ||||
1658 | void ScInterpreter::ScBitOr() | |||
1659 | { | |||
1660 | ||||
1661 | if ( !MustHaveParamCount( GetByte(), 2 ) ) | |||
1662 | return; | |||
1663 | ||||
1664 | double num1 = ::rtl::math::approxFloor( GetDouble()); | |||
1665 | double num2 = ::rtl::math::approxFloor( GetDouble()); | |||
1666 | if ( (num1 >= n2power48) || (num1 < 0) || | |||
1667 | (num2 >= n2power48) || (num2 < 0)) | |||
1668 | PushIllegalArgument(); | |||
1669 | else | |||
1670 | PushDouble (static_cast<sal_uInt64>(num1) | static_cast<sal_uInt64>(num2)); | |||
1671 | } | |||
1672 | ||||
1673 | void ScInterpreter::ScBitXor() | |||
1674 | { | |||
1675 | ||||
1676 | if ( !MustHaveParamCount( GetByte(), 2 ) ) | |||
1677 | return; | |||
1678 | ||||
1679 | double num1 = ::rtl::math::approxFloor( GetDouble()); | |||
1680 | double num2 = ::rtl::math::approxFloor( GetDouble()); | |||
1681 | if ( (num1 >= n2power48) || (num1 < 0) || | |||
1682 | (num2 >= n2power48) || (num2 < 0)) | |||
1683 | PushIllegalArgument(); | |||
1684 | else | |||
1685 | PushDouble (static_cast<sal_uInt64>(num1) ^ static_cast<sal_uInt64>(num2)); | |||
1686 | } | |||
1687 | ||||
1688 | void ScInterpreter::ScBitLshift() | |||
1689 | { | |||
1690 | ||||
1691 | if ( !MustHaveParamCount( GetByte(), 2 ) ) | |||
1692 | return; | |||
1693 | ||||
1694 | double fShift = ::rtl::math::approxFloor( GetDouble()); | |||
1695 | double num = ::rtl::math::approxFloor( GetDouble()); | |||
1696 | if ((num >= n2power48) || (num < 0)) | |||
1697 | PushIllegalArgument(); | |||
1698 | else | |||
1699 | { | |||
1700 | double fRes; | |||
1701 | if (fShift < 0) | |||
1702 | fRes = ::rtl::math::approxFloor( num / pow( 2.0, -fShift)); | |||
1703 | else if (fShift == 0) | |||
1704 | fRes = num; | |||
1705 | else | |||
1706 | fRes = num * pow( 2.0, fShift); | |||
1707 | PushDouble( fRes); | |||
1708 | } | |||
1709 | } | |||
1710 | ||||
1711 | void ScInterpreter::ScBitRshift() | |||
1712 | { | |||
1713 | ||||
1714 | if ( !MustHaveParamCount( GetByte(), 2 ) ) | |||
1715 | return; | |||
1716 | ||||
1717 | double fShift = ::rtl::math::approxFloor( GetDouble()); | |||
1718 | double num = ::rtl::math::approxFloor( GetDouble()); | |||
1719 | if ((num >= n2power48) || (num < 0)) | |||
1720 | PushIllegalArgument(); | |||
1721 | else | |||
1722 | { | |||
1723 | double fRes; | |||
1724 | if (fShift < 0) | |||
1725 | fRes = num * pow( 2.0, -fShift); | |||
1726 | else if (fShift == 0) | |||
1727 | fRes = num; | |||
1728 | else | |||
1729 | fRes = ::rtl::math::approxFloor( num / pow( 2.0, fShift)); | |||
1730 | PushDouble( fRes); | |||
1731 | } | |||
1732 | } | |||
1733 | ||||
1734 | void ScInterpreter::ScPi() | |||
1735 | { | |||
1736 | PushDouble(F_PI3.14159265358979323846); | |||
1737 | } | |||
1738 | ||||
1739 | void ScInterpreter::ScRandomImpl( const std::function<double( double fFirst, double fLast )>& RandomFunc, | |||
1740 | double fFirst, double fLast ) | |||
1741 | { | |||
1742 | if (bMatrixFormula) | |||
1743 | { | |||
1744 | SCCOL nCols = 0; | |||
1745 | SCROW nRows = 0; | |||
1746 | if (pMyFormulaCell) | |||
1747 | pMyFormulaCell->GetMatColsRows( nCols, nRows); | |||
1748 | ||||
1749 | if (nCols == 1 && nRows == 1) | |||
1750 | { | |||
1751 | // For compatibility with existing | |||
1752 | // com.sun.star.sheet.FunctionAccess.callFunction() calls that per | |||
1753 | // default are executed in array context unless | |||
1754 | // FA.setPropertyValue("IsArrayFunction",False) was set, return a | |||
1755 | // scalar double instead of a 1x1 matrix object. tdf#128218 | |||
1756 | PushDouble( RandomFunc( fFirst, fLast)); | |||
1757 | return; | |||
1758 | } | |||
1759 | ||||
1760 | // ScViewFunc::EnterMatrix() might be asking for | |||
1761 | // ScFormulaCell::GetResultDimensions(), which here are none so create | |||
1762 | // a 1x1 matrix at least which exactly is the case when EnterMatrix() | |||
1763 | // asks for a not selected range. | |||
1764 | if (nCols == 0) | |||
1765 | nCols = 1; | |||
1766 | if (nRows == 0) | |||
1767 | nRows = 1; | |||
1768 | ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), static_cast<SCSIZE>(nRows)); | |||
1769 | if (!pResMat) | |||
1770 | PushError( FormulaError::MatrixSize); | |||
1771 | else | |||
1772 | { | |||
1773 | for (SCCOL i=0; i < nCols; ++i) | |||
1774 | { | |||
1775 | for (SCROW j=0; j < nRows; ++j) | |||
1776 | { | |||
1777 | pResMat->PutDouble( RandomFunc( fFirst, fLast), | |||
1778 | static_cast<SCSIZE>(i), static_cast<SCSIZE>(j)); | |||
1779 | } | |||
1780 | } | |||
1781 | PushMatrix( pResMat); | |||
1782 | } | |||
1783 | } | |||
1784 | else | |||
1785 | { | |||
1786 | PushDouble( RandomFunc( fFirst, fLast)); | |||
1787 | } | |||
1788 | } | |||
1789 | ||||
1790 | void ScInterpreter::ScRandom() | |||
1791 | { | |||
1792 | auto RandomFunc = []( double, double ) | |||
1793 | { | |||
1794 | return comphelper::rng::uniform_real_distribution(); | |||
1795 | }; | |||
1796 | ScRandomImpl( RandomFunc, 0.0, 0.0); | |||
1797 | } | |||
1798 | ||||
1799 | void ScInterpreter::ScRandbetween() | |||
1800 | { | |||
1801 | if (!MustHaveParamCount( GetByte(), 2)) | |||
1802 | return; | |||
1803 | ||||
1804 | // Same like scaddins/source/analysis/analysis.cxx | |||
1805 | // AnalysisAddIn::getRandbetween() | |||
1806 | double fMax = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up); | |||
1807 | double fMin = rtl::math::round( GetDouble(), 0, rtl_math_RoundingMode_Up); | |||
1808 | if (nGlobalError != FormulaError::NONE || fMin > fMax) | |||
1809 | { | |||
1810 | PushIllegalArgument(); | |||
1811 | return; | |||
1812 | } | |||
1813 | fMax = std::nextafter( fMax+1, -DBL_MAX1.7976931348623157e+308); | |||
1814 | auto RandomFunc = []( double fFirst, double fLast ) | |||
1815 | { | |||
1816 | return floor( comphelper::rng::uniform_real_distribution( fFirst, fLast)); | |||
1817 | }; | |||
1818 | ScRandomImpl( RandomFunc, fMin, fMax); | |||
1819 | } | |||
1820 | ||||
1821 | void ScInterpreter::ScTrue() | |||
1822 | { | |||
1823 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1824 | PushInt(1); | |||
1825 | } | |||
1826 | ||||
1827 | void ScInterpreter::ScFalse() | |||
1828 | { | |||
1829 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1830 | PushInt(0); | |||
1831 | } | |||
1832 | ||||
1833 | void ScInterpreter::ScDeg() | |||
1834 | { | |||
1835 | PushDouble(basegfx::rad2deg(GetDouble())); | |||
1836 | } | |||
1837 | ||||
1838 | void ScInterpreter::ScRad() | |||
1839 | { | |||
1840 | PushDouble(basegfx::deg2rad(GetDouble())); | |||
1841 | } | |||
1842 | ||||
1843 | void ScInterpreter::ScSin() | |||
1844 | { | |||
1845 | PushDouble(::rtl::math::sin(GetDouble())); | |||
1846 | } | |||
1847 | ||||
1848 | void ScInterpreter::ScCos() | |||
1849 | { | |||
1850 | PushDouble(::rtl::math::cos(GetDouble())); | |||
1851 | } | |||
1852 | ||||
1853 | void ScInterpreter::ScTan() | |||
1854 | { | |||
1855 | PushDouble(::rtl::math::tan(GetDouble())); | |||
1856 | } | |||
1857 | ||||
1858 | void ScInterpreter::ScCot() | |||
1859 | { | |||
1860 | PushDouble(1.0 / ::rtl::math::tan(GetDouble())); | |||
1861 | } | |||
1862 | ||||
1863 | void ScInterpreter::ScArcSin() | |||
1864 | { | |||
1865 | PushDouble(asin(GetDouble())); | |||
1866 | } | |||
1867 | ||||
1868 | void ScInterpreter::ScArcCos() | |||
1869 | { | |||
1870 | PushDouble(acos(GetDouble())); | |||
1871 | } | |||
1872 | ||||
1873 | void ScInterpreter::ScArcTan() | |||
1874 | { | |||
1875 | PushDouble(atan(GetDouble())); | |||
1876 | } | |||
1877 | ||||
1878 | void ScInterpreter::ScArcCot() | |||
1879 | { | |||
1880 | PushDouble((F_PI21.57079632679489661923) - atan(GetDouble())); | |||
1881 | } | |||
1882 | ||||
1883 | void ScInterpreter::ScSinHyp() | |||
1884 | { | |||
1885 | PushDouble(sinh(GetDouble())); | |||
1886 | } | |||
1887 | ||||
1888 | void ScInterpreter::ScCosHyp() | |||
1889 | { | |||
1890 | PushDouble(cosh(GetDouble())); | |||
1891 | } | |||
1892 | ||||
1893 | void ScInterpreter::ScTanHyp() | |||
1894 | { | |||
1895 | PushDouble(tanh(GetDouble())); | |||
1896 | } | |||
1897 | ||||
1898 | void ScInterpreter::ScCotHyp() | |||
1899 | { | |||
1900 | PushDouble(1.0 / tanh(GetDouble())); | |||
1901 | } | |||
1902 | ||||
1903 | void ScInterpreter::ScArcSinHyp() | |||
1904 | { | |||
1905 | PushDouble( ::rtl::math::asinh( GetDouble())); | |||
1906 | } | |||
1907 | ||||
1908 | void ScInterpreter::ScArcCosHyp() | |||
1909 | { | |||
1910 | double fVal = GetDouble(); | |||
1911 | if (fVal < 1.0) | |||
1912 | PushIllegalArgument(); | |||
1913 | else | |||
1914 | PushDouble( ::rtl::math::acosh( fVal)); | |||
1915 | } | |||
1916 | ||||
1917 | void ScInterpreter::ScArcTanHyp() | |||
1918 | { | |||
1919 | double fVal = GetDouble(); | |||
1920 | if (fabs(fVal) >= 1.0) | |||
1921 | PushIllegalArgument(); | |||
1922 | else | |||
1923 | PushDouble( ::rtl::math::atanh( fVal)); | |||
1924 | } | |||
1925 | ||||
1926 | void ScInterpreter::ScArcCotHyp() | |||
1927 | { | |||
1928 | double nVal = GetDouble(); | |||
1929 | if (fabs(nVal) <= 1.0) | |||
1930 | PushIllegalArgument(); | |||
1931 | else | |||
1932 | PushDouble(0.5 * log((nVal + 1.0) / (nVal - 1.0))); | |||
1933 | } | |||
1934 | ||||
1935 | void ScInterpreter::ScCosecant() | |||
1936 | { | |||
1937 | PushDouble(1.0 / ::rtl::math::sin(GetDouble())); | |||
1938 | } | |||
1939 | ||||
1940 | void ScInterpreter::ScSecant() | |||
1941 | { | |||
1942 | PushDouble(1.0 / ::rtl::math::cos(GetDouble())); | |||
1943 | } | |||
1944 | ||||
1945 | void ScInterpreter::ScCosecantHyp() | |||
1946 | { | |||
1947 | PushDouble(1.0 / sinh(GetDouble())); | |||
1948 | } | |||
1949 | ||||
1950 | void ScInterpreter::ScSecantHyp() | |||
1951 | { | |||
1952 | PushDouble(1.0 / cosh(GetDouble())); | |||
1953 | } | |||
1954 | ||||
1955 | void ScInterpreter::ScExp() | |||
1956 | { | |||
1957 | PushDouble(exp(GetDouble())); | |||
1958 | } | |||
1959 | ||||
1960 | void ScInterpreter::ScSqrt() | |||
1961 | { | |||
1962 | double fVal = GetDouble(); | |||
1963 | if (fVal >= 0.0) | |||
1964 | PushDouble(sqrt(fVal)); | |||
1965 | else | |||
1966 | PushIllegalArgument(); | |||
1967 | } | |||
1968 | ||||
1969 | void ScInterpreter::ScIsEmpty() | |||
1970 | { | |||
1971 | short nRes = 0; | |||
1972 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
1973 | switch ( GetRawStackType() ) | |||
1974 | { | |||
1975 | case svEmptyCell: | |||
1976 | { | |||
1977 | FormulaConstTokenRef p = PopToken(); | |||
1978 | if (!static_cast<const ScEmptyCellToken*>(p.get())->IsInherited()) | |||
1979 | nRes = 1; | |||
1980 | } | |||
1981 | break; | |||
1982 | case svDoubleRef : | |||
1983 | case svSingleRef : | |||
1984 | { | |||
1985 | ScAddress aAdr; | |||
1986 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
1987 | break; | |||
1988 | // NOTE: this differs from COUNTBLANK() ScCountEmptyCells() that | |||
1989 | // may treat ="" in the referenced cell as blank for Excel | |||
1990 | // interoperability. | |||
1991 | ScRefCellValue aCell(mrDoc, aAdr); | |||
1992 | if (aCell.meType == CELLTYPE_NONE) | |||
1993 | nRes = 1; | |||
1994 | } | |||
1995 | break; | |||
1996 | case svExternalSingleRef: | |||
1997 | case svExternalDoubleRef: | |||
1998 | case svMatrix: | |||
1999 | { | |||
2000 | ScMatrixRef pMat = GetMatrix(); | |||
2001 | if ( !pMat ) | |||
2002 | ; // nothing | |||
2003 | else if ( !pJumpMatrix ) | |||
2004 | nRes = pMat->IsEmptyCell( 0, 0) ? 1 : 0; | |||
2005 | else | |||
2006 | { | |||
2007 | SCSIZE nCols, nRows, nC, nR; | |||
2008 | pMat->GetDimensions( nCols, nRows); | |||
2009 | pJumpMatrix->GetPos( nC, nR); | |||
2010 | if ( nC < nCols && nR < nRows ) | |||
2011 | nRes = pMat->IsEmptyCell( nC, nR) ? 1 : 0; | |||
2012 | // else: false, not empty (which is what Xcl does) | |||
2013 | } | |||
2014 | } | |||
2015 | break; | |||
2016 | default: | |||
2017 | Pop(); | |||
2018 | } | |||
2019 | nGlobalError = FormulaError::NONE; | |||
2020 | PushInt( nRes ); | |||
2021 | } | |||
2022 | ||||
2023 | bool ScInterpreter::IsString() | |||
2024 | { | |||
2025 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
2026 | bool bRes = false; | |||
2027 | switch ( GetRawStackType() ) | |||
2028 | { | |||
2029 | case svString: | |||
2030 | Pop(); | |||
2031 | bRes = true; | |||
2032 | break; | |||
2033 | case svDoubleRef : | |||
2034 | case svSingleRef : | |||
2035 | { | |||
2036 | ScAddress aAdr; | |||
2037 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
2038 | break; | |||
2039 | ||||
2040 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2041 | if (GetCellErrCode(aCell) == FormulaError::NONE) | |||
2042 | { | |||
2043 | switch (aCell.meType) | |||
2044 | { | |||
2045 | case CELLTYPE_STRING : | |||
2046 | case CELLTYPE_EDIT : | |||
2047 | bRes = true; | |||
2048 | break; | |||
2049 | case CELLTYPE_FORMULA : | |||
2050 | bRes = (!aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty()); | |||
2051 | break; | |||
2052 | default: | |||
2053 | ; // nothing | |||
2054 | } | |||
2055 | } | |||
2056 | } | |||
2057 | break; | |||
2058 | case svExternalSingleRef: | |||
2059 | { | |||
2060 | ScExternalRefCache::TokenRef pToken; | |||
2061 | PopExternalSingleRef(pToken); | |||
2062 | if (nGlobalError == FormulaError::NONE && pToken->GetType() == svString) | |||
2063 | bRes = true; | |||
2064 | } | |||
2065 | break; | |||
2066 | case svExternalDoubleRef: | |||
2067 | case svMatrix: | |||
2068 | { | |||
2069 | ScMatrixRef pMat = GetMatrix(); | |||
2070 | if ( !pMat ) | |||
2071 | ; // nothing | |||
2072 | else if ( !pJumpMatrix ) | |||
2073 | bRes = pMat->IsStringOrEmpty(0, 0) && !pMat->IsEmpty(0, 0); | |||
2074 | else | |||
2075 | { | |||
2076 | SCSIZE nCols, nRows, nC, nR; | |||
2077 | pMat->GetDimensions( nCols, nRows); | |||
2078 | pJumpMatrix->GetPos( nC, nR); | |||
2079 | if ( nC < nCols && nR < nRows ) | |||
2080 | bRes = pMat->IsStringOrEmpty( nC, nR) && !pMat->IsEmpty( nC, nR); | |||
2081 | } | |||
2082 | } | |||
2083 | break; | |||
2084 | default: | |||
2085 | Pop(); | |||
2086 | } | |||
2087 | nGlobalError = FormulaError::NONE; | |||
2088 | return bRes; | |||
2089 | } | |||
2090 | ||||
2091 | void ScInterpreter::ScIsString() | |||
2092 | { | |||
2093 | PushInt( int(IsString()) ); | |||
2094 | } | |||
2095 | ||||
2096 | void ScInterpreter::ScIsNonString() | |||
2097 | { | |||
2098 | PushInt( int(!IsString()) ); | |||
2099 | } | |||
2100 | ||||
2101 | void ScInterpreter::ScIsLogical() | |||
2102 | { | |||
2103 | bool bRes = false; | |||
2104 | switch ( GetStackType() ) | |||
2105 | { | |||
2106 | case svDoubleRef : | |||
2107 | case svSingleRef : | |||
2108 | { | |||
2109 | ScAddress aAdr; | |||
2110 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
2111 | break; | |||
2112 | ||||
2113 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2114 | if (GetCellErrCode(aCell) == FormulaError::NONE) | |||
2115 | { | |||
2116 | if (aCell.hasNumeric()) | |||
2117 | { | |||
2118 | sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell); | |||
2119 | bRes = (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL); | |||
2120 | } | |||
2121 | } | |||
2122 | } | |||
2123 | break; | |||
2124 | case svMatrix: | |||
2125 | { | |||
2126 | double fVal; | |||
2127 | svl::SharedString aStr; | |||
2128 | ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr); | |||
2129 | bRes = (nMatValType == ScMatValType::Boolean); | |||
2130 | } | |||
2131 | break; | |||
2132 | default: | |||
2133 | PopError(); | |||
2134 | if ( nGlobalError == FormulaError::NONE ) | |||
2135 | bRes = ( nCurFmtType == SvNumFormatType::LOGICAL ); | |||
2136 | } | |||
2137 | nCurFmtType = nFuncFmtType = SvNumFormatType::LOGICAL; | |||
2138 | nGlobalError = FormulaError::NONE; | |||
2139 | PushInt( int(bRes) ); | |||
2140 | } | |||
2141 | ||||
2142 | void ScInterpreter::ScType() | |||
2143 | { | |||
2144 | short nType = 0; | |||
2145 | switch ( GetStackType() ) | |||
2146 | { | |||
2147 | case svDoubleRef : | |||
2148 | case svSingleRef : | |||
2149 | { | |||
2150 | ScAddress aAdr; | |||
2151 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
2152 | break; | |||
2153 | ||||
2154 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2155 | if (GetCellErrCode(aCell) == FormulaError::NONE) | |||
2156 | { | |||
2157 | switch (aCell.meType) | |||
2158 | { | |||
2159 | // NOTE: this is Xcl nonsense! | |||
2160 | case CELLTYPE_STRING : | |||
2161 | case CELLTYPE_EDIT : | |||
2162 | nType = 2; | |||
2163 | break; | |||
2164 | case CELLTYPE_VALUE : | |||
2165 | { | |||
2166 | sal_uInt32 nFormat = GetCellNumberFormat(aAdr, aCell); | |||
2167 | if (pFormatter->GetType(nFormat) == SvNumFormatType::LOGICAL) | |||
2168 | nType = 4; | |||
2169 | else | |||
2170 | nType = 1; | |||
2171 | } | |||
2172 | break; | |||
2173 | case CELLTYPE_NONE: | |||
2174 | // always 1, s. tdf#73078 | |||
2175 | nType = 1; | |||
2176 | break; | |||
2177 | case CELLTYPE_FORMULA : | |||
2178 | nType = 8; | |||
2179 | break; | |||
2180 | default: | |||
2181 | PushIllegalArgument(); | |||
2182 | } | |||
2183 | } | |||
2184 | else | |||
2185 | nType = 16; | |||
2186 | } | |||
2187 | break; | |||
2188 | case svString: | |||
2189 | PopError(); | |||
2190 | if ( nGlobalError != FormulaError::NONE ) | |||
2191 | { | |||
2192 | nType = 16; | |||
2193 | nGlobalError = FormulaError::NONE; | |||
2194 | } | |||
2195 | else | |||
2196 | nType = 2; | |||
2197 | break; | |||
2198 | case svMatrix: | |||
2199 | PopMatrix(); | |||
2200 | if ( nGlobalError != FormulaError::NONE ) | |||
2201 | { | |||
2202 | nType = 16; | |||
2203 | nGlobalError = FormulaError::NONE; | |||
2204 | } | |||
2205 | else | |||
2206 | nType = 64; | |||
2207 | // we could return the type of one element if in JumpMatrix or | |||
2208 | // ForceArray mode, but Xcl doesn't ... | |||
2209 | break; | |||
2210 | default: | |||
2211 | PopError(); | |||
2212 | if ( nGlobalError != FormulaError::NONE ) | |||
2213 | { | |||
2214 | nType = 16; | |||
2215 | nGlobalError = FormulaError::NONE; | |||
2216 | } | |||
2217 | else | |||
2218 | nType = 1; | |||
2219 | } | |||
2220 | PushInt( nType ); | |||
2221 | } | |||
2222 | ||||
2223 | static bool lcl_FormatHasNegColor( const SvNumberformat* pFormat ) | |||
2224 | { | |||
2225 | return pFormat && pFormat->GetColor( 1 ); | |||
2226 | } | |||
2227 | ||||
2228 | static bool lcl_FormatHasOpenPar( const SvNumberformat* pFormat ) | |||
2229 | { | |||
2230 | return pFormat && (pFormat->GetFormatstring().indexOf('(') != -1); | |||
2231 | } | |||
2232 | ||||
2233 | namespace { | |||
2234 | ||||
2235 | void getFormatString(const SvNumberFormatter* pFormatter, sal_uLong nFormat, OUString& rFmtStr) | |||
2236 | { | |||
2237 | rFmtStr = pFormatter->GetCalcCellReturn( nFormat); | |||
2238 | } | |||
2239 | ||||
2240 | } | |||
2241 | ||||
2242 | void ScInterpreter::ScCell() | |||
2243 | { // ATTRIBUTE ; [REF] | |||
2244 | sal_uInt8 nParamCount = GetByte(); | |||
2245 | if( !MustHaveParamCount( nParamCount, 1, 2 ) ) | |||
2246 | return; | |||
2247 | ||||
2248 | ScAddress aCellPos( aPos ); | |||
2249 | bool bError = false; | |||
2250 | if( nParamCount == 2 ) | |||
2251 | { | |||
2252 | switch (GetStackType()) | |||
2253 | { | |||
2254 | case svExternalSingleRef: | |||
2255 | case svExternalDoubleRef: | |||
2256 | { | |||
2257 | // Let's handle external reference separately... | |||
2258 | ScCellExternal(); | |||
2259 | return; | |||
2260 | } | |||
2261 | default: | |||
2262 | ; | |||
2263 | } | |||
2264 | bError = !PopDoubleRefOrSingleRef( aCellPos ); | |||
2265 | } | |||
2266 | OUString aInfoType = GetString().getString(); | |||
2267 | if( bError || nGlobalError != FormulaError::NONE ) | |||
2268 | PushIllegalParameter(); | |||
2269 | else | |||
2270 | { | |||
2271 | ScRefCellValue aCell(mrDoc, aCellPos); | |||
2272 | ||||
2273 | ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell); | |||
2274 | ||||
2275 | // *** ADDRESS INFO *** | |||
2276 | if( aInfoType == "COL" ) | |||
2277 | { // column number (1-based) | |||
2278 | PushInt( aCellPos.Col() + 1 ); | |||
2279 | } | |||
2280 | else if( aInfoType == "ROW" ) | |||
2281 | { // row number (1-based) | |||
2282 | PushInt( aCellPos.Row() + 1 ); | |||
2283 | } | |||
2284 | else if( aInfoType == "SHEET" ) | |||
2285 | { // table number (1-based) | |||
2286 | PushInt( aCellPos.Tab() + 1 ); | |||
2287 | } | |||
2288 | else if( aInfoType == "ADDRESS" ) | |||
2289 | { // address formatted as [['FILENAME'#]$TABLE.]$COL$ROW | |||
2290 | ScRefFlags nFlags = (aCellPos.Tab() == aPos.Tab()) ? ScRefFlags::ADDR_ABS : ScRefFlags::ADDR_ABS_3D; | |||
2291 | OUString aStr(aCellPos.Format(nFlags, &mrDoc, mrDoc.GetAddressConvention())); | |||
2292 | PushString(aStr); | |||
2293 | } | |||
2294 | else if( aInfoType == "FILENAME" ) | |||
2295 | { // file name and table name: 'FILENAME'#$TABLE | |||
2296 | SCTAB nTab = aCellPos.Tab(); | |||
2297 | OUString aFuncResult; | |||
2298 | if( nTab < mrDoc.GetTableCount() ) | |||
2299 | { | |||
2300 | if( mrDoc.GetLinkMode( nTab ) == ScLinkMode::VALUE ) | |||
2301 | mrDoc.GetName( nTab, aFuncResult ); | |||
2302 | else | |||
2303 | { | |||
2304 | SfxObjectShell* pShell = mrDoc.GetDocumentShell(); | |||
2305 | if( pShell && pShell->GetMedium() ) | |||
2306 | { | |||
2307 | OUStringBuffer aBuf; | |||
2308 | aBuf.append('\''); | |||
2309 | const INetURLObject& rURLObj = pShell->GetMedium()->GetURLObject(); | |||
2310 | aBuf.append(rURLObj.GetMainURL(INetURLObject::DecodeMechanism::Unambiguous)); | |||
2311 | aBuf.append("'#$"); | |||
2312 | OUString aTabName; | |||
2313 | mrDoc.GetName( nTab, aTabName ); | |||
2314 | aBuf.append(aTabName); | |||
2315 | aFuncResult = aBuf.makeStringAndClear(); | |||
2316 | } | |||
2317 | } | |||
2318 | } | |||
2319 | PushString( aFuncResult ); | |||
2320 | } | |||
2321 | else if( aInfoType == "COORD" ) | |||
2322 | { // address, lotus 1-2-3 formatted: $TABLE:$COL$ROW | |||
2323 | // Yes, passing tab as col is intentional! | |||
2324 | OUString aCellStr1 = | |||
2325 | ScAddress( static_cast<SCCOL>(aCellPos.Tab()), 0, 0 ).Format( | |||
2326 | (ScRefFlags::COL_ABS|ScRefFlags::COL_VALID), nullptr, mrDoc.GetAddressConvention() ); | |||
2327 | OUString aCellStr2 = | |||
2328 | aCellPos.Format((ScRefFlags::COL_ABS|ScRefFlags::COL_VALID|ScRefFlags::ROW_ABS|ScRefFlags::ROW_VALID), | |||
2329 | nullptr, mrDoc.GetAddressConvention()); | |||
2330 | OUString aFuncResult = aCellStr1 + ":" + aCellStr2; | |||
2331 | PushString( aFuncResult ); | |||
2332 | } | |||
2333 | ||||
2334 | // *** CELL PROPERTIES *** | |||
2335 | else if( aInfoType == "CONTENTS" ) | |||
2336 | { // contents of the cell, no formatting | |||
2337 | if (aCell.hasString()) | |||
2338 | { | |||
2339 | svl::SharedString aStr; | |||
2340 | GetCellString(aStr, aCell); | |||
2341 | PushString( aStr ); | |||
2342 | } | |||
2343 | else | |||
2344 | PushDouble(GetCellValue(aCellPos, aCell)); | |||
2345 | } | |||
2346 | else if( aInfoType == "TYPE" ) | |||
2347 | { // b = blank; l = string (label); v = otherwise (value) | |||
2348 | sal_Unicode c; | |||
2349 | if (aCell.hasString()) | |||
2350 | c = 'l'; | |||
2351 | else | |||
2352 | c = aCell.hasNumeric() ? 'v' : 'b'; | |||
2353 | PushString( OUString(c) ); | |||
2354 | } | |||
2355 | else if( aInfoType == "WIDTH" ) | |||
2356 | { // column width (rounded off as count of zero characters in standard font and size) | |||
2357 | Printer* pPrinter = mrDoc.GetPrinter(); | |||
2358 | MapMode aOldMode( pPrinter->GetMapMode() ); | |||
2359 | vcl::Font aOldFont( pPrinter->GetFont() ); | |||
2360 | vcl::Font aDefFont; | |||
2361 | ||||
2362 | pPrinter->SetMapMode(MapMode(MapUnit::MapTwip)); | |||
2363 | // font color doesn't matter here | |||
2364 | mrDoc.GetDefPattern()->GetFont( aDefFont, SC_AUTOCOL_BLACK, pPrinter ); | |||
2365 | pPrinter->SetFont( aDefFont ); | |||
2366 | long nZeroWidth = pPrinter->GetTextWidth( OUString( '0' ) ); | |||
2367 | pPrinter->SetFont( aOldFont ); | |||
2368 | pPrinter->SetMapMode( aOldMode ); | |||
2369 | int nZeroCount = static_cast<int>(mrDoc.GetColWidth( aCellPos.Col(), aCellPos.Tab() ) / nZeroWidth); | |||
2370 | PushInt( nZeroCount ); | |||
2371 | } | |||
2372 | else if( aInfoType == "PREFIX" ) | |||
2373 | { // ' = left; " = right; ^ = centered | |||
2374 | sal_Unicode c = 0; | |||
2375 | if (aCell.hasString()) | |||
2376 | { | |||
2377 | const SvxHorJustifyItem* pJustAttr = mrDoc.GetAttr( aCellPos, ATTR_HOR_JUSTIFY ); | |||
2378 | switch( pJustAttr->GetValue() ) | |||
2379 | { | |||
2380 | case SvxCellHorJustify::Standard: | |||
2381 | case SvxCellHorJustify::Left: | |||
2382 | case SvxCellHorJustify::Block: c = '\''; break; | |||
2383 | case SvxCellHorJustify::Center: c = '^'; break; | |||
2384 | case SvxCellHorJustify::Right: c = '"'; break; | |||
2385 | case SvxCellHorJustify::Repeat: c = '\\'; break; | |||
2386 | } | |||
2387 | } | |||
2388 | PushString( OUString(c) ); | |||
2389 | } | |||
2390 | else if( aInfoType == "PROTECT" ) | |||
2391 | { // 1 = cell locked | |||
2392 | const ScProtectionAttr* pProtAttr = mrDoc.GetAttr( aCellPos, ATTR_PROTECTION ); | |||
2393 | PushInt( pProtAttr->GetProtection() ? 1 : 0 ); | |||
2394 | } | |||
2395 | ||||
2396 | // *** FORMATTING *** | |||
2397 | else if( aInfoType == "FORMAT" ) | |||
2398 | { // specific format code for standard formats | |||
2399 | OUString aFuncResult; | |||
2400 | sal_uInt32 nFormat = mrDoc.GetNumberFormat( aCellPos ); | |||
2401 | getFormatString(pFormatter, nFormat, aFuncResult); | |||
2402 | PushString( aFuncResult ); | |||
2403 | } | |||
2404 | else if( aInfoType == "COLOR" ) | |||
2405 | { // 1 = negative values are colored, otherwise 0 | |||
2406 | const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) ); | |||
2407 | PushInt( lcl_FormatHasNegColor( pFormat ) ? 1 : 0 ); | |||
2408 | } | |||
2409 | else if( aInfoType == "PARENTHESES" ) | |||
2410 | { // 1 = format string contains a '(' character, otherwise 0 | |||
2411 | const SvNumberformat* pFormat = pFormatter->GetEntry( mrDoc.GetNumberFormat( aCellPos ) ); | |||
2412 | PushInt( lcl_FormatHasOpenPar( pFormat ) ? 1 : 0 ); | |||
2413 | } | |||
2414 | else | |||
2415 | PushIllegalArgument(); | |||
2416 | } | |||
2417 | } | |||
2418 | ||||
2419 | void ScInterpreter::ScCellExternal() | |||
2420 | { | |||
2421 | sal_uInt16 nFileId; | |||
2422 | OUString aTabName; | |||
2423 | ScSingleRefData aRef; | |||
2424 | ScExternalRefCache::TokenRef pToken; | |||
2425 | ScExternalRefCache::CellFormat aFmt; | |||
2426 | PopExternalSingleRef(nFileId, aTabName, aRef, pToken, &aFmt); | |||
2427 | if (nGlobalError != FormulaError::NONE) | |||
2428 | { | |||
2429 | PushError( nGlobalError); | |||
2430 | return; | |||
2431 | } | |||
2432 | ||||
2433 | OUString aInfoType = GetString().getString(); | |||
2434 | if (nGlobalError != FormulaError::NONE) | |||
2435 | { | |||
2436 | PushError( nGlobalError); | |||
2437 | return; | |||
2438 | } | |||
2439 | ||||
2440 | SCCOL nCol; | |||
2441 | SCROW nRow; | |||
2442 | SCTAB nTab; | |||
2443 | aRef.SetAbsTab(0); // external ref has a tab index of -1, which SingleRefToVars() don't like. | |||
2444 | SingleRefToVars(aRef, nCol, nRow, nTab); | |||
2445 | if (nGlobalError != FormulaError::NONE) | |||
2446 | { | |||
2447 | PushIllegalParameter(); | |||
2448 | return; | |||
2449 | } | |||
2450 | aRef.SetAbsTab(-1); // revert the value. | |||
2451 | ||||
2452 | ScCellKeywordTranslator::transKeyword(aInfoType, ScGlobal::GetLocale(), ocCell); | |||
2453 | ScExternalRefManager* pRefMgr = mrDoc.GetExternalRefManager(); | |||
2454 | ||||
2455 | if ( aInfoType == "COL" ) | |||
2456 | PushInt(nCol + 1); | |||
2457 | else if ( aInfoType == "ROW" ) | |||
2458 | PushInt(nRow + 1); | |||
2459 | else if ( aInfoType == "SHEET" ) | |||
2460 | { | |||
2461 | // For SHEET, No idea what number we should set, but let's always set | |||
2462 | // 1 if the external sheet exists, no matter what sheet. Excel does | |||
2463 | // the same. | |||
2464 | if (pRefMgr->getCacheTable(nFileId, aTabName, false)) | |||
2465 | PushInt(1); | |||
2466 | else | |||
2467 | SetError(FormulaError::NoName); | |||
2468 | } | |||
2469 | else if ( aInfoType == "ADDRESS" ) | |||
2470 | { | |||
2471 | // ODF 1.2 says we need to always display address using the ODF A1 grammar. | |||
2472 | ScTokenArray aArray(mrDoc); | |||
2473 | aArray.AddExternalSingleReference(nFileId, svl::SharedString( aTabName), aRef); // string not interned | |||
2474 | ScCompiler aComp(mrDoc, aPos, aArray, formula::FormulaGrammar::GRAM_ODFF_A1); | |||
2475 | OUString aStr; | |||
2476 | aComp.CreateStringFromTokenArray(aStr); | |||
2477 | PushString(aStr); | |||
2478 | } | |||
2479 | else if ( aInfoType == "FILENAME" ) | |||
2480 | { | |||
2481 | // 'file URI'#$SheetName | |||
2482 | ||||
2483 | const OUString* p = pRefMgr->getExternalFileName(nFileId); | |||
2484 | if (!p) | |||
2485 | { | |||
2486 | // In theory this should never happen... | |||
2487 | SetError(FormulaError::NoName); | |||
2488 | return; | |||
2489 | } | |||
2490 | ||||
2491 | OUString aBuf = "'" + *p + "'#$" + aTabName; | |||
2492 | PushString(aBuf); | |||
2493 | } | |||
2494 | else if ( aInfoType == "CONTENTS" ) | |||
2495 | { | |||
2496 | switch (pToken->GetType()) | |||
2497 | { | |||
2498 | case svString: | |||
2499 | PushString(pToken->GetString()); | |||
2500 | break; | |||
2501 | case svDouble: | |||
2502 | PushString(OUString::number(pToken->GetDouble())); | |||
2503 | break; | |||
2504 | case svError: | |||
2505 | PushString(ScGlobal::GetErrorString(pToken->GetError())); | |||
2506 | break; | |||
2507 | default: | |||
2508 | PushString(ScGlobal::GetEmptyOUString()); | |||
2509 | } | |||
2510 | } | |||
2511 | else if ( aInfoType == "TYPE" ) | |||
2512 | { | |||
2513 | sal_Unicode c = 'v'; | |||
2514 | switch (pToken->GetType()) | |||
2515 | { | |||
2516 | case svString: | |||
2517 | c = 'l'; | |||
2518 | break; | |||
2519 | case svEmptyCell: | |||
2520 | c = 'b'; | |||
2521 | break; | |||
2522 | default: | |||
2523 | ; | |||
2524 | } | |||
2525 | PushString(OUString(c)); | |||
2526 | } | |||
2527 | else if ( aInfoType == "FORMAT" ) | |||
2528 | { | |||
2529 | OUString aFmtStr; | |||
2530 | sal_uLong nFmt = aFmt.mbIsSet ? aFmt.mnIndex : 0; | |||
2531 | getFormatString(pFormatter, nFmt, aFmtStr); | |||
2532 | PushString(aFmtStr); | |||
2533 | } | |||
2534 | else if ( aInfoType == "COLOR" ) | |||
2535 | { | |||
2536 | // 1 = negative values are colored, otherwise 0 | |||
2537 | int nVal = 0; | |||
2538 | if (aFmt.mbIsSet) | |||
2539 | { | |||
2540 | const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex); | |||
2541 | nVal = lcl_FormatHasNegColor(pFormat) ? 1 : 0; | |||
2542 | } | |||
2543 | PushInt(nVal); | |||
2544 | } | |||
2545 | else if ( aInfoType == "PARENTHESES" ) | |||
2546 | { | |||
2547 | // 1 = format string contains a '(' character, otherwise 0 | |||
2548 | int nVal = 0; | |||
2549 | if (aFmt.mbIsSet) | |||
2550 | { | |||
2551 | const SvNumberformat* pFormat = pFormatter->GetEntry(aFmt.mnIndex); | |||
2552 | nVal = lcl_FormatHasOpenPar(pFormat) ? 1 : 0; | |||
2553 | } | |||
2554 | PushInt(nVal); | |||
2555 | } | |||
2556 | else | |||
2557 | PushIllegalParameter(); | |||
2558 | } | |||
2559 | ||||
2560 | void ScInterpreter::ScIsRef() | |||
2561 | { | |||
2562 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
2563 | bool bRes = false; | |||
2564 | switch ( GetStackType() ) | |||
2565 | { | |||
2566 | case svSingleRef : | |||
2567 | { | |||
2568 | ScAddress aAdr; | |||
2569 | PopSingleRef( aAdr ); | |||
2570 | if ( nGlobalError == FormulaError::NONE ) | |||
2571 | bRes = true; | |||
2572 | } | |||
2573 | break; | |||
2574 | case svDoubleRef : | |||
2575 | { | |||
2576 | ScRange aRange; | |||
2577 | PopDoubleRef( aRange ); | |||
2578 | if ( nGlobalError == FormulaError::NONE ) | |||
2579 | bRes = true; | |||
2580 | } | |||
2581 | break; | |||
2582 | case svRefList : | |||
2583 | { | |||
2584 | FormulaConstTokenRef x = PopToken(); | |||
2585 | if ( nGlobalError == FormulaError::NONE ) | |||
2586 | bRes = !x->GetRefList()->empty(); | |||
2587 | } | |||
2588 | break; | |||
2589 | case svExternalSingleRef: | |||
2590 | { | |||
2591 | ScExternalRefCache::TokenRef pToken; | |||
2592 | PopExternalSingleRef(pToken); | |||
2593 | if (nGlobalError == FormulaError::NONE) | |||
2594 | bRes = true; | |||
2595 | } | |||
2596 | break; | |||
2597 | case svExternalDoubleRef: | |||
2598 | { | |||
2599 | ScExternalRefCache::TokenArrayRef pArray; | |||
2600 | PopExternalDoubleRef(pArray); | |||
2601 | if (nGlobalError == FormulaError::NONE) | |||
2602 | bRes = true; | |||
2603 | } | |||
2604 | break; | |||
2605 | default: | |||
2606 | Pop(); | |||
2607 | } | |||
2608 | nGlobalError = FormulaError::NONE; | |||
2609 | PushInt( int(bRes) ); | |||
2610 | } | |||
2611 | ||||
2612 | void ScInterpreter::ScIsValue() | |||
2613 | { | |||
2614 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
2615 | bool bRes = false; | |||
2616 | switch ( GetRawStackType() ) | |||
2617 | { | |||
2618 | case svDouble: | |||
2619 | Pop(); | |||
2620 | bRes = true; | |||
2621 | break; | |||
2622 | case svDoubleRef : | |||
2623 | case svSingleRef : | |||
2624 | { | |||
2625 | ScAddress aAdr; | |||
2626 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
2627 | break; | |||
2628 | ||||
2629 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2630 | if (GetCellErrCode(aCell) == FormulaError::NONE) | |||
2631 | { | |||
2632 | switch (aCell.meType) | |||
2633 | { | |||
2634 | case CELLTYPE_VALUE : | |||
2635 | bRes = true; | |||
2636 | break; | |||
2637 | case CELLTYPE_FORMULA : | |||
2638 | bRes = (aCell.mpFormula->IsValue() && !aCell.mpFormula->IsEmpty()); | |||
2639 | break; | |||
2640 | default: | |||
2641 | ; // nothing | |||
2642 | } | |||
2643 | } | |||
2644 | } | |||
2645 | break; | |||
2646 | case svExternalSingleRef: | |||
2647 | { | |||
2648 | ScExternalRefCache::TokenRef pToken; | |||
2649 | PopExternalSingleRef(pToken); | |||
2650 | if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble) | |||
2651 | bRes = true; | |||
2652 | } | |||
2653 | break; | |||
2654 | case svExternalDoubleRef: | |||
2655 | case svMatrix: | |||
2656 | { | |||
2657 | ScMatrixRef pMat = GetMatrix(); | |||
2658 | if ( !pMat ) | |||
2659 | ; // nothing | |||
2660 | else if ( !pJumpMatrix ) | |||
2661 | { | |||
2662 | if (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NONE) | |||
2663 | bRes = pMat->IsValue( 0, 0); | |||
2664 | } | |||
2665 | else | |||
2666 | { | |||
2667 | SCSIZE nCols, nRows, nC, nR; | |||
2668 | pMat->GetDimensions( nCols, nRows); | |||
2669 | pJumpMatrix->GetPos( nC, nR); | |||
2670 | if ( nC < nCols && nR < nRows ) | |||
2671 | if (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NONE) | |||
2672 | bRes = pMat->IsValue( nC, nR); | |||
2673 | } | |||
2674 | } | |||
2675 | break; | |||
2676 | default: | |||
2677 | Pop(); | |||
2678 | } | |||
2679 | nGlobalError = FormulaError::NONE; | |||
2680 | PushInt( int(bRes) ); | |||
2681 | } | |||
2682 | ||||
2683 | void ScInterpreter::ScIsFormula() | |||
2684 | { | |||
2685 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
2686 | bool bRes = false; | |||
2687 | switch ( GetStackType() ) | |||
2688 | { | |||
2689 | case svDoubleRef : | |||
2690 | if (IsInArrayContext()) | |||
2691 | { | |||
2692 | SCCOL nCol1, nCol2; | |||
2693 | SCROW nRow1, nRow2; | |||
2694 | SCTAB nTab1, nTab2; | |||
2695 | PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
2696 | if (nGlobalError != FormulaError::NONE) | |||
2697 | { | |||
2698 | PushError( nGlobalError); | |||
2699 | return; | |||
2700 | } | |||
2701 | if (nTab1 != nTab2) | |||
2702 | { | |||
2703 | PushIllegalArgument(); | |||
2704 | return; | |||
2705 | } | |||
2706 | ||||
2707 | ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCol2 - nCol1 + 1), | |||
2708 | static_cast<SCSIZE>(nRow2 - nRow1 + 1), true); | |||
2709 | if (!pResMat) | |||
2710 | { | |||
2711 | PushError( FormulaError::MatrixSize); | |||
2712 | return; | |||
2713 | } | |||
2714 | ||||
2715 | /* TODO: we really should have a gap-aware cell iterator. */ | |||
2716 | SCSIZE i=0, j=0; | |||
2717 | ScAddress aAdr( 0, 0, nTab1); | |||
2718 | for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) | |||
2719 | { | |||
2720 | aAdr.SetCol(nCol); | |||
2721 | for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) | |||
2722 | { | |||
2723 | aAdr.SetRow(nRow); | |||
2724 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2725 | pResMat->PutBoolean( (aCell.meType == CELLTYPE_FORMULA), i,j); | |||
2726 | ++j; | |||
2727 | } | |||
2728 | ++i; | |||
2729 | j = 0; | |||
2730 | } | |||
2731 | ||||
2732 | PushMatrix( pResMat); | |||
2733 | return; | |||
2734 | } | |||
2735 | [[fallthrough]]; | |||
2736 | case svSingleRef : | |||
2737 | { | |||
2738 | ScAddress aAdr; | |||
2739 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
2740 | break; | |||
2741 | ||||
2742 | bRes = (mrDoc.GetCellType(aAdr) == CELLTYPE_FORMULA); | |||
2743 | } | |||
2744 | break; | |||
2745 | default: | |||
2746 | Pop(); | |||
2747 | } | |||
2748 | nGlobalError = FormulaError::NONE; | |||
2749 | PushInt( int(bRes) ); | |||
2750 | } | |||
2751 | ||||
2752 | void ScInterpreter::ScFormula() | |||
2753 | { | |||
2754 | OUString aFormula; | |||
2755 | switch ( GetStackType() ) | |||
2756 | { | |||
2757 | case svDoubleRef : | |||
2758 | if (IsInArrayContext()) | |||
2759 | { | |||
2760 | SCCOL nCol1, nCol2; | |||
2761 | SCROW nRow1, nRow2; | |||
2762 | SCTAB nTab1, nTab2; | |||
2763 | PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
2764 | if (nGlobalError != FormulaError::NONE) | |||
2765 | break; | |||
2766 | ||||
2767 | if (nTab1 != nTab2) | |||
2768 | { | |||
2769 | SetError( FormulaError::IllegalArgument); | |||
2770 | break; | |||
2771 | } | |||
2772 | ||||
2773 | ScMatrixRef pResMat = GetNewMat( nCol2 - nCol1 + 1, nRow2 - nRow1 + 1, true); | |||
2774 | if (!pResMat) | |||
2775 | break; | |||
2776 | ||||
2777 | /* TODO: use a column iterator instead? */ | |||
2778 | SCSIZE i=0, j=0; | |||
2779 | ScAddress aAdr(0,0,nTab1); | |||
2780 | for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) | |||
2781 | { | |||
2782 | aAdr.SetCol(nCol); | |||
2783 | for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) | |||
2784 | { | |||
2785 | aAdr.SetRow(nRow); | |||
2786 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2787 | switch (aCell.meType) | |||
2788 | { | |||
2789 | case CELLTYPE_FORMULA : | |||
2790 | aCell.mpFormula->GetFormula(aFormula, formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext); | |||
2791 | pResMat->PutString( mrStrPool.intern( aFormula), i,j); | |||
2792 | break; | |||
2793 | default: | |||
2794 | pResMat->PutError( FormulaError::NotAvailable, i,j); | |||
2795 | } | |||
2796 | ++j; | |||
2797 | } | |||
2798 | ++i; | |||
2799 | j = 0; | |||
2800 | } | |||
2801 | ||||
2802 | PushMatrix( pResMat); | |||
2803 | return; | |||
2804 | } | |||
2805 | [[fallthrough]]; | |||
2806 | case svSingleRef : | |||
2807 | { | |||
2808 | ScAddress aAdr; | |||
2809 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
2810 | break; | |||
2811 | ||||
2812 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2813 | switch (aCell.meType) | |||
2814 | { | |||
2815 | case CELLTYPE_FORMULA : | |||
2816 | aCell.mpFormula->GetFormula(aFormula, formula::FormulaGrammar::GRAM_UNSPECIFIED, &mrContext); | |||
2817 | break; | |||
2818 | default: | |||
2819 | SetError( FormulaError::NotAvailable ); | |||
2820 | } | |||
2821 | } | |||
2822 | break; | |||
2823 | default: | |||
2824 | Pop(); | |||
2825 | SetError( FormulaError::NotAvailable ); | |||
2826 | } | |||
2827 | PushString( aFormula ); | |||
2828 | } | |||
2829 | ||||
2830 | void ScInterpreter::ScIsNV() | |||
2831 | { | |||
2832 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
2833 | bool bRes = false; | |||
2834 | switch ( GetStackType() ) | |||
2835 | { | |||
2836 | case svDoubleRef : | |||
2837 | case svSingleRef : | |||
2838 | { | |||
2839 | ScAddress aAdr; | |||
2840 | bool bOk = PopDoubleRefOrSingleRef( aAdr ); | |||
2841 | if ( nGlobalError == FormulaError::NotAvailable ) | |||
2842 | bRes = true; | |||
2843 | else if (bOk) | |||
2844 | { | |||
2845 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2846 | FormulaError nErr = GetCellErrCode(aCell); | |||
2847 | bRes = (nErr == FormulaError::NotAvailable); | |||
2848 | } | |||
2849 | } | |||
2850 | break; | |||
2851 | case svExternalSingleRef: | |||
2852 | { | |||
2853 | ScExternalRefCache::TokenRef pToken; | |||
2854 | PopExternalSingleRef(pToken); | |||
2855 | if (nGlobalError == FormulaError::NotAvailable || | |||
2856 | (pToken && pToken->GetType() == svError && pToken->GetError() == FormulaError::NotAvailable)) | |||
2857 | bRes = true; | |||
2858 | } | |||
2859 | break; | |||
2860 | case svExternalDoubleRef: | |||
2861 | case svMatrix: | |||
2862 | { | |||
2863 | ScMatrixRef pMat = GetMatrix(); | |||
2864 | if ( !pMat ) | |||
2865 | ; // nothing | |||
2866 | else if ( !pJumpMatrix ) | |||
2867 | bRes = (pMat->GetErrorIfNotString( 0, 0) == FormulaError::NotAvailable); | |||
2868 | else | |||
2869 | { | |||
2870 | SCSIZE nCols, nRows, nC, nR; | |||
2871 | pMat->GetDimensions( nCols, nRows); | |||
2872 | pJumpMatrix->GetPos( nC, nR); | |||
2873 | if ( nC < nCols && nR < nRows ) | |||
2874 | bRes = (pMat->GetErrorIfNotString( nC, nR) == FormulaError::NotAvailable); | |||
2875 | } | |||
2876 | } | |||
2877 | break; | |||
2878 | default: | |||
2879 | PopError(); | |||
2880 | if ( nGlobalError == FormulaError::NotAvailable ) | |||
2881 | bRes = true; | |||
2882 | } | |||
2883 | nGlobalError = FormulaError::NONE; | |||
2884 | PushInt( int(bRes) ); | |||
2885 | } | |||
2886 | ||||
2887 | void ScInterpreter::ScIsErr() | |||
2888 | { | |||
2889 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
2890 | bool bRes = false; | |||
2891 | switch ( GetStackType() ) | |||
2892 | { | |||
2893 | case svDoubleRef : | |||
2894 | case svSingleRef : | |||
2895 | { | |||
2896 | ScAddress aAdr; | |||
2897 | bool bOk = PopDoubleRefOrSingleRef( aAdr ); | |||
2898 | if ( !bOk || (nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) ) | |||
2899 | bRes = true; | |||
2900 | else | |||
2901 | { | |||
2902 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2903 | FormulaError nErr = GetCellErrCode(aCell); | |||
2904 | bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable); | |||
2905 | } | |||
2906 | } | |||
2907 | break; | |||
2908 | case svExternalSingleRef: | |||
2909 | { | |||
2910 | ScExternalRefCache::TokenRef pToken; | |||
2911 | PopExternalSingleRef(pToken); | |||
2912 | if ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pToken || | |||
2913 | (pToken->GetType() == svError && pToken->GetError() != FormulaError::NotAvailable)) | |||
2914 | bRes = true; | |||
2915 | } | |||
2916 | break; | |||
2917 | case svExternalDoubleRef: | |||
2918 | case svMatrix: | |||
2919 | { | |||
2920 | ScMatrixRef pMat = GetMatrix(); | |||
2921 | if ( nGlobalError != FormulaError::NONE || !pMat ) | |||
2922 | bRes = ((nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable) || !pMat); | |||
2923 | else if ( !pJumpMatrix ) | |||
2924 | { | |||
2925 | FormulaError nErr = pMat->GetErrorIfNotString( 0, 0); | |||
2926 | bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable); | |||
2927 | } | |||
2928 | else | |||
2929 | { | |||
2930 | SCSIZE nCols, nRows, nC, nR; | |||
2931 | pMat->GetDimensions( nCols, nRows); | |||
2932 | pJumpMatrix->GetPos( nC, nR); | |||
2933 | if ( nC < nCols && nR < nRows ) | |||
2934 | { | |||
2935 | FormulaError nErr = pMat->GetErrorIfNotString( nC, nR); | |||
2936 | bRes = (nErr != FormulaError::NONE && nErr != FormulaError::NotAvailable); | |||
2937 | } | |||
2938 | } | |||
2939 | } | |||
2940 | break; | |||
2941 | default: | |||
2942 | PopError(); | |||
2943 | if ( nGlobalError != FormulaError::NONE && nGlobalError != FormulaError::NotAvailable ) | |||
2944 | bRes = true; | |||
2945 | } | |||
2946 | nGlobalError = FormulaError::NONE; | |||
2947 | PushInt( int(bRes) ); | |||
2948 | } | |||
2949 | ||||
2950 | void ScInterpreter::ScIsError() | |||
2951 | { | |||
2952 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
2953 | bool bRes = false; | |||
2954 | switch ( GetStackType() ) | |||
2955 | { | |||
2956 | case svDoubleRef : | |||
2957 | case svSingleRef : | |||
2958 | { | |||
2959 | ScAddress aAdr; | |||
2960 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
2961 | { | |||
2962 | bRes = true; | |||
2963 | break; | |||
2964 | } | |||
2965 | if ( nGlobalError != FormulaError::NONE ) | |||
2966 | bRes = true; | |||
2967 | else | |||
2968 | { | |||
2969 | ScRefCellValue aCell(mrDoc, aAdr); | |||
2970 | bRes = (GetCellErrCode(aCell) != FormulaError::NONE); | |||
2971 | } | |||
2972 | } | |||
2973 | break; | |||
2974 | case svExternalSingleRef: | |||
2975 | { | |||
2976 | ScExternalRefCache::TokenRef pToken; | |||
2977 | PopExternalSingleRef(pToken); | |||
2978 | if (nGlobalError != FormulaError::NONE || pToken->GetType() == svError) | |||
2979 | bRes = true; | |||
2980 | } | |||
2981 | break; | |||
2982 | case svExternalDoubleRef: | |||
2983 | case svMatrix: | |||
2984 | { | |||
2985 | ScMatrixRef pMat = GetMatrix(); | |||
2986 | if ( nGlobalError != FormulaError::NONE || !pMat ) | |||
2987 | bRes = true; | |||
2988 | else if ( !pJumpMatrix ) | |||
2989 | bRes = (pMat->GetErrorIfNotString( 0, 0) != FormulaError::NONE); | |||
2990 | else | |||
2991 | { | |||
2992 | SCSIZE nCols, nRows, nC, nR; | |||
2993 | pMat->GetDimensions( nCols, nRows); | |||
2994 | pJumpMatrix->GetPos( nC, nR); | |||
2995 | if ( nC < nCols && nR < nRows ) | |||
2996 | bRes = (pMat->GetErrorIfNotString( nC, nR) != FormulaError::NONE); | |||
2997 | } | |||
2998 | } | |||
2999 | break; | |||
3000 | default: | |||
3001 | PopError(); | |||
3002 | if ( nGlobalError != FormulaError::NONE ) | |||
3003 | bRes = true; | |||
3004 | } | |||
3005 | nGlobalError = FormulaError::NONE; | |||
3006 | PushInt( int(bRes) ); | |||
3007 | } | |||
3008 | ||||
3009 | bool ScInterpreter::IsEven() | |||
3010 | { | |||
3011 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
3012 | bool bRes = false; | |||
3013 | double fVal = 0.0; | |||
3014 | switch ( GetStackType() ) | |||
3015 | { | |||
3016 | case svDoubleRef : | |||
3017 | case svSingleRef : | |||
3018 | { | |||
3019 | ScAddress aAdr; | |||
3020 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
3021 | break; | |||
3022 | ||||
3023 | ScRefCellValue aCell(mrDoc, aAdr); | |||
3024 | FormulaError nErr = GetCellErrCode(aCell); | |||
3025 | if (nErr != FormulaError::NONE) | |||
3026 | SetError(nErr); | |||
3027 | else | |||
3028 | { | |||
3029 | switch (aCell.meType) | |||
3030 | { | |||
3031 | case CELLTYPE_VALUE : | |||
3032 | fVal = GetCellValue(aAdr, aCell); | |||
3033 | bRes = true; | |||
3034 | break; | |||
3035 | case CELLTYPE_FORMULA : | |||
3036 | if (aCell.mpFormula->IsValue()) | |||
3037 | { | |||
3038 | fVal = GetCellValue(aAdr, aCell); | |||
3039 | bRes = true; | |||
3040 | } | |||
3041 | break; | |||
3042 | default: | |||
3043 | ; // nothing | |||
3044 | } | |||
3045 | } | |||
3046 | } | |||
3047 | break; | |||
3048 | case svDouble: | |||
3049 | { | |||
3050 | fVal = PopDouble(); | |||
3051 | bRes = true; | |||
3052 | } | |||
3053 | break; | |||
3054 | case svExternalSingleRef: | |||
3055 | { | |||
3056 | ScExternalRefCache::TokenRef pToken; | |||
3057 | PopExternalSingleRef(pToken); | |||
3058 | if (nGlobalError == FormulaError::NONE && pToken->GetType() == svDouble) | |||
3059 | { | |||
3060 | fVal = pToken->GetDouble(); | |||
3061 | bRes = true; | |||
3062 | } | |||
3063 | } | |||
3064 | break; | |||
3065 | case svExternalDoubleRef: | |||
3066 | case svMatrix: | |||
3067 | { | |||
3068 | ScMatrixRef pMat = GetMatrix(); | |||
3069 | if ( !pMat ) | |||
3070 | ; // nothing | |||
3071 | else if ( !pJumpMatrix ) | |||
3072 | { | |||
3073 | bRes = pMat->IsValue( 0, 0); | |||
3074 | if ( bRes ) | |||
3075 | fVal = pMat->GetDouble( 0, 0); | |||
3076 | } | |||
3077 | else | |||
3078 | { | |||
3079 | SCSIZE nCols, nRows, nC, nR; | |||
3080 | pMat->GetDimensions( nCols, nRows); | |||
3081 | pJumpMatrix->GetPos( nC, nR); | |||
3082 | if ( nC < nCols && nR < nRows ) | |||
3083 | { | |||
3084 | bRes = pMat->IsValue( nC, nR); | |||
3085 | if ( bRes ) | |||
3086 | fVal = pMat->GetDouble( nC, nR); | |||
3087 | } | |||
3088 | else | |||
3089 | SetError( FormulaError::NoValue); | |||
3090 | } | |||
3091 | } | |||
3092 | break; | |||
3093 | default: | |||
3094 | ; // nothing | |||
3095 | } | |||
3096 | if ( !bRes ) | |||
3097 | SetError( FormulaError::IllegalParameter); | |||
3098 | else | |||
3099 | bRes = ( fmod( ::rtl::math::approxFloor( fabs( fVal ) ), 2.0 ) < 0.5 ); | |||
3100 | return bRes; | |||
3101 | } | |||
3102 | ||||
3103 | void ScInterpreter::ScIsEven() | |||
3104 | { | |||
3105 | PushInt( int(IsEven()) ); | |||
3106 | } | |||
3107 | ||||
3108 | void ScInterpreter::ScIsOdd() | |||
3109 | { | |||
3110 | PushInt( int(!IsEven()) ); | |||
3111 | } | |||
3112 | ||||
3113 | void ScInterpreter::ScN() | |||
3114 | { | |||
3115 | FormulaError nErr = nGlobalError; | |||
3116 | nGlobalError = FormulaError::NONE; | |||
3117 | // Temporarily override the ConvertStringToValue() error for | |||
3118 | // GetCellValue() / GetCellValueOrZero() | |||
3119 | FormulaError nSErr = mnStringNoValueError; | |||
3120 | mnStringNoValueError = FormulaError::CellNoValue; | |||
3121 | double fVal = GetDouble(); | |||
3122 | mnStringNoValueError = nSErr; | |||
3123 | if (nErr != FormulaError::NONE) | |||
3124 | nGlobalError = nErr; // preserve previous error if any | |||
3125 | else if (nGlobalError == FormulaError::CellNoValue) | |||
3126 | nGlobalError = FormulaError::NONE; // reset temporary detection error | |||
3127 | PushDouble(fVal); | |||
3128 | } | |||
3129 | ||||
3130 | void ScInterpreter::ScTrim() | |||
3131 | { | |||
3132 | // Doesn't only trim but also removes duplicated blanks within! | |||
3133 | OUString aVal = comphelper::string::strip(GetString().getString(), ' '); | |||
3134 | OUStringBuffer aStr; | |||
3135 | const sal_Unicode* p = aVal.getStr(); | |||
3136 | const sal_Unicode* const pEnd = p + aVal.getLength(); | |||
3137 | while ( p < pEnd ) | |||
3138 | { | |||
3139 | if ( *p != ' ' || p[-1] != ' ' ) // first can't be ' ', so -1 is fine | |||
3140 | aStr.append(*p); | |||
3141 | p++; | |||
3142 | } | |||
3143 | PushString(aStr.makeStringAndClear()); | |||
3144 | } | |||
3145 | ||||
3146 | void ScInterpreter::ScUpper() | |||
3147 | { | |||
3148 | OUString aString = ScGlobal::getCharClassPtr()->uppercase(GetString().getString()); | |||
3149 | PushString(aString); | |||
3150 | } | |||
3151 | ||||
3152 | void ScInterpreter::ScProper() | |||
3153 | { | |||
3154 | //2do: what to do with I18N-CJK ?!? | |||
3155 | OUStringBuffer aStr(GetString().getString()); | |||
3156 | const sal_Int32 nLen = aStr.getLength(); | |||
3157 | if ( nLen > 0 ) | |||
3158 | { | |||
3159 | OUString aUpr(ScGlobal::getCharClassPtr()->uppercase(aStr.toString())); | |||
3160 | OUString aLwr(ScGlobal::getCharClassPtr()->lowercase(aStr.toString())); | |||
3161 | aStr[0] = aUpr[0]; | |||
3162 | sal_Int32 nPos = 1; | |||
3163 | while( nPos < nLen ) | |||
3164 | { | |||
3165 | OUString aTmpStr( aStr[nPos-1] ); | |||
3166 | if ( !ScGlobal::getCharClassPtr()->isLetter( aTmpStr, 0 ) ) | |||
3167 | aStr[nPos] = aUpr[nPos]; | |||
3168 | else | |||
3169 | aStr[nPos] = aLwr[nPos]; | |||
3170 | ++nPos; | |||
3171 | } | |||
3172 | } | |||
3173 | PushString(aStr.makeStringAndClear()); | |||
3174 | } | |||
3175 | ||||
3176 | void ScInterpreter::ScLower() | |||
3177 | { | |||
3178 | OUString aString = ScGlobal::getCharClassPtr()->lowercase(GetString().getString()); | |||
3179 | PushString(aString); | |||
3180 | } | |||
3181 | ||||
3182 | void ScInterpreter::ScLen() | |||
3183 | { | |||
3184 | OUString aStr = GetString().getString(); | |||
3185 | sal_Int32 nIdx = 0; | |||
3186 | sal_Int32 nCnt = 0; | |||
3187 | while ( nIdx < aStr.getLength() ) | |||
3188 | { | |||
3189 | aStr.iterateCodePoints( &nIdx ); | |||
3190 | ++nCnt; | |||
3191 | } | |||
3192 | PushDouble( nCnt ); | |||
3193 | } | |||
3194 | ||||
3195 | void ScInterpreter::ScT() | |||
3196 | { | |||
3197 | switch ( GetStackType() ) | |||
3198 | { | |||
3199 | case svDoubleRef : | |||
3200 | case svSingleRef : | |||
3201 | { | |||
3202 | ScAddress aAdr; | |||
3203 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
3204 | { | |||
3205 | PushInt(0); | |||
3206 | return ; | |||
3207 | } | |||
3208 | bool bValue = false; | |||
3209 | ScRefCellValue aCell(mrDoc, aAdr); | |||
3210 | if (GetCellErrCode(aCell) == FormulaError::NONE) | |||
3211 | { | |||
3212 | switch (aCell.meType) | |||
3213 | { | |||
3214 | case CELLTYPE_VALUE : | |||
3215 | bValue = true; | |||
3216 | break; | |||
3217 | case CELLTYPE_FORMULA : | |||
3218 | bValue = aCell.mpFormula->IsValue(); | |||
3219 | break; | |||
3220 | default: | |||
3221 | ; // nothing | |||
3222 | } | |||
3223 | } | |||
3224 | if ( bValue ) | |||
3225 | PushString(EMPTY_OUSTRINGScGlobal::GetEmptyOUString()); | |||
3226 | else | |||
3227 | { | |||
3228 | // like GetString() | |||
3229 | svl::SharedString aStr; | |||
3230 | GetCellString(aStr, aCell); | |||
3231 | PushString(aStr); | |||
3232 | } | |||
3233 | } | |||
3234 | break; | |||
3235 | case svMatrix: | |||
3236 | case svExternalSingleRef: | |||
3237 | case svExternalDoubleRef: | |||
3238 | { | |||
3239 | double fVal; | |||
3240 | svl::SharedString aStr; | |||
3241 | ScMatValType nMatValType = GetDoubleOrStringFromMatrix( fVal, aStr); | |||
3242 | if (ScMatrix::IsValueType( nMatValType)) | |||
3243 | PushString(svl::SharedString::getEmptyString()); | |||
3244 | else | |||
3245 | PushString( aStr); | |||
3246 | } | |||
3247 | break; | |||
3248 | case svDouble : | |||
3249 | { | |||
3250 | PopError(); | |||
3251 | PushString( EMPTY_OUSTRINGScGlobal::GetEmptyOUString() ); | |||
3252 | } | |||
3253 | break; | |||
3254 | case svString : | |||
3255 | ; // leave on stack | |||
3256 | break; | |||
3257 | default : | |||
3258 | PushError( FormulaError::UnknownOpCode); | |||
3259 | } | |||
3260 | } | |||
3261 | ||||
3262 | void ScInterpreter::ScValue() | |||
3263 | { | |||
3264 | OUString aInputString; | |||
3265 | double fVal; | |||
3266 | ||||
3267 | switch ( GetRawStackType() ) | |||
3268 | { | |||
3269 | case svMissing: | |||
3270 | case svEmptyCell: | |||
3271 | Pop(); | |||
3272 | PushInt(0); | |||
3273 | return; | |||
3274 | case svDouble: | |||
3275 | return; // leave on stack | |||
3276 | ||||
3277 | case svSingleRef: | |||
3278 | case svDoubleRef: | |||
3279 | { | |||
3280 | ScAddress aAdr; | |||
3281 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
3282 | { | |||
3283 | PushInt(0); | |||
3284 | return; | |||
3285 | } | |||
3286 | ScRefCellValue aCell(mrDoc, aAdr); | |||
3287 | if (aCell.hasString()) | |||
3288 | { | |||
3289 | svl::SharedString aSS; | |||
3290 | GetCellString(aSS, aCell); | |||
3291 | aInputString = aSS.getString(); | |||
3292 | } | |||
3293 | else if (aCell.hasNumeric()) | |||
3294 | { | |||
3295 | PushDouble( GetCellValue(aAdr, aCell) ); | |||
3296 | return; | |||
3297 | } | |||
3298 | else | |||
3299 | { | |||
3300 | PushDouble(0.0); | |||
3301 | return; | |||
3302 | } | |||
3303 | } | |||
3304 | break; | |||
3305 | case svMatrix: | |||
3306 | { | |||
3307 | svl::SharedString aSS; | |||
3308 | ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, | |||
3309 | aSS); | |||
3310 | aInputString = aSS.getString(); | |||
3311 | switch (nType) | |||
3312 | { | |||
3313 | case ScMatValType::Empty: | |||
3314 | fVal = 0.0; | |||
3315 | [[fallthrough]]; | |||
3316 | case ScMatValType::Value: | |||
3317 | case ScMatValType::Boolean: | |||
3318 | PushDouble( fVal); | |||
3319 | return; | |||
3320 | case ScMatValType::String: | |||
3321 | // evaluated below | |||
3322 | break; | |||
3323 | default: | |||
3324 | PushIllegalArgument(); | |||
3325 | } | |||
3326 | } | |||
3327 | break; | |||
3328 | default: | |||
3329 | aInputString = GetString().getString(); | |||
3330 | break; | |||
3331 | } | |||
3332 | ||||
3333 | sal_uInt32 nFIndex = 0; // 0 for default locale | |||
3334 | if (pFormatter->IsNumberFormat(aInputString, nFIndex, fVal)) | |||
3335 | PushDouble(fVal); | |||
3336 | else | |||
3337 | PushIllegalArgument(); | |||
3338 | } | |||
3339 | ||||
3340 | // fdo#57180 | |||
3341 | void ScInterpreter::ScNumberValue() | |||
3342 | { | |||
3343 | ||||
3344 | sal_uInt8 nParamCount = GetByte(); | |||
3345 | if ( !MustHaveParamCount( nParamCount, 1, 3 ) ) | |||
3346 | return; | |||
3347 | ||||
3348 | OUString aInputString; | |||
3349 | OUString aGroupSeparator; | |||
3350 | sal_Unicode cDecimalSeparator = 0; | |||
3351 | ||||
3352 | if ( nParamCount == 3 ) | |||
3353 | aGroupSeparator = GetString().getString(); | |||
3354 | ||||
3355 | if ( nParamCount >= 2 ) | |||
3356 | { | |||
3357 | OUString aDecimalSeparator = GetString().getString(); | |||
3358 | if ( aDecimalSeparator.getLength() == 1 ) | |||
3359 | cDecimalSeparator = aDecimalSeparator[ 0 ]; | |||
3360 | else | |||
3361 | { | |||
3362 | PushIllegalArgument(); //if given, separator length must be 1 | |||
3363 | return; | |||
3364 | } | |||
3365 | } | |||
3366 | ||||
3367 | if ( cDecimalSeparator && aGroupSeparator.indexOf( cDecimalSeparator ) != -1 ) | |||
3368 | { | |||
3369 | PushIllegalArgument(); //decimal separator cannot appear in group separator | |||
3370 | return; | |||
3371 | } | |||
3372 | ||||
3373 | switch (GetStackType()) | |||
3374 | { | |||
3375 | case svDouble: | |||
3376 | return; // leave on stack | |||
3377 | default: | |||
3378 | aInputString = GetString().getString(); | |||
3379 | } | |||
3380 | if ( nGlobalError != FormulaError::NONE ) | |||
3381 | { | |||
3382 | PushError( nGlobalError ); | |||
3383 | return; | |||
3384 | } | |||
3385 | if ( aInputString.isEmpty() ) | |||
3386 | { | |||
3387 | if ( maCalcConfig.mbEmptyStringAsZero ) | |||
3388 | PushDouble( 0.0 ); | |||
3389 | else | |||
3390 | PushNoValue(); | |||
3391 | return; | |||
3392 | } | |||
3393 | ||||
3394 | sal_Int32 nDecSep = aInputString.indexOf( cDecimalSeparator ); | |||
3395 | if ( nDecSep != 0 ) | |||
3396 | { | |||
3397 | OUString aTemporary( nDecSep >= 0 ? aInputString.copy( 0, nDecSep ) : aInputString ); | |||
3398 | sal_Int32 nIndex = 0; | |||
3399 | while (nIndex < aGroupSeparator.getLength()) | |||
3400 | { | |||
3401 | sal_uInt32 nChar = aGroupSeparator.iterateCodePoints( &nIndex ); | |||
3402 | aTemporary = aTemporary.replaceAll( OUString( &nChar, 1 ), "" ); | |||
3403 | } | |||
3404 | if ( nDecSep >= 0 ) | |||
3405 | aInputString = aTemporary + aInputString.copy( nDecSep ); | |||
3406 | else | |||
3407 | aInputString = aTemporary; | |||
3408 | } | |||
3409 | ||||
3410 | for ( sal_Int32 i = aInputString.getLength(); --i >= 0; ) | |||
3411 | { | |||
3412 | sal_Unicode c = aInputString[ i ]; | |||
3413 | if ( c == 0x0020 || c == 0x0009 || c == 0x000A || c == 0x000D ) | |||
3414 | aInputString = aInputString.replaceAt( i, 1, "" ); // remove spaces etc. | |||
3415 | } | |||
3416 | sal_Int32 nPercentCount = 0; | |||
3417 | for ( sal_Int32 i = aInputString.getLength() - 1; i >= 0 && aInputString[ i ] == 0x0025; i-- ) | |||
3418 | { | |||
3419 | aInputString = aInputString.replaceAt( i, 1, "" ); // remove and count trailing '%' | |||
3420 | nPercentCount++; | |||
3421 | } | |||
3422 | ||||
3423 | rtl_math_ConversionStatus eStatus; | |||
3424 | sal_Int32 nParseEnd; | |||
3425 | double fVal = ::rtl::math::stringToDouble( aInputString, cDecimalSeparator, 0, &eStatus, &nParseEnd ); | |||
3426 | if ( eStatus == rtl_math_ConversionStatus_Ok && nParseEnd == aInputString.getLength() ) | |||
3427 | { | |||
3428 | if (nPercentCount) | |||
3429 | fVal *= pow( 10.0, -(nPercentCount * 2)); // process '%' from input string | |||
3430 | PushDouble(fVal); | |||
3431 | return; | |||
3432 | } | |||
3433 | PushNoValue(); | |||
3434 | } | |||
3435 | ||||
3436 | //2do: this should be a proper unicode string method | |||
3437 | static bool lcl_ScInterpreter_IsPrintable( sal_Unicode c ) | |||
3438 | { | |||
3439 | return 0x20 <= c && c != 0x7f; | |||
3440 | } | |||
3441 | ||||
3442 | void ScInterpreter::ScClean() | |||
3443 | { | |||
3444 | OUString aStr = GetString().getString(); | |||
3445 | for ( sal_Int32 i = 0; i < aStr.getLength(); i++ ) | |||
3446 | { | |||
3447 | if ( !lcl_ScInterpreter_IsPrintable( aStr[i] ) ) | |||
3448 | aStr = aStr.replaceAt(i,1,""); | |||
3449 | } | |||
3450 | PushString(aStr); | |||
3451 | } | |||
3452 | ||||
3453 | void ScInterpreter::ScCode() | |||
3454 | { | |||
3455 | //2do: make it full range unicode? | |||
3456 | OUString aStr = GetString().getString(); | |||
3457 | if (aStr.isEmpty()) | |||
3458 | PushInt(0); | |||
3459 | else | |||
3460 | { | |||
3461 | //"classic" ByteString conversion flags | |||
3462 | const sal_uInt32 convertFlags = | |||
3463 | RTL_UNICODETOTEXT_FLAGS_NONSPACING_IGNORE((sal_uInt32)0x0800) | | |||
3464 | RTL_UNICODETOTEXT_FLAGS_CONTROL_IGNORE((sal_uInt32)0x1000) | | |||
3465 | RTL_UNICODETOTEXT_FLAGS_FLUSH((sal_uInt32)0x8000) | | |||
3466 | RTL_UNICODETOTEXT_FLAGS_UNDEFINED_DEFAULT((sal_uInt32)0x0006) | | |||
3467 | RTL_UNICODETOTEXT_FLAGS_INVALID_DEFAULT((sal_uInt32)0x0060) | | |||
3468 | RTL_UNICODETOTEXT_FLAGS_UNDEFINED_REPLACE((sal_uInt32)0x0100); | |||
3469 | PushInt( static_cast<unsigned char>(OUStringToOString(OUString(aStr[0]), osl_getThreadTextEncoding(), convertFlags).toChar()) ); | |||
3470 | } | |||
3471 | } | |||
3472 | ||||
3473 | void ScInterpreter::ScChar() | |||
3474 | { | |||
3475 | //2do: make it full range unicode? | |||
3476 | double fVal = GetDouble(); | |||
3477 | if (fVal < 0.0 || fVal >= 256.0) | |||
3478 | PushIllegalArgument(); | |||
3479 | else | |||
3480 | { | |||
3481 | //"classic" ByteString conversion flags | |||
3482 | const sal_uInt32 convertFlags = | |||
3483 | RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_DEFAULT((sal_uInt32)0x0004) | | |||
3484 | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_DEFAULT((sal_uInt32)0x0030) | | |||
3485 | RTL_TEXTTOUNICODE_FLAGS_INVALID_DEFAULT((sal_uInt32)0x0300); | |||
3486 | ||||
3487 | char cEncodedChar = static_cast<char>(fVal); | |||
3488 | OUString aStr(&cEncodedChar, 1, osl_getThreadTextEncoding(), convertFlags); | |||
3489 | PushString(aStr); | |||
3490 | } | |||
3491 | } | |||
3492 | ||||
3493 | /* #i70213# fullwidth/halfwidth conversion provided by | |||
3494 | * Takashi Nakamoto <bluedwarf@ooo> | |||
3495 | * erAck: added Excel compatibility conversions as seen in issue's test case. */ | |||
3496 | ||||
3497 | static OUString lcl_convertIntoHalfWidth( const OUString & rStr ) | |||
3498 | { | |||
3499 | // Make the initialization thread-safe. Since another function needs to be called, move it all to another | |||
3500 | // function and thread-safely initialize a static reference in this function. | |||
3501 | auto init = []() -> utl::TransliterationWrapper& | |||
3502 | { | |||
3503 | static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE ); | |||
3504 | trans.loadModuleByImplName( "FULLWIDTH_HALFWIDTH_LIKE_ASC", LANGUAGE_SYSTEMLanguageType(0x0000) ); | |||
3505 | return trans; | |||
3506 | }; | |||
3507 | static utl::TransliterationWrapper& aTrans( init()); | |||
3508 | return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) ); | |||
3509 | } | |||
3510 | ||||
3511 | static OUString lcl_convertIntoFullWidth( const OUString & rStr ) | |||
3512 | { | |||
3513 | auto init = []() -> utl::TransliterationWrapper& | |||
3514 | { | |||
3515 | static utl::TransliterationWrapper trans( ::comphelper::getProcessComponentContext(), TransliterationFlags::NONE ); | |||
3516 | trans.loadModuleByImplName( "HALFWIDTH_FULLWIDTH_LIKE_JIS", LANGUAGE_SYSTEMLanguageType(0x0000) ); | |||
3517 | return trans; | |||
3518 | }; | |||
3519 | static utl::TransliterationWrapper& aTrans( init()); | |||
3520 | return aTrans.transliterate( rStr, 0, sal_uInt16( rStr.getLength() ) ); | |||
3521 | } | |||
3522 | ||||
3523 | /* ODFF: | |||
3524 | * Summary: Converts half-width to full-width ASCII and katakana characters. | |||
3525 | * Semantics: Conversion is done for half-width ASCII and katakana characters, | |||
3526 | * other characters are simply copied from T to the result. This is the | |||
3527 | * complementary function to ASC. | |||
3528 | * For references regarding halfwidth and fullwidth characters see | |||
3529 | * http://www.unicode.org/reports/tr11/ | |||
3530 | * http://www.unicode.org/charts/charindex2.html#H | |||
3531 | * http://www.unicode.org/charts/charindex2.html#F | |||
3532 | */ | |||
3533 | void ScInterpreter::ScJis() | |||
3534 | { | |||
3535 | if (MustHaveParamCount( GetByte(), 1)) | |||
3536 | PushString( lcl_convertIntoFullWidth( GetString().getString())); | |||
3537 | } | |||
3538 | ||||
3539 | /* ODFF: | |||
3540 | * Summary: Converts full-width to half-width ASCII and katakana characters. | |||
3541 | * Semantics: Conversion is done for full-width ASCII and katakana characters, | |||
3542 | * other characters are simply copied from T to the result. This is the | |||
3543 | * complementary function to JIS. | |||
3544 | */ | |||
3545 | void ScInterpreter::ScAsc() | |||
3546 | { | |||
3547 | if (MustHaveParamCount( GetByte(), 1)) | |||
3548 | PushString( lcl_convertIntoHalfWidth( GetString().getString())); | |||
3549 | } | |||
3550 | ||||
3551 | void ScInterpreter::ScUnicode() | |||
3552 | { | |||
3553 | if ( MustHaveParamCount( GetByte(), 1 ) ) | |||
3554 | { | |||
3555 | OUString aStr = GetString().getString(); | |||
3556 | if (aStr.isEmpty()) | |||
3557 | PushIllegalParameter(); | |||
3558 | else | |||
3559 | { | |||
3560 | sal_Int32 i = 0; | |||
3561 | PushDouble(aStr.iterateCodePoints(&i)); | |||
3562 | } | |||
3563 | } | |||
3564 | } | |||
3565 | ||||
3566 | void ScInterpreter::ScUnichar() | |||
3567 | { | |||
3568 | if ( MustHaveParamCount( GetByte(), 1 ) ) | |||
3569 | { | |||
3570 | sal_uInt32 nCodePoint = GetUInt32(); | |||
3571 | if (nGlobalError != FormulaError::NONE || !rtl::isUnicodeCodePoint(nCodePoint)) | |||
3572 | PushIllegalArgument(); | |||
3573 | else | |||
3574 | { | |||
3575 | OUString aStr( &nCodePoint, 1 ); | |||
3576 | PushString( aStr ); | |||
3577 | } | |||
3578 | } | |||
3579 | } | |||
3580 | ||||
3581 | bool ScInterpreter::SwitchToArrayRefList( ScMatrixRef& xResMat, SCSIZE nMatRows, double fCurrent, | |||
3582 | const std::function<void( SCSIZE i, double fCurrent )>& MatOpFunc, bool bDoMatOp ) | |||
3583 | { | |||
3584 | const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); | |||
3585 | if (!p || !p->IsArrayResult()) | |||
3586 | return false; | |||
3587 | ||||
3588 | if (!xResMat) | |||
3589 | { | |||
3590 | // Create and init all elements with current value. | |||
3591 | assert(nMatRows > 0)(static_cast <bool> (nMatRows > 0) ? void (0) : __assert_fail ("nMatRows > 0", "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" , 3591, __extension__ __PRETTY_FUNCTION__)); | |||
3592 | xResMat = GetNewMat( 1, nMatRows, true); | |||
3593 | xResMat->FillDouble( fCurrent, 0,0, 0,nMatRows-1); | |||
3594 | } | |||
3595 | else if (bDoMatOp) | |||
3596 | { | |||
3597 | // Current value and values from vector are operands | |||
3598 | // for each vector position. | |||
3599 | for (SCSIZE i=0; i < nMatRows; ++i) | |||
3600 | { | |||
3601 | MatOpFunc( i, fCurrent); | |||
3602 | } | |||
3603 | } | |||
3604 | return true; | |||
3605 | } | |||
3606 | ||||
3607 | void ScInterpreter::ScMin( bool bTextAsZero ) | |||
3608 | { | |||
3609 | short nParamCount = GetByte(); | |||
3610 | if (!MustHaveParamCountMin( nParamCount, 1)) | |||
3611 | return; | |||
3612 | ||||
3613 | ScMatrixRef xResMat; | |||
3614 | double nMin = ::std::numeric_limits<double>::max(); | |||
3615 | auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMin ) | |||
3616 | { | |||
3617 | double fVecRes = xResMat->GetDouble(0,i); | |||
3618 | if (fVecRes > fCurMin) | |||
3619 | xResMat->PutDouble( fCurMin, 0,i); | |||
3620 | }; | |||
3621 | const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount); | |||
3622 | size_t nRefArrayPos = std::numeric_limits<size_t>::max(); | |||
3623 | ||||
3624 | double nVal = 0.0; | |||
3625 | ScAddress aAdr; | |||
3626 | ScRange aRange; | |||
3627 | size_t nRefInList = 0; | |||
3628 | while (nParamCount-- > 0) | |||
3629 | { | |||
3630 | switch (GetStackType()) | |||
3631 | { | |||
3632 | case svDouble : | |||
3633 | { | |||
3634 | nVal = GetDouble(); | |||
3635 | if (nMin > nVal) nMin = nVal; | |||
3636 | nFuncFmtType = SvNumFormatType::NUMBER; | |||
3637 | } | |||
3638 | break; | |||
3639 | case svSingleRef : | |||
3640 | { | |||
3641 | PopSingleRef( aAdr ); | |||
3642 | ScRefCellValue aCell(mrDoc, aAdr); | |||
3643 | if (aCell.hasNumeric()) | |||
3644 | { | |||
3645 | nVal = GetCellValue(aAdr, aCell); | |||
3646 | CurFmtToFuncFmt(); | |||
3647 | if (nMin > nVal) nMin = nVal; | |||
3648 | } | |||
3649 | else if (bTextAsZero && aCell.hasString()) | |||
3650 | { | |||
3651 | if ( nMin > 0.0 ) | |||
3652 | nMin = 0.0; | |||
3653 | } | |||
3654 | } | |||
3655 | break; | |||
3656 | case svRefList : | |||
3657 | { | |||
3658 | // bDoMatOp only for non-array value when switching to | |||
3659 | // ArrayRefList. | |||
3660 | if (SwitchToArrayRefList( xResMat, nMatRows, nMin, MatOpFunc, | |||
3661 | nRefArrayPos == std::numeric_limits<size_t>::max())) | |||
3662 | { | |||
3663 | nRefArrayPos = nRefInList; | |||
3664 | } | |||
3665 | } | |||
3666 | [[fallthrough]]; | |||
3667 | case svDoubleRef : | |||
3668 | { | |||
3669 | FormulaError nErr = FormulaError::NONE; | |||
3670 | PopDoubleRef( aRange, nParamCount, nRefInList); | |||
3671 | ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero ); | |||
3672 | aValIter.SetInterpreterContext( &mrContext ); | |||
3673 | if (aValIter.GetFirst(nVal, nErr)) | |||
3674 | { | |||
3675 | if (nMin > nVal) | |||
3676 | nMin = nVal; | |||
3677 | aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex ); | |||
3678 | while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr)) | |||
3679 | { | |||
3680 | if (nMin > nVal) | |||
3681 | nMin = nVal; | |||
3682 | } | |||
3683 | SetError(nErr); | |||
3684 | } | |||
3685 | if (nRefArrayPos != std::numeric_limits<size_t>::max()) | |||
3686 | { | |||
3687 | // Update vector element with current value. | |||
3688 | MatOpFunc( nRefArrayPos, nMin); | |||
3689 | ||||
3690 | // Reset. | |||
3691 | nMin = std::numeric_limits<double>::max(); | |||
3692 | nVal = 0.0; | |||
3693 | nRefArrayPos = std::numeric_limits<size_t>::max(); | |||
3694 | } | |||
3695 | } | |||
3696 | break; | |||
3697 | case svMatrix : | |||
3698 | case svExternalSingleRef: | |||
3699 | case svExternalDoubleRef: | |||
3700 | { | |||
3701 | ScMatrixRef pMat = GetMatrix(); | |||
3702 | if (pMat) | |||
3703 | { | |||
3704 | nFuncFmtType = SvNumFormatType::NUMBER; | |||
3705 | nVal = pMat->GetMinValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal)); | |||
3706 | if (nMin > nVal) | |||
3707 | nMin = nVal; | |||
3708 | } | |||
3709 | } | |||
3710 | break; | |||
3711 | case svString : | |||
3712 | { | |||
3713 | Pop(); | |||
3714 | if ( bTextAsZero ) | |||
3715 | { | |||
3716 | if ( nMin > 0.0 ) | |||
3717 | nMin = 0.0; | |||
3718 | } | |||
3719 | else | |||
3720 | SetError(FormulaError::IllegalParameter); | |||
3721 | } | |||
3722 | break; | |||
3723 | default : | |||
3724 | Pop(); | |||
3725 | SetError(FormulaError::IllegalParameter); | |||
3726 | } | |||
3727 | } | |||
3728 | ||||
3729 | if (xResMat) | |||
3730 | { | |||
3731 | // Include value of last non-references-array type and calculate final result. | |||
3732 | if (nMin < std::numeric_limits<double>::max()) | |||
3733 | { | |||
3734 | for (SCSIZE i=0; i < nMatRows; ++i) | |||
3735 | { | |||
3736 | MatOpFunc( i, nMin); | |||
3737 | } | |||
3738 | } | |||
3739 | else | |||
3740 | { | |||
3741 | /* TODO: the awkward "no value is minimum 0.0" is likely the case | |||
3742 | * if a value is numeric_limits::max. Still, that could be a valid | |||
3743 | * minimum value as well, but nVal and nMin had been reset after | |||
3744 | * the last svRefList... so we may lie here. */ | |||
3745 | for (SCSIZE i=0; i < nMatRows; ++i) | |||
3746 | { | |||
3747 | double fVecRes = xResMat->GetDouble(0,i); | |||
3748 | if (fVecRes == std::numeric_limits<double>::max()) | |||
3749 | xResMat->PutDouble( 0.0, 0,i); | |||
3750 | } | |||
3751 | } | |||
3752 | PushMatrix( xResMat); | |||
3753 | } | |||
3754 | else | |||
3755 | { | |||
3756 | if (!std::isfinite(nVal)) | |||
3757 | PushError( GetDoubleErrorValue( nVal)); | |||
3758 | else if ( nVal < nMin ) | |||
3759 | PushDouble(0.0); // zero or only empty arguments | |||
3760 | else | |||
3761 | PushDouble(nMin); | |||
3762 | } | |||
3763 | } | |||
3764 | ||||
3765 | void ScInterpreter::ScMax( bool bTextAsZero ) | |||
3766 | { | |||
3767 | short nParamCount = GetByte(); | |||
3768 | if (!MustHaveParamCountMin( nParamCount, 1)) | |||
3769 | return; | |||
3770 | ||||
3771 | ScMatrixRef xResMat; | |||
3772 | double nMax = std::numeric_limits<double>::lowest(); | |||
3773 | auto MatOpFunc = [&xResMat]( SCSIZE i, double fCurMax ) | |||
3774 | { | |||
3775 | double fVecRes = xResMat->GetDouble(0,i); | |||
3776 | if (fVecRes < fCurMax) | |||
3777 | xResMat->PutDouble( fCurMax, 0,i); | |||
3778 | }; | |||
3779 | const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount); | |||
3780 | size_t nRefArrayPos = std::numeric_limits<size_t>::max(); | |||
3781 | ||||
3782 | double nVal = 0.0; | |||
3783 | ScAddress aAdr; | |||
3784 | ScRange aRange; | |||
3785 | size_t nRefInList = 0; | |||
3786 | while (nParamCount-- > 0) | |||
3787 | { | |||
3788 | switch (GetStackType()) | |||
3789 | { | |||
3790 | case svDouble : | |||
3791 | { | |||
3792 | nVal = GetDouble(); | |||
3793 | if (nMax < nVal) nMax = nVal; | |||
3794 | nFuncFmtType = SvNumFormatType::NUMBER; | |||
3795 | } | |||
3796 | break; | |||
3797 | case svSingleRef : | |||
3798 | { | |||
3799 | PopSingleRef( aAdr ); | |||
3800 | ScRefCellValue aCell(mrDoc, aAdr); | |||
3801 | if (aCell.hasNumeric()) | |||
3802 | { | |||
3803 | nVal = GetCellValue(aAdr, aCell); | |||
3804 | CurFmtToFuncFmt(); | |||
3805 | if (nMax < nVal) nMax = nVal; | |||
3806 | } | |||
3807 | else if (bTextAsZero && aCell.hasString()) | |||
3808 | { | |||
3809 | if ( nMax < 0.0 ) | |||
3810 | nMax = 0.0; | |||
3811 | } | |||
3812 | } | |||
3813 | break; | |||
3814 | case svRefList : | |||
3815 | { | |||
3816 | // bDoMatOp only for non-array value when switching to | |||
3817 | // ArrayRefList. | |||
3818 | if (SwitchToArrayRefList( xResMat, nMatRows, nMax, MatOpFunc, | |||
3819 | nRefArrayPos == std::numeric_limits<size_t>::max())) | |||
3820 | { | |||
3821 | nRefArrayPos = nRefInList; | |||
3822 | } | |||
3823 | } | |||
3824 | [[fallthrough]]; | |||
3825 | case svDoubleRef : | |||
3826 | { | |||
3827 | FormulaError nErr = FormulaError::NONE; | |||
3828 | PopDoubleRef( aRange, nParamCount, nRefInList); | |||
3829 | ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero ); | |||
3830 | aValIter.SetInterpreterContext( &mrContext ); | |||
3831 | if (aValIter.GetFirst(nVal, nErr)) | |||
3832 | { | |||
3833 | if (nMax < nVal) | |||
3834 | nMax = nVal; | |||
3835 | aValIter.GetCurNumFmtInfo( mrContext, nFuncFmtType, nFuncFmtIndex ); | |||
3836 | while ((nErr == FormulaError::NONE) && aValIter.GetNext(nVal, nErr)) | |||
3837 | { | |||
3838 | if (nMax < nVal) | |||
3839 | nMax = nVal; | |||
3840 | } | |||
3841 | SetError(nErr); | |||
3842 | } | |||
3843 | if (nRefArrayPos != std::numeric_limits<size_t>::max()) | |||
3844 | { | |||
3845 | // Update vector element with current value. | |||
3846 | MatOpFunc( nRefArrayPos, nMax); | |||
3847 | ||||
3848 | // Reset. | |||
3849 | nMax = std::numeric_limits<double>::lowest(); | |||
3850 | nVal = 0.0; | |||
3851 | nRefArrayPos = std::numeric_limits<size_t>::max(); | |||
3852 | } | |||
3853 | } | |||
3854 | break; | |||
3855 | case svMatrix : | |||
3856 | case svExternalSingleRef: | |||
3857 | case svExternalDoubleRef: | |||
3858 | { | |||
3859 | ScMatrixRef pMat = GetMatrix(); | |||
3860 | if (pMat) | |||
3861 | { | |||
3862 | nFuncFmtType = SvNumFormatType::NUMBER; | |||
3863 | nVal = pMat->GetMaxValue(bTextAsZero, bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal)); | |||
3864 | if (nMax < nVal) | |||
3865 | nMax = nVal; | |||
3866 | } | |||
3867 | } | |||
3868 | break; | |||
3869 | case svString : | |||
3870 | { | |||
3871 | Pop(); | |||
3872 | if ( bTextAsZero ) | |||
3873 | { | |||
3874 | if ( nMax < 0.0 ) | |||
3875 | nMax = 0.0; | |||
3876 | } | |||
3877 | else | |||
3878 | SetError(FormulaError::IllegalParameter); | |||
3879 | } | |||
3880 | break; | |||
3881 | default : | |||
3882 | Pop(); | |||
3883 | SetError(FormulaError::IllegalParameter); | |||
3884 | } | |||
3885 | } | |||
3886 | ||||
3887 | if (xResMat) | |||
3888 | { | |||
3889 | // Include value of last non-references-array type and calculate final result. | |||
3890 | if (nMax > std::numeric_limits<double>::lowest()) | |||
3891 | { | |||
3892 | for (SCSIZE i=0; i < nMatRows; ++i) | |||
3893 | { | |||
3894 | MatOpFunc( i, nMax); | |||
3895 | } | |||
3896 | } | |||
3897 | else | |||
3898 | { | |||
3899 | /* TODO: the awkward "no value is maximum 0.0" is likely the case | |||
3900 | * if a value is numeric_limits::lowest. Still, that could be a | |||
3901 | * valid maximum value as well, but nVal and nMax had been reset | |||
3902 | * after the last svRefList... so we may lie here. */ | |||
3903 | for (SCSIZE i=0; i < nMatRows; ++i) | |||
3904 | { | |||
3905 | double fVecRes = xResMat->GetDouble(0,i); | |||
3906 | if (fVecRes == -std::numeric_limits<double>::max()) | |||
3907 | xResMat->PutDouble( 0.0, 0,i); | |||
3908 | } | |||
3909 | } | |||
3910 | PushMatrix( xResMat); | |||
3911 | } | |||
3912 | else | |||
3913 | { | |||
3914 | if (!std::isfinite(nVal)) | |||
3915 | PushError( GetDoubleErrorValue( nVal)); | |||
3916 | else if ( nVal > nMax ) | |||
3917 | PushDouble(0.0); // zero or only empty arguments | |||
3918 | else | |||
3919 | PushDouble(nMax); | |||
3920 | } | |||
3921 | } | |||
3922 | ||||
3923 | void ScInterpreter::GetStVarParams( bool bTextAsZero, double(*VarResult)( double fVal, size_t nValCount ) ) | |||
3924 | { | |||
3925 | short nParamCount = GetByte(); | |||
3926 | const SCSIZE nMatRows = GetRefListArrayMaxSize( nParamCount); | |||
3927 | ||||
3928 | struct ArrayRefListValue | |||
3929 | { | |||
3930 | std::vector<double> mvValues; | |||
3931 | double mfSum; | |||
3932 | ArrayRefListValue() : mfSum(0.0) {} | |||
3933 | }; | |||
3934 | std::vector<ArrayRefListValue> vArrayValues; | |||
3935 | ||||
3936 | std::vector<double> values; | |||
3937 | double fSum = 0.0; | |||
3938 | double fVal = 0.0; | |||
3939 | ScAddress aAdr; | |||
3940 | ScRange aRange; | |||
3941 | size_t nRefInList = 0; | |||
3942 | while (nGlobalError == FormulaError::NONE && nParamCount-- > 0) | |||
3943 | { | |||
3944 | switch (GetStackType()) | |||
3945 | { | |||
3946 | case svDouble : | |||
3947 | { | |||
3948 | fVal = GetDouble(); | |||
3949 | if (nGlobalError == FormulaError::NONE) | |||
3950 | { | |||
3951 | values.push_back(fVal); | |||
3952 | fSum += fVal; | |||
3953 | } | |||
3954 | } | |||
3955 | break; | |||
3956 | case svSingleRef : | |||
3957 | { | |||
3958 | PopSingleRef( aAdr ); | |||
3959 | ScRefCellValue aCell(mrDoc, aAdr); | |||
3960 | if (aCell.hasNumeric()) | |||
3961 | { | |||
3962 | fVal = GetCellValue(aAdr, aCell); | |||
3963 | if (nGlobalError == FormulaError::NONE) | |||
3964 | { | |||
3965 | values.push_back(fVal); | |||
3966 | fSum += fVal; | |||
3967 | } | |||
3968 | } | |||
3969 | else if (bTextAsZero && aCell.hasString()) | |||
3970 | { | |||
3971 | values.push_back(0.0); | |||
3972 | } | |||
3973 | } | |||
3974 | break; | |||
3975 | case svRefList : | |||
3976 | { | |||
3977 | const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); | |||
3978 | if (p && p->IsArrayResult()) | |||
3979 | { | |||
3980 | size_t nRefArrayPos = nRefInList; | |||
3981 | if (vArrayValues.empty()) | |||
3982 | { | |||
3983 | // Create and init all elements with current value. | |||
3984 | assert(nMatRows > 0)(static_cast <bool> (nMatRows > 0) ? void (0) : __assert_fail ("nMatRows > 0", "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" , 3984, __extension__ __PRETTY_FUNCTION__)); | |||
3985 | vArrayValues.resize(nMatRows); | |||
3986 | for (auto & it : vArrayValues) | |||
3987 | { | |||
3988 | it.mvValues = values; | |||
3989 | it.mfSum = fSum; | |||
3990 | } | |||
3991 | } | |||
3992 | else | |||
3993 | { | |||
3994 | // Current value and values from vector are operands | |||
3995 | // for each vector position. | |||
3996 | for (auto & it : vArrayValues) | |||
3997 | { | |||
3998 | it.mvValues.insert( it.mvValues.end(), values.begin(), values.end()); | |||
3999 | it.mfSum += fSum; | |||
4000 | } | |||
4001 | } | |||
4002 | ArrayRefListValue& rArrayValue = vArrayValues[nRefArrayPos]; | |||
4003 | FormulaError nErr = FormulaError::NONE; | |||
4004 | PopDoubleRef( aRange, nParamCount, nRefInList); | |||
4005 | ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero ); | |||
4006 | if (aValIter.GetFirst(fVal, nErr)) | |||
4007 | { | |||
4008 | do | |||
4009 | { | |||
4010 | rArrayValue.mvValues.push_back(fVal); | |||
4011 | rArrayValue.mfSum += fVal; | |||
4012 | } | |||
4013 | while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr)); | |||
4014 | } | |||
4015 | if ( nErr != FormulaError::NONE ) | |||
4016 | { | |||
4017 | rArrayValue.mfSum = CreateDoubleError( nErr); | |||
4018 | } | |||
4019 | // Reset. | |||
4020 | std::vector<double>().swap(values); | |||
4021 | fSum = 0.0; | |||
4022 | break; | |||
4023 | } | |||
4024 | } | |||
4025 | [[fallthrough]]; | |||
4026 | case svDoubleRef : | |||
4027 | { | |||
4028 | FormulaError nErr = FormulaError::NONE; | |||
4029 | PopDoubleRef( aRange, nParamCount, nRefInList); | |||
4030 | ScValueIterator aValIter( mrDoc, aRange, mnSubTotalFlags, bTextAsZero ); | |||
4031 | if (aValIter.GetFirst(fVal, nErr)) | |||
4032 | { | |||
4033 | do | |||
4034 | { | |||
4035 | values.push_back(fVal); | |||
4036 | fSum += fVal; | |||
4037 | } | |||
4038 | while ((nErr == FormulaError::NONE) && aValIter.GetNext(fVal, nErr)); | |||
4039 | } | |||
4040 | if ( nErr != FormulaError::NONE ) | |||
4041 | { | |||
4042 | SetError(nErr); | |||
4043 | } | |||
4044 | } | |||
4045 | break; | |||
4046 | case svExternalSingleRef : | |||
4047 | case svExternalDoubleRef : | |||
4048 | case svMatrix : | |||
4049 | { | |||
4050 | ScMatrixRef pMat = GetMatrix(); | |||
4051 | if (pMat) | |||
4052 | { | |||
4053 | const bool bIgnoreErrVal = bool(mnSubTotalFlags & SubtotalFlags::IgnoreErrVal); | |||
4054 | SCSIZE nC, nR; | |||
4055 | pMat->GetDimensions(nC, nR); | |||
4056 | for (SCSIZE nMatCol = 0; nMatCol < nC; nMatCol++) | |||
4057 | { | |||
4058 | for (SCSIZE nMatRow = 0; nMatRow < nR; nMatRow++) | |||
4059 | { | |||
4060 | if (!pMat->IsStringOrEmpty(nMatCol,nMatRow)) | |||
4061 | { | |||
4062 | fVal= pMat->GetDouble(nMatCol,nMatRow); | |||
4063 | if (nGlobalError == FormulaError::NONE) | |||
4064 | { | |||
4065 | values.push_back(fVal); | |||
4066 | fSum += fVal; | |||
4067 | } | |||
4068 | else if (bIgnoreErrVal) | |||
4069 | nGlobalError = FormulaError::NONE; | |||
4070 | } | |||
4071 | else if ( bTextAsZero ) | |||
4072 | { | |||
4073 | values.push_back(0.0); | |||
4074 | } | |||
4075 | } | |||
4076 | } | |||
4077 | } | |||
4078 | } | |||
4079 | break; | |||
4080 | case svString : | |||
4081 | { | |||
4082 | Pop(); | |||
4083 | if ( bTextAsZero ) | |||
4084 | { | |||
4085 | values.push_back(0.0); | |||
4086 | } | |||
4087 | else | |||
4088 | SetError(FormulaError::IllegalParameter); | |||
4089 | } | |||
4090 | break; | |||
4091 | default : | |||
4092 | Pop(); | |||
4093 | SetError(FormulaError::IllegalParameter); | |||
4094 | } | |||
4095 | } | |||
4096 | ||||
4097 | if (!vArrayValues.empty()) | |||
4098 | { | |||
4099 | // Include value of last non-references-array type and calculate final result. | |||
4100 | if (!values.empty()) | |||
4101 | { | |||
4102 | for (auto & it : vArrayValues) | |||
4103 | { | |||
4104 | it.mvValues.insert( it.mvValues.end(), values.begin(), values.end()); | |||
4105 | it.mfSum += fSum; | |||
4106 | } | |||
4107 | } | |||
4108 | ScMatrixRef xResMat = GetNewMat( 1, nMatRows, true); | |||
4109 | for (SCSIZE r=0; r < nMatRows; ++r) | |||
4110 | { | |||
4111 | ::std::vector<double>::size_type n = vArrayValues[r].mvValues.size(); | |||
4112 | if (!n) | |||
4113 | xResMat->PutError( FormulaError::DivisionByZero, 0, r); | |||
4114 | else | |||
4115 | { | |||
4116 | ArrayRefListValue& rArrayValue = vArrayValues[r]; | |||
4117 | double vSum = 0.0; | |||
4118 | const double vMean = rArrayValue.mfSum / n; | |||
4119 | for (::std::vector<double>::size_type i = 0; i < n; i++) | |||
4120 | vSum += ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean) * | |||
4121 | ::rtl::math::approxSub( rArrayValue.mvValues[i], vMean); | |||
4122 | xResMat->PutDouble( VarResult( vSum, n), 0, r); | |||
4123 | } | |||
4124 | } | |||
4125 | PushMatrix( xResMat); | |||
4126 | } | |||
4127 | else | |||
4128 | { | |||
4129 | ::std::vector<double>::size_type n = values.size(); | |||
4130 | if (!n) | |||
4131 | SetError( FormulaError::DivisionByZero); | |||
4132 | double vSum = 0.0; | |||
4133 | if (nGlobalError == FormulaError::NONE) | |||
4134 | { | |||
4135 | const double vMean = fSum / n; | |||
4136 | for (::std::vector<double>::size_type i = 0; i < n; i++) | |||
4137 | vSum += ::rtl::math::approxSub( values[i], vMean) * ::rtl::math::approxSub( values[i], vMean); | |||
4138 | } | |||
4139 | PushDouble( VarResult( vSum, n)); | |||
4140 | } | |||
4141 | } | |||
4142 | ||||
4143 | void ScInterpreter::ScVar( bool bTextAsZero ) | |||
4144 | { | |||
4145 | auto VarResult = []( double fVal, size_t nValCount ) | |||
4146 | { | |||
4147 | if (nValCount <= 1) | |||
4148 | return CreateDoubleError( FormulaError::DivisionByZero ); | |||
4149 | else | |||
4150 | return fVal / (nValCount - 1); | |||
4151 | }; | |||
4152 | GetStVarParams( bTextAsZero, VarResult ); | |||
4153 | } | |||
4154 | ||||
4155 | void ScInterpreter::ScVarP( bool bTextAsZero ) | |||
4156 | { | |||
4157 | auto VarResult = []( double fVal, size_t nValCount ) | |||
4158 | { | |||
4159 | return sc::div( fVal, nValCount); | |||
4160 | }; | |||
4161 | GetStVarParams( bTextAsZero, VarResult ); | |||
4162 | ||||
4163 | } | |||
4164 | ||||
4165 | void ScInterpreter::ScStDev( bool bTextAsZero ) | |||
4166 | { | |||
4167 | auto VarResult = []( double fVal, size_t nValCount ) | |||
4168 | { | |||
4169 | if (nValCount <= 1) | |||
4170 | return CreateDoubleError( FormulaError::DivisionByZero ); | |||
4171 | else | |||
4172 | return sqrt( fVal / (nValCount - 1)); | |||
4173 | }; | |||
4174 | GetStVarParams( bTextAsZero, VarResult ); | |||
4175 | } | |||
4176 | ||||
4177 | void ScInterpreter::ScStDevP( bool bTextAsZero ) | |||
4178 | { | |||
4179 | auto VarResult = []( double fVal, size_t nValCount ) | |||
4180 | { | |||
4181 | if (nValCount == 0) | |||
4182 | return CreateDoubleError( FormulaError::DivisionByZero ); | |||
4183 | else | |||
4184 | return sqrt( fVal / nValCount); | |||
4185 | }; | |||
4186 | GetStVarParams( bTextAsZero, VarResult ); | |||
4187 | ||||
4188 | /* this was: PushDouble( sqrt( div( nVal, nValCount))); | |||
4189 | * | |||
4190 | * Besides that the special NAN gets lost in the call through sqrt(), | |||
4191 | * unxlngi6.pro then looped back and forth somewhere between div() and | |||
4192 | * ::rtl::math::setNan(). Tests showed that | |||
4193 | * | |||
4194 | * sqrt( div( 1, 0)); | |||
4195 | * | |||
4196 | * produced a loop, but | |||
4197 | * | |||
4198 | * double f1 = div( 1, 0); | |||
4199 | * sqrt( f1 ); | |||
4200 | * | |||
4201 | * was fine. There seems to be some compiler optimization problem. It does | |||
4202 | * not occur when compiled with debug=t. | |||
4203 | */ | |||
4204 | } | |||
4205 | ||||
4206 | void ScInterpreter::ScColumns() | |||
4207 | { | |||
4208 | sal_uInt8 nParamCount = GetByte(); | |||
4209 | sal_uLong nVal = 0; | |||
4210 | SCCOL nCol1; | |||
4211 | SCROW nRow1; | |||
4212 | SCTAB nTab1; | |||
4213 | SCCOL nCol2; | |||
4214 | SCROW nRow2; | |||
4215 | SCTAB nTab2; | |||
4216 | while (nParamCount-- > 0) | |||
4217 | { | |||
4218 | switch ( GetStackType() ) | |||
4219 | { | |||
4220 | case svSingleRef: | |||
4221 | PopError(); | |||
4222 | nVal++; | |||
4223 | break; | |||
4224 | case svDoubleRef: | |||
4225 | PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
4226 | nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) * | |||
4227 | static_cast<sal_uLong>(nCol2 - nCol1 + 1); | |||
4228 | break; | |||
4229 | case svMatrix: | |||
4230 | { | |||
4231 | ScMatrixRef pMat = PopMatrix(); | |||
4232 | if (pMat) | |||
4233 | { | |||
4234 | SCSIZE nC, nR; | |||
4235 | pMat->GetDimensions(nC, nR); | |||
4236 | nVal += nC; | |||
4237 | } | |||
4238 | } | |||
4239 | break; | |||
4240 | case svExternalSingleRef: | |||
4241 | PopError(); | |||
4242 | nVal++; | |||
4243 | break; | |||
4244 | case svExternalDoubleRef: | |||
4245 | { | |||
4246 | sal_uInt16 nFileId; | |||
4247 | OUString aTabName; | |||
4248 | ScComplexRefData aRef; | |||
4249 | PopExternalDoubleRef( nFileId, aTabName, aRef); | |||
4250 | ScRange aAbs = aRef.toAbs(mrDoc, aPos); | |||
4251 | nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) * | |||
4252 | static_cast<sal_uLong>(aAbs.aEnd.Col() - aAbs.aStart.Col() + 1); | |||
4253 | } | |||
4254 | break; | |||
4255 | default: | |||
4256 | PopError(); | |||
4257 | SetError(FormulaError::IllegalParameter); | |||
4258 | } | |||
4259 | } | |||
4260 | PushDouble(static_cast<double>(nVal)); | |||
4261 | } | |||
4262 | ||||
4263 | void ScInterpreter::ScRows() | |||
4264 | { | |||
4265 | sal_uInt8 nParamCount = GetByte(); | |||
4266 | sal_uLong nVal = 0; | |||
4267 | SCCOL nCol1; | |||
4268 | SCROW nRow1; | |||
4269 | SCTAB nTab1; | |||
4270 | SCCOL nCol2; | |||
4271 | SCROW nRow2; | |||
4272 | SCTAB nTab2; | |||
4273 | while (nParamCount-- > 0) | |||
4274 | { | |||
4275 | switch ( GetStackType() ) | |||
4276 | { | |||
4277 | case svSingleRef: | |||
4278 | PopError(); | |||
4279 | nVal++; | |||
4280 | break; | |||
4281 | case svDoubleRef: | |||
4282 | PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
4283 | nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1) * | |||
4284 | static_cast<sal_uLong>(nRow2 - nRow1 + 1); | |||
4285 | break; | |||
4286 | case svMatrix: | |||
4287 | { | |||
4288 | ScMatrixRef pMat = PopMatrix(); | |||
4289 | if (pMat) | |||
4290 | { | |||
4291 | SCSIZE nC, nR; | |||
4292 | pMat->GetDimensions(nC, nR); | |||
4293 | nVal += nR; | |||
4294 | } | |||
4295 | } | |||
4296 | break; | |||
4297 | case svExternalSingleRef: | |||
4298 | PopError(); | |||
4299 | nVal++; | |||
4300 | break; | |||
4301 | case svExternalDoubleRef: | |||
4302 | { | |||
4303 | sal_uInt16 nFileId; | |||
4304 | OUString aTabName; | |||
4305 | ScComplexRefData aRef; | |||
4306 | PopExternalDoubleRef( nFileId, aTabName, aRef); | |||
4307 | ScRange aAbs = aRef.toAbs(mrDoc, aPos); | |||
4308 | nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1) * | |||
4309 | static_cast<sal_uLong>(aAbs.aEnd.Row() - aAbs.aStart.Row() + 1); | |||
4310 | } | |||
4311 | break; | |||
4312 | default: | |||
4313 | PopError(); | |||
4314 | SetError(FormulaError::IllegalParameter); | |||
4315 | } | |||
4316 | } | |||
4317 | PushDouble(static_cast<double>(nVal)); | |||
4318 | } | |||
4319 | ||||
4320 | void ScInterpreter::ScSheets() | |||
4321 | { | |||
4322 | sal_uInt8 nParamCount = GetByte(); | |||
4323 | sal_uLong nVal; | |||
4324 | if ( nParamCount == 0 ) | |||
4325 | nVal = mrDoc.GetTableCount(); | |||
4326 | else | |||
4327 | { | |||
4328 | nVal = 0; | |||
4329 | SCCOL nCol1; | |||
4330 | SCROW nRow1; | |||
4331 | SCTAB nTab1; | |||
4332 | SCCOL nCol2; | |||
4333 | SCROW nRow2; | |||
4334 | SCTAB nTab2; | |||
4335 | while (nGlobalError == FormulaError::NONE && nParamCount-- > 0) | |||
4336 | { | |||
4337 | switch ( GetStackType() ) | |||
4338 | { | |||
4339 | case svSingleRef: | |||
4340 | case svExternalSingleRef: | |||
4341 | PopError(); | |||
4342 | nVal++; | |||
4343 | break; | |||
4344 | case svDoubleRef: | |||
4345 | PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
4346 | nVal += static_cast<sal_uLong>(nTab2 - nTab1 + 1); | |||
4347 | break; | |||
4348 | case svExternalDoubleRef: | |||
4349 | { | |||
4350 | sal_uInt16 nFileId; | |||
4351 | OUString aTabName; | |||
4352 | ScComplexRefData aRef; | |||
4353 | PopExternalDoubleRef( nFileId, aTabName, aRef); | |||
4354 | ScRange aAbs = aRef.toAbs(mrDoc, aPos); | |||
4355 | nVal += static_cast<sal_uLong>(aAbs.aEnd.Tab() - aAbs.aStart.Tab() + 1); | |||
4356 | } | |||
4357 | break; | |||
4358 | default: | |||
4359 | PopError(); | |||
4360 | SetError( FormulaError::IllegalParameter ); | |||
4361 | } | |||
4362 | } | |||
4363 | } | |||
4364 | PushDouble( static_cast<double>(nVal) ); | |||
4365 | } | |||
4366 | ||||
4367 | void ScInterpreter::ScColumn() | |||
4368 | { | |||
4369 | sal_uInt8 nParamCount = GetByte(); | |||
4370 | if ( !MustHaveParamCount( nParamCount, 0, 1 ) ) | |||
4371 | return; | |||
4372 | ||||
4373 | double nVal = 0.0; | |||
4374 | if (nParamCount == 0) | |||
4375 | { | |||
4376 | nVal = aPos.Col() + 1; | |||
4377 | if (bMatrixFormula) | |||
4378 | { | |||
4379 | SCCOL nCols = 0; | |||
4380 | SCROW nRows = 0; | |||
4381 | if (pMyFormulaCell) | |||
4382 | pMyFormulaCell->GetMatColsRows( nCols, nRows); | |||
4383 | if (nCols == 0) | |||
4384 | { | |||
4385 | // Happens if called via ScViewFunc::EnterMatrix() | |||
4386 | // ScFormulaCell::GetResultDimensions() as of course a | |||
4387 | // matrix result is not available yet. | |||
4388 | nCols = 1; | |||
4389 | } | |||
4390 | ScMatrixRef pResMat = GetNewMat( static_cast<SCSIZE>(nCols), 1); | |||
4391 | if (pResMat) | |||
4392 | { | |||
4393 | for (SCCOL i=0; i < nCols; ++i) | |||
4394 | pResMat->PutDouble( nVal + i, static_cast<SCSIZE>(i), 0); | |||
4395 | PushMatrix( pResMat); | |||
4396 | return; | |||
4397 | } | |||
4398 | } | |||
4399 | } | |||
4400 | else | |||
4401 | { | |||
4402 | switch ( GetStackType() ) | |||
4403 | { | |||
4404 | case svSingleRef : | |||
4405 | { | |||
4406 | SCCOL nCol1(0); | |||
4407 | SCROW nRow1(0); | |||
4408 | SCTAB nTab1(0); | |||
4409 | PopSingleRef( nCol1, nRow1, nTab1 ); | |||
4410 | nVal = static_cast<double>(nCol1 + 1); | |||
4411 | } | |||
4412 | break; | |||
4413 | case svExternalSingleRef : | |||
4414 | { | |||
4415 | sal_uInt16 nFileId; | |||
4416 | OUString aTabName; | |||
4417 | ScSingleRefData aRef; | |||
4418 | PopExternalSingleRef( nFileId, aTabName, aRef ); | |||
4419 | ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos); | |||
4420 | nVal = static_cast<double>( aAbsRef.Col() + 1 ); | |||
4421 | } | |||
4422 | break; | |||
4423 | ||||
4424 | case svDoubleRef : | |||
4425 | case svExternalDoubleRef : | |||
4426 | { | |||
4427 | SCCOL nCol1; | |||
4428 | SCCOL nCol2; | |||
4429 | if ( GetStackType() == svDoubleRef ) | |||
4430 | { | |||
4431 | SCROW nRow1; | |||
4432 | SCTAB nTab1; | |||
4433 | SCROW nRow2; | |||
4434 | SCTAB nTab2; | |||
4435 | PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); | |||
4436 | } | |||
4437 | else | |||
4438 | { | |||
4439 | sal_uInt16 nFileId; | |||
4440 | OUString aTabName; | |||
4441 | ScComplexRefData aRef; | |||
4442 | PopExternalDoubleRef( nFileId, aTabName, aRef ); | |||
4443 | ScRange aAbs = aRef.toAbs(mrDoc, aPos); | |||
4444 | nCol1 = aAbs.aStart.Col(); | |||
4445 | nCol2 = aAbs.aEnd.Col(); | |||
4446 | } | |||
4447 | if (nCol2 > nCol1) | |||
4448 | { | |||
4449 | ScMatrixRef pResMat = GetNewMat( | |||
4450 | static_cast<SCSIZE>(nCol2-nCol1+1), 1); | |||
4451 | if (pResMat) | |||
4452 | { | |||
4453 | for (SCCOL i = nCol1; i <= nCol2; i++) | |||
4454 | pResMat->PutDouble(static_cast<double>(i+1), | |||
4455 | static_cast<SCSIZE>(i-nCol1), 0); | |||
4456 | PushMatrix(pResMat); | |||
4457 | return; | |||
4458 | } | |||
4459 | } | |||
4460 | else | |||
4461 | nVal = static_cast<double>(nCol1 + 1); | |||
4462 | } | |||
4463 | break; | |||
4464 | default: | |||
4465 | SetError( FormulaError::IllegalParameter ); | |||
4466 | } | |||
4467 | } | |||
4468 | PushDouble( nVal ); | |||
4469 | } | |||
4470 | ||||
4471 | void ScInterpreter::ScRow() | |||
4472 | { | |||
4473 | sal_uInt8 nParamCount = GetByte(); | |||
4474 | if ( !MustHaveParamCount( nParamCount, 0, 1 ) ) | |||
4475 | return; | |||
4476 | ||||
4477 | double nVal = 0.0; | |||
4478 | if (nParamCount == 0) | |||
4479 | { | |||
4480 | nVal = aPos.Row() + 1; | |||
4481 | if (bMatrixFormula) | |||
4482 | { | |||
4483 | SCCOL nCols = 0; | |||
4484 | SCROW nRows = 0; | |||
4485 | if (pMyFormulaCell) | |||
4486 | pMyFormulaCell->GetMatColsRows( nCols, nRows); | |||
4487 | if (nRows == 0) | |||
4488 | { | |||
4489 | // Happens if called via ScViewFunc::EnterMatrix() | |||
4490 | // ScFormulaCell::GetResultDimensions() as of course a | |||
4491 | // matrix result is not available yet. | |||
4492 | nRows = 1; | |||
4493 | } | |||
4494 | ScMatrixRef pResMat = GetNewMat( 1, static_cast<SCSIZE>(nRows)); | |||
4495 | if (pResMat) | |||
4496 | { | |||
4497 | for (SCROW i=0; i < nRows; i++) | |||
4498 | pResMat->PutDouble( nVal + i, 0, static_cast<SCSIZE>(i)); | |||
4499 | PushMatrix( pResMat); | |||
4500 | return; | |||
4501 | } | |||
4502 | } | |||
4503 | } | |||
4504 | else | |||
4505 | { | |||
4506 | switch ( GetStackType() ) | |||
4507 | { | |||
4508 | case svSingleRef : | |||
4509 | { | |||
4510 | SCCOL nCol1(0); | |||
4511 | SCROW nRow1(0); | |||
4512 | SCTAB nTab1(0); | |||
4513 | PopSingleRef( nCol1, nRow1, nTab1 ); | |||
4514 | nVal = static_cast<double>(nRow1 + 1); | |||
4515 | } | |||
4516 | break; | |||
4517 | case svExternalSingleRef : | |||
4518 | { | |||
4519 | sal_uInt16 nFileId; | |||
4520 | OUString aTabName; | |||
4521 | ScSingleRefData aRef; | |||
4522 | PopExternalSingleRef( nFileId, aTabName, aRef ); | |||
4523 | ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos); | |||
4524 | nVal = static_cast<double>( aAbsRef.Row() + 1 ); | |||
4525 | } | |||
4526 | break; | |||
4527 | case svDoubleRef : | |||
4528 | case svExternalDoubleRef : | |||
4529 | { | |||
4530 | SCROW nRow1; | |||
4531 | SCROW nRow2; | |||
4532 | if ( GetStackType() == svDoubleRef ) | |||
4533 | { | |||
4534 | SCCOL nCol1; | |||
4535 | SCTAB nTab1; | |||
4536 | SCCOL nCol2; | |||
4537 | SCTAB nTab2; | |||
4538 | PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); | |||
4539 | } | |||
4540 | else | |||
4541 | { | |||
4542 | sal_uInt16 nFileId; | |||
4543 | OUString aTabName; | |||
4544 | ScComplexRefData aRef; | |||
4545 | PopExternalDoubleRef( nFileId, aTabName, aRef ); | |||
4546 | ScRange aAbs = aRef.toAbs(mrDoc, aPos); | |||
4547 | nRow1 = aAbs.aStart.Row(); | |||
4548 | nRow2 = aAbs.aEnd.Row(); | |||
4549 | } | |||
4550 | if (nRow2 > nRow1) | |||
4551 | { | |||
4552 | ScMatrixRef pResMat = GetNewMat( 1, | |||
4553 | static_cast<SCSIZE>(nRow2-nRow1+1)); | |||
4554 | if (pResMat) | |||
4555 | { | |||
4556 | for (SCROW i = nRow1; i <= nRow2; i++) | |||
4557 | pResMat->PutDouble(static_cast<double>(i+1), 0, | |||
4558 | static_cast<SCSIZE>(i-nRow1)); | |||
4559 | PushMatrix(pResMat); | |||
4560 | return; | |||
4561 | } | |||
4562 | } | |||
4563 | else | |||
4564 | nVal = static_cast<double>(nRow1 + 1); | |||
4565 | } | |||
4566 | break; | |||
4567 | default: | |||
4568 | SetError( FormulaError::IllegalParameter ); | |||
4569 | } | |||
4570 | } | |||
4571 | PushDouble( nVal ); | |||
4572 | } | |||
4573 | ||||
4574 | void ScInterpreter::ScSheet() | |||
4575 | { | |||
4576 | sal_uInt8 nParamCount = GetByte(); | |||
4577 | if ( !MustHaveParamCount( nParamCount, 0, 1 ) ) | |||
4578 | return; | |||
4579 | ||||
4580 | SCTAB nVal = 0; | |||
4581 | if ( nParamCount == 0 ) | |||
4582 | nVal = aPos.Tab() + 1; | |||
4583 | else | |||
4584 | { | |||
4585 | switch ( GetStackType() ) | |||
4586 | { | |||
4587 | case svString : | |||
4588 | { | |||
4589 | svl::SharedString aStr = PopString(); | |||
4590 | if ( mrDoc.GetTable(aStr.getString(), nVal)) | |||
4591 | ++nVal; | |||
4592 | else | |||
4593 | SetError( FormulaError::IllegalArgument ); | |||
4594 | } | |||
4595 | break; | |||
4596 | case svSingleRef : | |||
4597 | { | |||
4598 | SCCOL nCol1(0); | |||
4599 | SCROW nRow1(0); | |||
4600 | SCTAB nTab1(0); | |||
4601 | PopSingleRef(nCol1, nRow1, nTab1); | |||
4602 | nVal = nTab1 + 1; | |||
4603 | } | |||
4604 | break; | |||
4605 | case svDoubleRef : | |||
4606 | { | |||
4607 | SCCOL nCol1; | |||
4608 | SCROW nRow1; | |||
4609 | SCTAB nTab1; | |||
4610 | SCCOL nCol2; | |||
4611 | SCROW nRow2; | |||
4612 | SCTAB nTab2; | |||
4613 | PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); | |||
4614 | nVal = nTab1 + 1; | |||
4615 | } | |||
4616 | break; | |||
4617 | default: | |||
4618 | SetError( FormulaError::IllegalParameter ); | |||
4619 | } | |||
4620 | if ( nGlobalError != FormulaError::NONE ) | |||
4621 | nVal = 0; | |||
4622 | } | |||
4623 | PushDouble( static_cast<double>(nVal) ); | |||
4624 | } | |||
4625 | ||||
4626 | namespace { | |||
4627 | ||||
4628 | class VectorMatrixAccessor | |||
4629 | { | |||
4630 | public: | |||
4631 | VectorMatrixAccessor(const ScMatrix& rMat, bool bColVec) : | |||
4632 | mrMat(rMat), mbColVec(bColVec) {} | |||
4633 | ||||
4634 | bool IsEmpty(SCSIZE i) const | |||
4635 | { | |||
4636 | return mbColVec ? mrMat.IsEmpty(0, i) : mrMat.IsEmpty(i, 0); | |||
4637 | } | |||
4638 | ||||
4639 | bool IsEmptyPath(SCSIZE i) const | |||
4640 | { | |||
4641 | return mbColVec ? mrMat.IsEmptyPath(0, i) : mrMat.IsEmptyPath(i, 0); | |||
4642 | } | |||
4643 | ||||
4644 | bool IsValue(SCSIZE i) const | |||
4645 | { | |||
4646 | return mbColVec ? mrMat.IsValue(0, i) : mrMat.IsValue(i, 0); | |||
4647 | } | |||
4648 | ||||
4649 | bool IsStringOrEmpty(SCSIZE i) const | |||
4650 | { | |||
4651 | return mbColVec ? mrMat.IsStringOrEmpty(0, i) : mrMat.IsStringOrEmpty(i, 0); | |||
4652 | } | |||
4653 | ||||
4654 | double GetDouble(SCSIZE i) const | |||
4655 | { | |||
4656 | return mbColVec ? mrMat.GetDouble(0, i) : mrMat.GetDouble(i, 0); | |||
4657 | } | |||
4658 | ||||
4659 | OUString GetString(SCSIZE i) const | |||
4660 | { | |||
4661 | return mbColVec ? mrMat.GetString(0, i).getString() : mrMat.GetString(i, 0).getString(); | |||
4662 | } | |||
4663 | ||||
4664 | SCSIZE GetElementCount() const | |||
4665 | { | |||
4666 | SCSIZE nC, nR; | |||
4667 | mrMat.GetDimensions(nC, nR); | |||
4668 | return mbColVec ? nR : nC; | |||
4669 | } | |||
4670 | ||||
4671 | private: | |||
4672 | const ScMatrix& mrMat; | |||
4673 | bool mbColVec; | |||
4674 | }; | |||
4675 | ||||
4676 | /** returns -1 when the matrix value is smaller than the query value, 0 when | |||
4677 | they are equal, and 1 when the matrix value is larger than the query | |||
4678 | value. */ | |||
4679 | sal_Int32 lcl_CompareMatrix2Query( | |||
4680 | SCSIZE i, const VectorMatrixAccessor& rMat, const ScQueryEntry& rEntry) | |||
4681 | { | |||
4682 | if (rMat.IsEmpty(i)) | |||
4683 | { | |||
4684 | /* TODO: in case we introduced query for real empty this would have to | |||
4685 | * be changed! */ | |||
4686 | return -1; // empty always less than anything else | |||
4687 | } | |||
4688 | ||||
4689 | /* FIXME: what is an empty path (result of IF(false;true_path) in | |||
4690 | * comparisons? */ | |||
4691 | ||||
4692 | bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString; | |||
4693 | if (rMat.IsValue(i)) | |||
4694 | { | |||
4695 | const double nVal1 = rMat.GetDouble(i); | |||
4696 | if (!std::isfinite(nVal1)) | |||
4697 | { | |||
4698 | // XXX Querying for error values is not required, otherwise we'd | |||
4699 | // need to check here. | |||
4700 | return 1; // error always greater than numeric or string | |||
4701 | } | |||
4702 | ||||
4703 | if (bByString) | |||
4704 | return -1; // numeric always less than string | |||
4705 | ||||
4706 | const double nVal2 = rEntry.GetQueryItem().mfVal; | |||
4707 | // XXX Querying for error values is not required, otherwise we'd need | |||
4708 | // to check here and move that check before the bByString check. | |||
4709 | if (nVal1 == nVal2) | |||
4710 | return 0; | |||
4711 | ||||
4712 | return nVal1 < nVal2 ? -1 : 1; | |||
4713 | } | |||
4714 | ||||
4715 | if (!bByString) | |||
4716 | return 1; // string always greater than numeric | |||
4717 | ||||
4718 | OUString aStr1 = rMat.GetString(i); | |||
4719 | OUString aStr2 = rEntry.GetQueryItem().maString.getString(); | |||
4720 | ||||
4721 | return ScGlobal::GetCollator()->compareString(aStr1, aStr2); // case-insensitive | |||
4722 | } | |||
4723 | ||||
4724 | /** returns the last item with the identical value as the original item | |||
4725 | value. */ | |||
4726 | void lcl_GetLastMatch( SCSIZE& rIndex, const VectorMatrixAccessor& rMat, | |||
4727 | SCSIZE nMatCount, bool bReverse) | |||
4728 | { | |||
4729 | if (rMat.IsValue(rIndex)) | |||
4730 | { | |||
4731 | double nVal = rMat.GetDouble(rIndex); | |||
4732 | if (bReverse) | |||
4733 | while (rIndex > 0 && rMat.IsValue(rIndex-1) && | |||
4734 | nVal == rMat.GetDouble(rIndex-1)) | |||
4735 | --rIndex; | |||
4736 | else | |||
4737 | while (rIndex < nMatCount-1 && rMat.IsValue(rIndex+1) && | |||
4738 | nVal == rMat.GetDouble(rIndex+1)) | |||
4739 | ++rIndex; | |||
4740 | } | |||
4741 | // Order of IsEmptyPath, IsEmpty, IsStringOrEmpty is significant! | |||
4742 | else if (rMat.IsEmptyPath(rIndex)) | |||
4743 | { | |||
4744 | if (bReverse) | |||
4745 | while (rIndex > 0 && rMat.IsEmptyPath(rIndex-1)) | |||
4746 | --rIndex; | |||
4747 | else | |||
4748 | while (rIndex < nMatCount-1 && rMat.IsEmptyPath(rIndex+1)) | |||
4749 | ++rIndex; | |||
4750 | } | |||
4751 | else if (rMat.IsEmpty(rIndex)) | |||
4752 | { | |||
4753 | if (bReverse) | |||
4754 | while (rIndex > 0 && rMat.IsEmpty(rIndex-1)) | |||
4755 | --rIndex; | |||
4756 | else | |||
4757 | while (rIndex < nMatCount-1 && rMat.IsEmpty(rIndex+1)) | |||
4758 | ++rIndex; | |||
4759 | } | |||
4760 | else if (rMat.IsStringOrEmpty(rIndex)) | |||
4761 | { | |||
4762 | OUString aStr( rMat.GetString(rIndex)); | |||
4763 | if (bReverse) | |||
4764 | while (rIndex > 0 && rMat.IsStringOrEmpty(rIndex-1) && | |||
4765 | aStr == rMat.GetString(rIndex-1)) | |||
4766 | --rIndex; | |||
4767 | else | |||
4768 | while (rIndex < nMatCount-1 && rMat.IsStringOrEmpty(rIndex+1) && | |||
4769 | aStr == rMat.GetString(rIndex+1)) | |||
4770 | ++rIndex; | |||
4771 | } | |||
4772 | else | |||
4773 | { | |||
4774 | OSL_FAIL("lcl_GetLastMatch: unhandled matrix type")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "4774" ": "), "%s", "lcl_GetLastMatch: unhandled matrix type" ); } } while (false); | |||
4775 | } | |||
4776 | } | |||
4777 | ||||
4778 | } | |||
4779 | ||||
4780 | void ScInterpreter::ScMatch() | |||
4781 | { | |||
4782 | ||||
4783 | sal_uInt8 nParamCount = GetByte(); | |||
4784 | if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) | |||
4785 | return; | |||
4786 | ||||
4787 | double fTyp; | |||
4788 | if (nParamCount == 3) | |||
4789 | fTyp = GetDouble(); | |||
4790 | else | |||
4791 | fTyp = 1.0; | |||
4792 | SCCOL nCol1 = 0; | |||
4793 | SCROW nRow1 = 0; | |||
4794 | SCTAB nTab1 = 0; | |||
4795 | SCCOL nCol2 = 0; | |||
4796 | SCROW nRow2 = 0; | |||
4797 | ScMatrixRef pMatSrc = nullptr; | |||
4798 | ||||
4799 | switch (GetStackType()) | |||
4800 | { | |||
4801 | case svSingleRef: | |||
4802 | PopSingleRef( nCol1, nRow1, nTab1); | |||
4803 | nCol2 = nCol1; | |||
4804 | nRow2 = nRow1; | |||
4805 | break; | |||
4806 | case svDoubleRef: | |||
4807 | { | |||
4808 | SCTAB nTab2 = 0; | |||
4809 | PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
4810 | if (nTab1 != nTab2 || (nCol1 != nCol2 && nRow1 != nRow2)) | |||
4811 | { | |||
4812 | PushIllegalParameter(); | |||
4813 | return; | |||
4814 | } | |||
4815 | } | |||
4816 | break; | |||
4817 | case svMatrix: | |||
4818 | case svExternalDoubleRef: | |||
4819 | { | |||
4820 | if (GetStackType() == svMatrix) | |||
4821 | pMatSrc = PopMatrix(); | |||
4822 | else | |||
4823 | PopExternalDoubleRef(pMatSrc); | |||
4824 | ||||
4825 | if (!pMatSrc) | |||
4826 | { | |||
4827 | PushIllegalParameter(); | |||
4828 | return; | |||
4829 | } | |||
4830 | } | |||
4831 | break; | |||
4832 | default: | |||
4833 | PushIllegalParameter(); | |||
4834 | return; | |||
4835 | } | |||
4836 | ||||
4837 | if (nGlobalError == FormulaError::NONE) | |||
4838 | { | |||
4839 | double fVal; | |||
4840 | ScQueryParam rParam; | |||
4841 | rParam.nCol1 = nCol1; | |||
4842 | rParam.nRow1 = nRow1; | |||
4843 | rParam.nCol2 = nCol2; | |||
4844 | rParam.nTab = nTab1; | |||
4845 | ||||
4846 | ScQueryEntry& rEntry = rParam.GetEntry(0); | |||
4847 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
4848 | rEntry.bDoQuery = true; | |||
4849 | if (fTyp < 0.0) | |||
4850 | rEntry.eOp = SC_GREATER_EQUAL; | |||
4851 | else if (fTyp > 0.0) | |||
4852 | rEntry.eOp = SC_LESS_EQUAL; | |||
4853 | switch ( GetStackType() ) | |||
4854 | { | |||
4855 | case svDouble: | |||
4856 | { | |||
4857 | fVal = GetDouble(); | |||
4858 | rItem.mfVal = fVal; | |||
4859 | rItem.meType = ScQueryEntry::ByValue; | |||
4860 | } | |||
4861 | break; | |||
4862 | case svString: | |||
4863 | { | |||
4864 | rItem.meType = ScQueryEntry::ByString; | |||
4865 | rItem.maString = GetString(); | |||
4866 | } | |||
4867 | break; | |||
4868 | case svDoubleRef : | |||
4869 | case svSingleRef : | |||
4870 | { | |||
4871 | ScAddress aAdr; | |||
4872 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
4873 | { | |||
4874 | PushInt(0); | |||
4875 | return ; | |||
4876 | } | |||
4877 | ScRefCellValue aCell(mrDoc, aAdr); | |||
4878 | if (aCell.hasNumeric()) | |||
4879 | { | |||
4880 | fVal = GetCellValue(aAdr, aCell); | |||
4881 | rItem.meType = ScQueryEntry::ByValue; | |||
4882 | rItem.mfVal = fVal; | |||
4883 | } | |||
4884 | else | |||
4885 | { | |||
4886 | GetCellString(rItem.maString, aCell); | |||
4887 | rItem.meType = ScQueryEntry::ByString; | |||
4888 | } | |||
4889 | } | |||
4890 | break; | |||
4891 | case svExternalSingleRef: | |||
4892 | { | |||
4893 | ScExternalRefCache::TokenRef pToken; | |||
4894 | PopExternalSingleRef(pToken); | |||
4895 | if (nGlobalError != FormulaError::NONE) | |||
4896 | { | |||
4897 | PushError( nGlobalError); | |||
4898 | return; | |||
4899 | } | |||
4900 | if (pToken->GetType() == svDouble) | |||
4901 | { | |||
4902 | rItem.meType = ScQueryEntry::ByValue; | |||
4903 | rItem.mfVal = pToken->GetDouble(); | |||
4904 | } | |||
4905 | else | |||
4906 | { | |||
4907 | rItem.meType = ScQueryEntry::ByString; | |||
4908 | rItem.maString = pToken->GetString(); | |||
4909 | } | |||
4910 | } | |||
4911 | break; | |||
4912 | case svExternalDoubleRef: | |||
4913 | case svMatrix : | |||
4914 | { | |||
4915 | svl::SharedString aStr; | |||
4916 | ScMatValType nType = GetDoubleOrStringFromMatrix( | |||
4917 | rItem.mfVal, aStr); | |||
4918 | rItem.maString = aStr; | |||
4919 | rItem.meType = ScMatrix::IsNonValueType(nType) ? | |||
4920 | ScQueryEntry::ByString : ScQueryEntry::ByValue; | |||
4921 | } | |||
4922 | break; | |||
4923 | default: | |||
4924 | { | |||
4925 | PushIllegalParameter(); | |||
4926 | return; | |||
4927 | } | |||
4928 | } | |||
4929 | if (rItem.meType == ScQueryEntry::ByString) | |||
4930 | { | |||
4931 | bool bIsVBAMode = mrDoc.IsInVBAMode(); | |||
4932 | ||||
4933 | if ( bIsVBAMode ) | |||
4934 | rParam.eSearchType = utl::SearchParam::SearchType::Wildcard; | |||
4935 | else | |||
4936 | rParam.eSearchType = DetectSearchType(rEntry.GetQueryItem().maString.getString(), mrDoc); | |||
4937 | } | |||
4938 | ||||
4939 | if (pMatSrc) // The source data is matrix array. | |||
4940 | { | |||
4941 | SCSIZE nC, nR; | |||
4942 | pMatSrc->GetDimensions( nC, nR); | |||
4943 | if (nC > 1 && nR > 1) | |||
4944 | { | |||
4945 | // The source matrix must be a vector. | |||
4946 | PushIllegalParameter(); | |||
4947 | return; | |||
4948 | } | |||
4949 | ||||
4950 | // Do not propagate errors from matrix while searching. | |||
4951 | pMatSrc->SetErrorInterpreter( nullptr); | |||
4952 | ||||
4953 | SCSIZE nMatCount = (nC == 1) ? nR : nC; | |||
4954 | VectorMatrixAccessor aMatAcc(*pMatSrc, nC == 1); | |||
4955 | ||||
4956 | // simple serial search for equality mode (source data doesn't | |||
4957 | // need to be sorted). | |||
4958 | ||||
4959 | if (rEntry.eOp == SC_EQUAL) | |||
4960 | { | |||
4961 | for (SCSIZE i = 0; i < nMatCount; ++i) | |||
4962 | { | |||
4963 | if (lcl_CompareMatrix2Query( i, aMatAcc, rEntry) == 0) | |||
4964 | { | |||
4965 | PushDouble(i+1); // found ! | |||
4966 | return; | |||
4967 | } | |||
4968 | } | |||
4969 | PushNA(); // not found | |||
4970 | return; | |||
4971 | } | |||
4972 | ||||
4973 | // binary search for non-equality mode (the source data is | |||
4974 | // assumed to be sorted). | |||
4975 | ||||
4976 | bool bAscOrder = (rEntry.eOp == SC_LESS_EQUAL); | |||
4977 | SCSIZE nFirst = 0, nLast = nMatCount-1, nHitIndex = 0; | |||
4978 | for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst) | |||
4979 | { | |||
4980 | SCSIZE nMid = nFirst + nLen/2; | |||
4981 | sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc, rEntry); | |||
4982 | if (nCmp == 0) | |||
4983 | { | |||
4984 | // exact match. find the last item with the same value. | |||
4985 | lcl_GetLastMatch( nMid, aMatAcc, nMatCount, false); | |||
4986 | PushDouble( nMid+1); | |||
4987 | return; | |||
4988 | } | |||
4989 | ||||
4990 | if (nLen == 1) // first and last items are next to each other. | |||
4991 | { | |||
4992 | if (nCmp < 0) | |||
4993 | nHitIndex = bAscOrder ? nLast : nFirst; | |||
4994 | else | |||
4995 | nHitIndex = bAscOrder ? nFirst : nLast; | |||
4996 | break; | |||
4997 | } | |||
4998 | ||||
4999 | if (nCmp < 0) | |||
5000 | { | |||
5001 | if (bAscOrder) | |||
5002 | nFirst = nMid; | |||
5003 | else | |||
5004 | nLast = nMid; | |||
5005 | } | |||
5006 | else | |||
5007 | { | |||
5008 | if (bAscOrder) | |||
5009 | nLast = nMid; | |||
5010 | else | |||
5011 | nFirst = nMid; | |||
5012 | } | |||
5013 | } | |||
5014 | ||||
5015 | if (nHitIndex == nMatCount-1) // last item | |||
5016 | { | |||
5017 | sal_Int32 nCmp = lcl_CompareMatrix2Query( nHitIndex, aMatAcc, rEntry); | |||
5018 | if ((bAscOrder && nCmp <= 0) || (!bAscOrder && nCmp >= 0)) | |||
5019 | { | |||
5020 | // either the last item is an exact match or the real | |||
5021 | // hit is beyond the last item. | |||
5022 | PushDouble( nHitIndex+1); | |||
5023 | return; | |||
5024 | } | |||
5025 | } | |||
5026 | ||||
5027 | if (nHitIndex > 0) // valid hit must be 2nd item or higher | |||
5028 | { | |||
5029 | PushDouble( nHitIndex); // non-exact match | |||
5030 | return; | |||
5031 | } | |||
5032 | ||||
5033 | PushNA(); | |||
5034 | return; | |||
5035 | } | |||
5036 | ||||
5037 | SCCOLROW nDelta = 0; | |||
5038 | if (nCol1 == nCol2) | |||
5039 | { // search row in column | |||
5040 | rParam.nRow2 = nRow2; | |||
5041 | rEntry.nField = nCol1; | |||
5042 | ScAddress aResultPos( nCol1, nRow1, nTab1); | |||
5043 | if (!LookupQueryWithCache( aResultPos, rParam)) | |||
5044 | { | |||
5045 | PushNA(); | |||
5046 | return; | |||
5047 | } | |||
5048 | nDelta = aResultPos.Row() - nRow1; | |||
5049 | } | |||
5050 | else | |||
5051 | { // search column in row | |||
5052 | SCCOL nC; | |||
5053 | rParam.bByRow = false; | |||
5054 | rParam.nRow2 = nRow1; | |||
5055 | rEntry.nField = nCol1; | |||
5056 | ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false); | |||
5057 | // Advance Entry.nField in Iterator if column changed | |||
5058 | aCellIter.SetAdvanceQueryParamEntryField( true ); | |||
5059 | if (fTyp == 0.0) | |||
5060 | { // EQUAL | |||
5061 | if ( aCellIter.GetFirst() ) | |||
5062 | nC = aCellIter.GetCol(); | |||
5063 | else | |||
5064 | { | |||
5065 | PushNA(); | |||
5066 | return; | |||
5067 | } | |||
5068 | } | |||
5069 | else | |||
5070 | { // <= or >= | |||
5071 | SCROW nR; | |||
5072 | if ( !aCellIter.FindEqualOrSortedLastInRange( nC, nR ) ) | |||
5073 | { | |||
5074 | PushNA(); | |||
5075 | return; | |||
5076 | } | |||
5077 | } | |||
5078 | nDelta = nC - nCol1; | |||
5079 | } | |||
5080 | PushDouble(static_cast<double>(nDelta + 1)); | |||
5081 | } | |||
5082 | else | |||
5083 | PushIllegalParameter(); | |||
5084 | } | |||
5085 | ||||
5086 | namespace { | |||
5087 | ||||
5088 | bool isCellContentEmpty( const ScRefCellValue& rCell ) | |||
5089 | { | |||
5090 | switch (rCell.meType) | |||
5091 | { | |||
5092 | case CELLTYPE_VALUE: | |||
5093 | case CELLTYPE_STRING: | |||
5094 | case CELLTYPE_EDIT: | |||
5095 | return false; | |||
5096 | case CELLTYPE_FORMULA: | |||
5097 | { | |||
5098 | // NOTE: Excel treats ="" in a referenced cell as blank in | |||
5099 | // COUNTBLANK() but not in ISBLANK(), which is inconsistent. | |||
5100 | // COUNTBLANK() tests the (display) result whereas ISBLANK() tests | |||
5101 | // the cell content. | |||
5102 | // ODFF allows both for COUNTBLANK(). | |||
5103 | // OOo and LibreOffice prior to 4.4 did not treat ="" as blank in | |||
5104 | // COUNTBLANK(), we now do for Excel interoperability. | |||
5105 | /* TODO: introduce yet another compatibility option? */ | |||
5106 | sc::FormulaResultValue aRes = rCell.mpFormula->GetResult(); | |||
5107 | if (aRes.meType != sc::FormulaResultValue::String) | |||
5108 | return false; | |||
5109 | if (!aRes.maString.isEmpty()) | |||
5110 | return false; | |||
5111 | } | |||
5112 | break; | |||
5113 | default: | |||
5114 | ; | |||
5115 | } | |||
5116 | ||||
5117 | return true; | |||
5118 | } | |||
5119 | ||||
5120 | } | |||
5121 | ||||
5122 | void ScInterpreter::ScCountEmptyCells() | |||
5123 | { | |||
5124 | if ( !MustHaveParamCount( GetByte(), 1 ) ) | |||
5125 | return; | |||
5126 | ||||
5127 | const SCSIZE nMatRows = GetRefListArrayMaxSize(1); | |||
5128 | // There's either one RefList and nothing else, or none. | |||
5129 | ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr); | |||
5130 | sal_uLong nMaxCount = 0, nCount = 0; | |||
5131 | switch (GetStackType()) | |||
5132 | { | |||
5133 | case svSingleRef : | |||
5134 | { | |||
5135 | nMaxCount = 1; | |||
5136 | ScAddress aAdr; | |||
5137 | PopSingleRef( aAdr ); | |||
5138 | ScRefCellValue aCell(mrDoc, aAdr); | |||
5139 | if (!isCellContentEmpty(aCell)) | |||
5140 | nCount = 1; | |||
5141 | } | |||
5142 | break; | |||
5143 | case svRefList : | |||
5144 | case svDoubleRef : | |||
5145 | { | |||
5146 | ScRange aRange; | |||
5147 | short nParam = 1; | |||
5148 | SCSIZE nRefListArrayPos = 0; | |||
5149 | size_t nRefInList = 0; | |||
5150 | while (nParam-- > 0) | |||
5151 | { | |||
5152 | nRefListArrayPos = nRefInList; | |||
5153 | PopDoubleRef( aRange, nParam, nRefInList); | |||
5154 | nMaxCount += | |||
5155 | static_cast<sal_uLong>(aRange.aEnd.Row() - aRange.aStart.Row() + 1) * | |||
5156 | static_cast<sal_uLong>(aRange.aEnd.Col() - aRange.aStart.Col() + 1) * | |||
5157 | static_cast<sal_uLong>(aRange.aEnd.Tab() - aRange.aStart.Tab() + 1); | |||
5158 | ||||
5159 | ScCellIterator aIter( mrDoc, aRange, mnSubTotalFlags); | |||
5160 | for (bool bHas = aIter.first(); bHas; bHas = aIter.next()) | |||
5161 | { | |||
5162 | const ScRefCellValue& rCell = aIter.getRefCellValue(); | |||
5163 | if (!isCellContentEmpty(rCell)) | |||
5164 | ++nCount; | |||
5165 | } | |||
5166 | if (xResMat) | |||
5167 | { | |||
5168 | xResMat->PutDouble( nMaxCount - nCount, 0, nRefListArrayPos); | |||
5169 | nMaxCount = nCount = 0; | |||
5170 | } | |||
5171 | } | |||
5172 | } | |||
5173 | break; | |||
5174 | case svMatrix: | |||
5175 | case svExternalSingleRef: | |||
5176 | case svExternalDoubleRef: | |||
5177 | { | |||
5178 | ScMatrixRef xMat = GetMatrix(); | |||
5179 | if (!xMat) | |||
5180 | SetError( FormulaError::IllegalParameter); | |||
5181 | else | |||
5182 | { | |||
5183 | SCSIZE nC, nR; | |||
5184 | xMat->GetDimensions( nC, nR); | |||
5185 | nMaxCount = nC * nR; | |||
5186 | // Numbers (implicit), strings and error values, ignore empty | |||
5187 | // strings as those if not entered in an inline array are the | |||
5188 | // result of a formula, to be par with a reference to formula | |||
5189 | // cell as *visual* blank, see isCellContentEmpty() above. | |||
5190 | nCount = xMat->Count( true, true, true); | |||
5191 | } | |||
5192 | } | |||
5193 | break; | |||
5194 | default : SetError(FormulaError::IllegalParameter); break; | |||
5195 | } | |||
5196 | if (xResMat) | |||
5197 | PushMatrix( xResMat); | |||
5198 | else | |||
5199 | PushDouble(nMaxCount - nCount); | |||
5200 | } | |||
5201 | ||||
5202 | void ScInterpreter::IterateParametersIf( ScIterFuncIf eFunc ) | |||
5203 | { | |||
5204 | sal_uInt8 nParamCount = GetByte(); | |||
5205 | if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) | |||
5206 | return; | |||
5207 | ||||
5208 | SCCOL nCol3 = 0; | |||
5209 | SCROW nRow3 = 0; | |||
5210 | SCTAB nTab3 = 0; | |||
5211 | ||||
5212 | ScMatrixRef pSumExtraMatrix; | |||
5213 | bool bSumExtraRange = (nParamCount == 3); | |||
5214 | if (bSumExtraRange) | |||
5215 | { | |||
5216 | // Save only the upperleft cell in case of cell range. The geometry | |||
5217 | // of the 3rd parameter is taken from the 1st parameter. | |||
5218 | ||||
5219 | switch ( GetStackType() ) | |||
5220 | { | |||
5221 | case svDoubleRef : | |||
5222 | { | |||
5223 | SCCOL nColJunk = 0; | |||
5224 | SCROW nRowJunk = 0; | |||
5225 | SCTAB nTabJunk = 0; | |||
5226 | PopDoubleRef( nCol3, nRow3, nTab3, nColJunk, nRowJunk, nTabJunk ); | |||
5227 | if ( nTabJunk != nTab3 ) | |||
5228 | { | |||
5229 | PushError( FormulaError::IllegalParameter); | |||
5230 | return; | |||
5231 | } | |||
5232 | } | |||
5233 | break; | |||
5234 | case svSingleRef : | |||
5235 | PopSingleRef( nCol3, nRow3, nTab3 ); | |||
5236 | break; | |||
5237 | case svMatrix: | |||
5238 | pSumExtraMatrix = PopMatrix(); | |||
5239 | // nCol3, nRow3, nTab3 remain 0 | |||
5240 | break; | |||
5241 | case svExternalSingleRef: | |||
5242 | { | |||
5243 | pSumExtraMatrix = GetNewMat(1,1); | |||
5244 | ScExternalRefCache::TokenRef pToken; | |||
5245 | PopExternalSingleRef(pToken); | |||
5246 | if (nGlobalError != FormulaError::NONE) | |||
5247 | { | |||
5248 | PushError( nGlobalError); | |||
5249 | return; | |||
5250 | } | |||
5251 | ||||
5252 | if (pToken->GetType() == svDouble) | |||
5253 | pSumExtraMatrix->PutDouble(pToken->GetDouble(), 0, 0); | |||
5254 | else | |||
5255 | pSumExtraMatrix->PutString(pToken->GetString(), 0, 0); | |||
5256 | } | |||
5257 | break; | |||
5258 | case svExternalDoubleRef: | |||
5259 | PopExternalDoubleRef(pSumExtraMatrix); | |||
5260 | break; | |||
5261 | default: | |||
5262 | PushError( FormulaError::IllegalParameter); | |||
5263 | return; | |||
5264 | } | |||
5265 | } | |||
5266 | ||||
5267 | svl::SharedString aString; | |||
5268 | double fVal = 0.0; | |||
5269 | bool bIsString = true; | |||
5270 | switch ( GetStackType() ) | |||
5271 | { | |||
5272 | case svDoubleRef : | |||
5273 | case svSingleRef : | |||
5274 | { | |||
5275 | ScAddress aAdr; | |||
5276 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
5277 | { | |||
5278 | PushError( nGlobalError); | |||
5279 | return; | |||
5280 | } | |||
5281 | ||||
5282 | ScRefCellValue aCell(mrDoc, aAdr); | |||
5283 | switch (aCell.meType) | |||
5284 | { | |||
5285 | case CELLTYPE_VALUE : | |||
5286 | fVal = GetCellValue(aAdr, aCell); | |||
5287 | bIsString = false; | |||
5288 | break; | |||
5289 | case CELLTYPE_FORMULA : | |||
5290 | if (aCell.mpFormula->IsValue()) | |||
5291 | { | |||
5292 | fVal = GetCellValue(aAdr, aCell); | |||
5293 | bIsString = false; | |||
5294 | } | |||
5295 | else | |||
5296 | GetCellString(aString, aCell); | |||
5297 | break; | |||
5298 | case CELLTYPE_STRING : | |||
5299 | case CELLTYPE_EDIT : | |||
5300 | GetCellString(aString, aCell); | |||
5301 | break; | |||
5302 | default: | |||
5303 | fVal = 0.0; | |||
5304 | bIsString = false; | |||
5305 | } | |||
5306 | } | |||
5307 | break; | |||
5308 | case svString: | |||
5309 | aString = GetString(); | |||
5310 | break; | |||
5311 | case svMatrix : | |||
5312 | case svExternalDoubleRef: | |||
5313 | { | |||
5314 | ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString); | |||
5315 | bIsString = ScMatrix::IsRealStringType( nType); | |||
5316 | } | |||
5317 | break; | |||
5318 | case svExternalSingleRef: | |||
5319 | { | |||
5320 | ScExternalRefCache::TokenRef pToken; | |||
5321 | PopExternalSingleRef(pToken); | |||
5322 | if (nGlobalError == FormulaError::NONE) | |||
5323 | { | |||
5324 | if (pToken->GetType() == svDouble) | |||
5325 | { | |||
5326 | fVal = pToken->GetDouble(); | |||
5327 | bIsString = false; | |||
5328 | } | |||
5329 | else | |||
5330 | aString = pToken->GetString(); | |||
5331 | } | |||
5332 | } | |||
5333 | break; | |||
5334 | default: | |||
5335 | { | |||
5336 | fVal = GetDouble(); | |||
5337 | bIsString = false; | |||
5338 | } | |||
5339 | } | |||
5340 | ||||
5341 | double fSum = 0.0; | |||
5342 | double fMem = 0.0; | |||
5343 | double fRes = 0.0; | |||
5344 | double fCount = 0.0; | |||
5345 | bool bNull = true; | |||
5346 | short nParam = 1; | |||
5347 | const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam); | |||
5348 | // There's either one RefList and nothing else, or none. | |||
5349 | ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr); | |||
5350 | SCSIZE nRefListArrayPos = 0; | |||
5351 | size_t nRefInList = 0; | |||
5352 | while (nParam-- > 0) | |||
5353 | { | |||
5354 | SCCOL nCol1 = 0; | |||
5355 | SCROW nRow1 = 0; | |||
5356 | SCTAB nTab1 = 0; | |||
5357 | SCCOL nCol2 = 0; | |||
5358 | SCROW nRow2 = 0; | |||
5359 | SCTAB nTab2 = 0; | |||
5360 | ScMatrixRef pQueryMatrix; | |||
5361 | switch ( GetStackType() ) | |||
5362 | { | |||
5363 | case svRefList : | |||
5364 | if (bSumExtraRange) | |||
5365 | { | |||
5366 | /* TODO: this could resolve if all refs are of the same size */ | |||
5367 | SetError( FormulaError::IllegalParameter); | |||
5368 | } | |||
5369 | else | |||
5370 | { | |||
5371 | nRefListArrayPos = nRefInList; | |||
5372 | ScRange aRange; | |||
5373 | PopDoubleRef( aRange, nParam, nRefInList); | |||
5374 | aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
5375 | } | |||
5376 | break; | |||
5377 | case svDoubleRef : | |||
5378 | PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); | |||
5379 | break; | |||
5380 | case svSingleRef : | |||
5381 | PopSingleRef( nCol1, nRow1, nTab1 ); | |||
5382 | nCol2 = nCol1; | |||
5383 | nRow2 = nRow1; | |||
5384 | nTab2 = nTab1; | |||
5385 | break; | |||
5386 | case svMatrix: | |||
5387 | case svExternalSingleRef: | |||
5388 | case svExternalDoubleRef: | |||
5389 | { | |||
5390 | pQueryMatrix = GetMatrix(); | |||
5391 | if (!pQueryMatrix) | |||
5392 | { | |||
5393 | PushError( FormulaError::IllegalParameter); | |||
5394 | return; | |||
5395 | } | |||
5396 | nCol1 = 0; | |||
5397 | nRow1 = 0; | |||
5398 | nTab1 = 0; | |||
5399 | SCSIZE nC, nR; | |||
5400 | pQueryMatrix->GetDimensions( nC, nR); | |||
5401 | nCol2 = static_cast<SCCOL>(nC - 1); | |||
5402 | nRow2 = static_cast<SCROW>(nR - 1); | |||
5403 | nTab2 = 0; | |||
5404 | } | |||
5405 | break; | |||
5406 | default: | |||
5407 | SetError( FormulaError::IllegalParameter); | |||
5408 | } | |||
5409 | if ( nTab1 != nTab2 ) | |||
5410 | { | |||
5411 | SetError( FormulaError::IllegalParameter); | |||
5412 | } | |||
5413 | ||||
5414 | if (bSumExtraRange) | |||
5415 | { | |||
5416 | // Take the range geometry of the 1st parameter and apply it to | |||
5417 | // the 3rd. If parts of the resulting range would point outside | |||
5418 | // the sheet, don't complain but silently ignore and simply cut | |||
5419 | // them away, this is what Xcl does :-/ | |||
5420 | ||||
5421 | // For the cut-away part we also don't need to determine the | |||
5422 | // criteria match, so shrink the source range accordingly, | |||
5423 | // instead of the result range. | |||
5424 | SCCOL nColDelta = nCol2 - nCol1; | |||
5425 | SCROW nRowDelta = nRow2 - nRow1; | |||
5426 | SCCOL nMaxCol; | |||
5427 | SCROW nMaxRow; | |||
5428 | if (pSumExtraMatrix) | |||
5429 | { | |||
5430 | SCSIZE nC, nR; | |||
5431 | pSumExtraMatrix->GetDimensions( nC, nR); | |||
5432 | nMaxCol = static_cast<SCCOL>(nC - 1); | |||
5433 | nMaxRow = static_cast<SCROW>(nR - 1); | |||
5434 | } | |||
5435 | else | |||
5436 | { | |||
5437 | nMaxCol = mrDoc.MaxCol(); | |||
5438 | nMaxRow = mrDoc.MaxRow(); | |||
5439 | } | |||
5440 | if (nCol3 + nColDelta > nMaxCol) | |||
5441 | { | |||
5442 | SCCOL nNewDelta = nMaxCol - nCol3; | |||
5443 | nCol2 = nCol1 + nNewDelta; | |||
5444 | } | |||
5445 | ||||
5446 | if (nRow3 + nRowDelta > nMaxRow) | |||
5447 | { | |||
5448 | SCROW nNewDelta = nMaxRow - nRow3; | |||
5449 | nRow2 = nRow1 + nNewDelta; | |||
5450 | } | |||
5451 | } | |||
5452 | else | |||
5453 | { | |||
5454 | nCol3 = nCol1; | |||
5455 | nRow3 = nRow1; | |||
5456 | nTab3 = nTab1; | |||
5457 | } | |||
5458 | ||||
5459 | if (nGlobalError == FormulaError::NONE) | |||
5460 | { | |||
5461 | ScQueryParam rParam; | |||
5462 | rParam.nRow1 = nRow1; | |||
5463 | rParam.nRow2 = nRow2; | |||
5464 | ||||
5465 | ScQueryEntry& rEntry = rParam.GetEntry(0); | |||
5466 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
5467 | rEntry.bDoQuery = true; | |||
5468 | if (!bIsString) | |||
5469 | { | |||
5470 | rItem.meType = ScQueryEntry::ByValue; | |||
5471 | rItem.mfVal = fVal; | |||
5472 | rEntry.eOp = SC_EQUAL; | |||
5473 | } | |||
5474 | else | |||
5475 | { | |||
5476 | rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter); | |||
5477 | if (rItem.meType == ScQueryEntry::ByString) | |||
5478 | rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); | |||
5479 | } | |||
5480 | ScAddress aAdr; | |||
5481 | aAdr.SetTab( nTab3 ); | |||
5482 | rParam.nCol1 = nCol1; | |||
5483 | rParam.nCol2 = nCol2; | |||
5484 | rEntry.nField = nCol1; | |||
5485 | SCCOL nColDiff = nCol3 - nCol1; | |||
5486 | SCROW nRowDiff = nRow3 - nRow1; | |||
5487 | if (pQueryMatrix) | |||
5488 | { | |||
5489 | // Never case-sensitive. | |||
5490 | sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType); | |||
5491 | ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); | |||
5492 | if (nGlobalError != FormulaError::NONE || !pResultMatrix) | |||
5493 | { | |||
5494 | SetError( FormulaError::IllegalParameter); | |||
5495 | } | |||
5496 | ||||
5497 | if (pSumExtraMatrix) | |||
5498 | { | |||
5499 | for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) | |||
5500 | { | |||
5501 | for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) | |||
5502 | { | |||
5503 | if (pResultMatrix->IsValue( nCol, nRow) && | |||
5504 | pResultMatrix->GetDouble( nCol, nRow)) | |||
5505 | { | |||
5506 | SCSIZE nC = nCol + nColDiff; | |||
5507 | SCSIZE nR = nRow + nRowDiff; | |||
5508 | if (pSumExtraMatrix->IsValue( nC, nR)) | |||
5509 | { | |||
5510 | fVal = pSumExtraMatrix->GetDouble( nC, nR); | |||
5511 | ++fCount; | |||
5512 | if ( bNull && fVal != 0.0 ) | |||
5513 | { | |||
5514 | bNull = false; | |||
5515 | fMem = fVal; | |||
5516 | } | |||
5517 | else | |||
5518 | fSum += fVal; | |||
5519 | } | |||
5520 | } | |||
5521 | } | |||
5522 | } | |||
5523 | } | |||
5524 | else | |||
5525 | { | |||
5526 | for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) | |||
5527 | { | |||
5528 | for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow) | |||
5529 | { | |||
5530 | if (pResultMatrix->GetDouble( nCol, nRow)) | |||
5531 | { | |||
5532 | aAdr.SetCol( nCol + nColDiff); | |||
5533 | aAdr.SetRow( nRow + nRowDiff); | |||
5534 | ScRefCellValue aCell(mrDoc, aAdr); | |||
5535 | if (aCell.hasNumeric()) | |||
5536 | { | |||
5537 | fVal = GetCellValue(aAdr, aCell); | |||
5538 | ++fCount; | |||
5539 | if ( bNull && fVal != 0.0 ) | |||
5540 | { | |||
5541 | bNull = false; | |||
5542 | fMem = fVal; | |||
5543 | } | |||
5544 | else | |||
5545 | fSum += fVal; | |||
5546 | } | |||
5547 | } | |||
5548 | } | |||
5549 | } | |||
5550 | } | |||
5551 | } | |||
5552 | else | |||
5553 | { | |||
5554 | ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false); | |||
5555 | // Increment Entry.nField in iterator when switching to next column. | |||
5556 | aCellIter.SetAdvanceQueryParamEntryField( true ); | |||
5557 | if ( aCellIter.GetFirst() ) | |||
5558 | { | |||
5559 | if (pSumExtraMatrix) | |||
5560 | { | |||
5561 | do | |||
5562 | { | |||
5563 | SCSIZE nC = aCellIter.GetCol() + nColDiff; | |||
5564 | SCSIZE nR = aCellIter.GetRow() + nRowDiff; | |||
5565 | if (pSumExtraMatrix->IsValue( nC, nR)) | |||
5566 | { | |||
5567 | fVal = pSumExtraMatrix->GetDouble( nC, nR); | |||
5568 | ++fCount; | |||
5569 | if ( bNull && fVal != 0.0 ) | |||
5570 | { | |||
5571 | bNull = false; | |||
5572 | fMem = fVal; | |||
5573 | } | |||
5574 | else | |||
5575 | fSum += fVal; | |||
5576 | } | |||
5577 | } while ( aCellIter.GetNext() ); | |||
5578 | } | |||
5579 | else | |||
5580 | { | |||
5581 | do | |||
5582 | { | |||
5583 | aAdr.SetCol( aCellIter.GetCol() + nColDiff); | |||
5584 | aAdr.SetRow( aCellIter.GetRow() + nRowDiff); | |||
5585 | ScRefCellValue aCell(mrDoc, aAdr); | |||
5586 | if (aCell.hasNumeric()) | |||
5587 | { | |||
5588 | fVal = GetCellValue(aAdr, aCell); | |||
5589 | ++fCount; | |||
5590 | if ( bNull && fVal != 0.0 ) | |||
5591 | { | |||
5592 | bNull = false; | |||
5593 | fMem = fVal; | |||
5594 | } | |||
5595 | else | |||
5596 | fSum += fVal; | |||
5597 | } | |||
5598 | } while ( aCellIter.GetNext() ); | |||
5599 | } | |||
5600 | } | |||
5601 | } | |||
5602 | } | |||
5603 | else | |||
5604 | { | |||
5605 | PushError( FormulaError::IllegalParameter); | |||
5606 | return; | |||
5607 | } | |||
5608 | ||||
5609 | switch( eFunc ) | |||
5610 | { | |||
5611 | case ifSUMIF: fRes = ::rtl::math::approxAdd( fSum, fMem ); break; | |||
5612 | case ifAVERAGEIF: fRes = div( ::rtl::math::approxAdd( fSum, fMem ), fCount); break; | |||
5613 | } | |||
5614 | if (xResMat) | |||
5615 | { | |||
5616 | if (nGlobalError == FormulaError::NONE) | |||
5617 | xResMat->PutDouble( fRes, 0, nRefListArrayPos); | |||
5618 | else | |||
5619 | { | |||
5620 | xResMat->PutError( nGlobalError, 0, nRefListArrayPos); | |||
5621 | nGlobalError = FormulaError::NONE; | |||
5622 | } | |||
5623 | fRes = fSum = fMem = fCount = 0.0; | |||
5624 | } | |||
5625 | } | |||
5626 | if (xResMat) | |||
5627 | PushMatrix( xResMat); | |||
5628 | else | |||
5629 | PushDouble( fRes); | |||
5630 | } | |||
5631 | ||||
5632 | void ScInterpreter::ScSumIf() | |||
5633 | { | |||
5634 | IterateParametersIf( ifSUMIF); | |||
5635 | } | |||
5636 | ||||
5637 | void ScInterpreter::ScAverageIf() | |||
5638 | { | |||
5639 | IterateParametersIf( ifAVERAGEIF); | |||
5640 | } | |||
5641 | ||||
5642 | void ScInterpreter::ScCountIf() | |||
5643 | { | |||
5644 | if ( !MustHaveParamCount( GetByte(), 2 ) ) | |||
5645 | return; | |||
5646 | ||||
5647 | svl::SharedString aString; | |||
5648 | double fVal = 0.0; | |||
5649 | bool bIsString = true; | |||
5650 | switch ( GetStackType() ) | |||
5651 | { | |||
5652 | case svDoubleRef : | |||
5653 | case svSingleRef : | |||
5654 | { | |||
5655 | ScAddress aAdr; | |||
5656 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
5657 | { | |||
5658 | PushInt(0); | |||
5659 | return ; | |||
5660 | } | |||
5661 | ScRefCellValue aCell(mrDoc, aAdr); | |||
5662 | switch (aCell.meType) | |||
5663 | { | |||
5664 | case CELLTYPE_VALUE : | |||
5665 | fVal = GetCellValue(aAdr, aCell); | |||
5666 | bIsString = false; | |||
5667 | break; | |||
5668 | case CELLTYPE_FORMULA : | |||
5669 | if (aCell.mpFormula->IsValue()) | |||
5670 | { | |||
5671 | fVal = GetCellValue(aAdr, aCell); | |||
5672 | bIsString = false; | |||
5673 | } | |||
5674 | else | |||
5675 | GetCellString(aString, aCell); | |||
5676 | break; | |||
5677 | case CELLTYPE_STRING : | |||
5678 | case CELLTYPE_EDIT : | |||
5679 | GetCellString(aString, aCell); | |||
5680 | break; | |||
5681 | default: | |||
5682 | fVal = 0.0; | |||
5683 | bIsString = false; | |||
5684 | } | |||
5685 | } | |||
5686 | break; | |||
5687 | case svMatrix: | |||
5688 | case svExternalSingleRef: | |||
5689 | case svExternalDoubleRef: | |||
5690 | { | |||
5691 | ScMatValType nType = GetDoubleOrStringFromMatrix(fVal, aString); | |||
5692 | bIsString = ScMatrix::IsRealStringType( nType); | |||
5693 | } | |||
5694 | break; | |||
5695 | case svString: | |||
5696 | aString = GetString(); | |||
5697 | break; | |||
5698 | default: | |||
5699 | { | |||
5700 | fVal = GetDouble(); | |||
5701 | bIsString = false; | |||
5702 | } | |||
5703 | } | |||
5704 | double fCount = 0.0; | |||
5705 | short nParam = 1; | |||
5706 | const SCSIZE nMatRows = GetRefListArrayMaxSize( nParam); | |||
5707 | // There's either one RefList and nothing else, or none. | |||
5708 | ScMatrixRef xResMat = (nMatRows ? GetNewMat( 1, nMatRows) : nullptr); | |||
5709 | SCSIZE nRefListArrayPos = 0; | |||
5710 | size_t nRefInList = 0; | |||
5711 | while (nParam-- > 0) | |||
5712 | { | |||
5713 | SCCOL nCol1 = 0; | |||
5714 | SCROW nRow1 = 0; | |||
5715 | SCTAB nTab1 = 0; | |||
5716 | SCCOL nCol2 = 0; | |||
5717 | SCROW nRow2 = 0; | |||
5718 | SCTAB nTab2 = 0; | |||
5719 | ScMatrixRef pQueryMatrix; | |||
5720 | switch ( GetStackType() ) | |||
5721 | { | |||
5722 | case svRefList : | |||
5723 | nRefListArrayPos = nRefInList; | |||
5724 | [[fallthrough]]; | |||
5725 | case svDoubleRef : | |||
5726 | { | |||
5727 | ScRange aRange; | |||
5728 | PopDoubleRef( aRange, nParam, nRefInList); | |||
5729 | aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
5730 | } | |||
5731 | break; | |||
5732 | case svSingleRef : | |||
5733 | PopSingleRef( nCol1, nRow1, nTab1 ); | |||
5734 | nCol2 = nCol1; | |||
5735 | nRow2 = nRow1; | |||
5736 | nTab2 = nTab1; | |||
5737 | break; | |||
5738 | case svMatrix: | |||
5739 | case svExternalSingleRef: | |||
5740 | case svExternalDoubleRef: | |||
5741 | { | |||
5742 | pQueryMatrix = GetMatrix(); | |||
5743 | if (!pQueryMatrix) | |||
5744 | { | |||
5745 | PushIllegalParameter(); | |||
5746 | return; | |||
5747 | } | |||
5748 | nCol1 = 0; | |||
5749 | nRow1 = 0; | |||
5750 | nTab1 = 0; | |||
5751 | SCSIZE nC, nR; | |||
5752 | pQueryMatrix->GetDimensions( nC, nR); | |||
5753 | nCol2 = static_cast<SCCOL>(nC - 1); | |||
5754 | nRow2 = static_cast<SCROW>(nR - 1); | |||
5755 | nTab2 = 0; | |||
5756 | } | |||
5757 | break; | |||
5758 | default: | |||
5759 | PopError(); // Propagate it further | |||
5760 | PushIllegalParameter(); | |||
5761 | return ; | |||
5762 | } | |||
5763 | if ( nTab1 != nTab2 ) | |||
5764 | { | |||
5765 | PushIllegalParameter(); | |||
5766 | return; | |||
5767 | } | |||
5768 | if (nCol1 > nCol2) | |||
5769 | { | |||
5770 | PushIllegalParameter(); | |||
5771 | return; | |||
5772 | } | |||
5773 | if (nGlobalError == FormulaError::NONE) | |||
5774 | { | |||
5775 | ScQueryParam rParam; | |||
5776 | rParam.nRow1 = nRow1; | |||
5777 | rParam.nRow2 = nRow2; | |||
5778 | ||||
5779 | ScQueryEntry& rEntry = rParam.GetEntry(0); | |||
5780 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
5781 | rEntry.bDoQuery = true; | |||
5782 | if (!bIsString) | |||
5783 | { | |||
5784 | rItem.meType = ScQueryEntry::ByValue; | |||
5785 | rItem.mfVal = fVal; | |||
5786 | rEntry.eOp = SC_EQUAL; | |||
5787 | } | |||
5788 | else | |||
5789 | { | |||
5790 | rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter); | |||
5791 | if (rItem.meType == ScQueryEntry::ByString) | |||
5792 | rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); | |||
5793 | } | |||
5794 | rParam.nCol1 = nCol1; | |||
5795 | rParam.nCol2 = nCol2; | |||
5796 | rEntry.nField = nCol1; | |||
5797 | if (pQueryMatrix) | |||
5798 | { | |||
5799 | // Never case-sensitive. | |||
5800 | sc::CompareOptions aOptions( mrDoc, rEntry, rParam.eSearchType); | |||
5801 | ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); | |||
5802 | if (nGlobalError != FormulaError::NONE || !pResultMatrix) | |||
5803 | { | |||
5804 | PushIllegalParameter(); | |||
5805 | return; | |||
5806 | } | |||
5807 | ||||
5808 | SCSIZE nSize = pResultMatrix->GetElementCount(); | |||
5809 | for (SCSIZE nIndex = 0; nIndex < nSize; ++nIndex) | |||
5810 | { | |||
5811 | if (pResultMatrix->IsValue( nIndex) && | |||
5812 | pResultMatrix->GetDouble( nIndex)) | |||
5813 | ++fCount; | |||
5814 | } | |||
5815 | } | |||
5816 | else | |||
5817 | { | |||
5818 | ScCountIfCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam); | |||
5819 | fCount += aCellIter.GetCount(); | |||
5820 | } | |||
5821 | } | |||
5822 | else | |||
5823 | { | |||
5824 | PushIllegalParameter(); | |||
5825 | return; | |||
5826 | } | |||
5827 | if (xResMat) | |||
5828 | { | |||
5829 | xResMat->PutDouble( fCount, 0, nRefListArrayPos); | |||
5830 | fCount = 0.0; | |||
5831 | } | |||
5832 | } | |||
5833 | if (xResMat) | |||
5834 | PushMatrix( xResMat); | |||
5835 | else | |||
5836 | PushDouble(fCount); | |||
5837 | } | |||
5838 | ||||
5839 | void ScInterpreter::IterateParametersIfs( double(*ResultFunc)( const sc::ParamIfsResult& rRes ) ) | |||
5840 | { | |||
5841 | sal_uInt8 nParamCount = GetByte(); | |||
5842 | sal_uInt8 nQueryCount = nParamCount / 2; | |||
5843 | ||||
5844 | std::vector<sal_uInt32>& vConditions = mrContext.maConditions; | |||
5845 | // vConditions is cached, although it is clear'ed after every cell is interpreted, | |||
5846 | // if the SUMIFS/COUNTIFS are part of a matrix formula, then that is not enough because | |||
5847 | // with a single InterpretTail() call it results in evaluation of all the cells in the | |||
5848 | // matrix formula. | |||
5849 | vConditions.clear(); | |||
5850 | ||||
5851 | SCCOL nStartColDiff = 0; | |||
5852 | SCCOL nEndColDiff = 0; | |||
5853 | SCROW nStartRowDiff = 0; | |||
5854 | SCROW nEndRowDiff = 0; | |||
5855 | bool bRangeReduce = false; | |||
5856 | ScRange aMainRange; | |||
5857 | ||||
5858 | // Range-reduce optimization | |||
5859 | if (nParamCount % 2) // Not COUNTIFS | |||
5860 | { | |||
5861 | bool bHasDoubleRefCriteriaRanges = true; | |||
5862 | // Do not attempt main-range reduce if any of the criteria-ranges are not double-refs. | |||
5863 | for (sal_uInt16 nParamIdx = 2; nParamIdx < nParamCount; nParamIdx += 2 ) | |||
5864 | { | |||
5865 | const formula::FormulaToken* pCriteriaRangeToken = pStack[ sp-nParamIdx ]; | |||
5866 | if (pCriteriaRangeToken->GetType() != svDoubleRef ) | |||
5867 | { | |||
5868 | bHasDoubleRefCriteriaRanges = false; | |||
5869 | break; | |||
5870 | } | |||
5871 | } | |||
5872 | ||||
5873 | // Probe the main range token, and try if we can shrink the range without altering results. | |||
5874 | const formula::FormulaToken* pMainRangeToken = pStack[ sp-nParamCount ]; | |||
5875 | if (pMainRangeToken->GetType() == svDoubleRef && bHasDoubleRefCriteriaRanges) | |||
5876 | { | |||
5877 | const ScComplexRefData* pRefData = pMainRangeToken->GetDoubleRef(); | |||
5878 | if (!pRefData->IsDeleted()) | |||
5879 | { | |||
5880 | DoubleRefToRange( *pRefData, aMainRange); | |||
5881 | ||||
5882 | if (aMainRange.aStart.Tab() == aMainRange.aEnd.Tab()) | |||
5883 | { | |||
5884 | // Shrink the range to actual data content. | |||
5885 | ScRange aSubRange = aMainRange; | |||
5886 | mrDoc.GetDataAreaSubrange(aSubRange); | |||
5887 | ||||
5888 | nStartColDiff = aSubRange.aStart.Col() - aMainRange.aStart.Col(); | |||
5889 | nStartRowDiff = aSubRange.aStart.Row() - aMainRange.aStart.Row(); | |||
5890 | ||||
5891 | nEndColDiff = aSubRange.aEnd.Col() - aMainRange.aEnd.Col(); | |||
5892 | nEndRowDiff = aSubRange.aEnd.Row() - aMainRange.aEnd.Row(); | |||
5893 | bRangeReduce = nStartColDiff || nStartRowDiff || nEndColDiff || nEndRowDiff; | |||
5894 | } | |||
5895 | } | |||
5896 | } | |||
5897 | } | |||
5898 | ||||
5899 | double fVal = 0.0; | |||
5900 | SCCOL nDimensionCols = 0; | |||
5901 | SCROW nDimensionRows = 0; | |||
5902 | const SCSIZE nRefArrayRows = GetRefListArrayMaxSize( nParamCount); | |||
5903 | std::vector<std::vector<sal_uInt32>> vRefArrayConditions; | |||
5904 | ||||
5905 | while (nParamCount > 1 && nGlobalError == FormulaError::NONE) | |||
5906 | { | |||
5907 | // take criteria | |||
5908 | svl::SharedString aString; | |||
5909 | fVal = 0.0; | |||
5910 | bool bIsString = true; | |||
5911 | switch ( GetStackType() ) | |||
5912 | { | |||
5913 | case svDoubleRef : | |||
5914 | case svSingleRef : | |||
5915 | { | |||
5916 | ScAddress aAdr; | |||
5917 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
5918 | { | |||
5919 | PushError( nGlobalError); | |||
5920 | return; | |||
5921 | } | |||
5922 | ||||
5923 | ScRefCellValue aCell(mrDoc, aAdr); | |||
5924 | switch (aCell.meType) | |||
5925 | { | |||
5926 | case CELLTYPE_VALUE : | |||
5927 | fVal = GetCellValue(aAdr, aCell); | |||
5928 | bIsString = false; | |||
5929 | break; | |||
5930 | case CELLTYPE_FORMULA : | |||
5931 | if (aCell.mpFormula->IsValue()) | |||
5932 | { | |||
5933 | fVal = GetCellValue(aAdr, aCell); | |||
5934 | bIsString = false; | |||
5935 | } | |||
5936 | else | |||
5937 | GetCellString(aString, aCell); | |||
5938 | break; | |||
5939 | case CELLTYPE_STRING : | |||
5940 | case CELLTYPE_EDIT : | |||
5941 | GetCellString(aString, aCell); | |||
5942 | break; | |||
5943 | default: | |||
5944 | fVal = 0.0; | |||
5945 | bIsString = false; | |||
5946 | } | |||
5947 | } | |||
5948 | break; | |||
5949 | case svString: | |||
5950 | aString = GetString(); | |||
5951 | break; | |||
5952 | case svMatrix : | |||
5953 | case svExternalDoubleRef: | |||
5954 | { | |||
5955 | ScMatValType nType = GetDoubleOrStringFromMatrix( fVal, aString); | |||
5956 | bIsString = ScMatrix::IsRealStringType( nType); | |||
5957 | } | |||
5958 | break; | |||
5959 | case svExternalSingleRef: | |||
5960 | { | |||
5961 | ScExternalRefCache::TokenRef pToken; | |||
5962 | PopExternalSingleRef(pToken); | |||
5963 | if (nGlobalError == FormulaError::NONE) | |||
5964 | { | |||
5965 | if (pToken->GetType() == svDouble) | |||
5966 | { | |||
5967 | fVal = pToken->GetDouble(); | |||
5968 | bIsString = false; | |||
5969 | } | |||
5970 | else | |||
5971 | aString = pToken->GetString(); | |||
5972 | } | |||
5973 | } | |||
5974 | break; | |||
5975 | default: | |||
5976 | { | |||
5977 | fVal = GetDouble(); | |||
5978 | bIsString = false; | |||
5979 | } | |||
5980 | } | |||
5981 | ||||
5982 | if (nGlobalError != FormulaError::NONE) | |||
5983 | { | |||
5984 | PushError( nGlobalError); | |||
5985 | return; // and bail out, no need to evaluate other arguments | |||
5986 | } | |||
5987 | ||||
5988 | // take range | |||
5989 | short nParam = nParamCount; | |||
5990 | size_t nRefInList = 0; | |||
5991 | size_t nRefArrayPos = std::numeric_limits<size_t>::max(); | |||
5992 | SCCOL nCol1 = 0; | |||
5993 | SCROW nRow1 = 0; | |||
5994 | SCTAB nTab1 = 0; | |||
5995 | SCCOL nCol2 = 0; | |||
5996 | SCROW nRow2 = 0; | |||
5997 | SCTAB nTab2 = 0; | |||
5998 | ScMatrixRef pQueryMatrix; | |||
5999 | while (nParam-- == nParamCount) | |||
6000 | { | |||
6001 | switch ( GetStackType() ) | |||
6002 | { | |||
6003 | case svRefList : | |||
6004 | { | |||
6005 | const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); | |||
6006 | if (p && p->IsArrayResult()) | |||
6007 | { | |||
6008 | if (nRefInList == 0) | |||
6009 | { | |||
6010 | if (vRefArrayConditions.empty()) | |||
6011 | vRefArrayConditions.resize( nRefArrayRows); | |||
6012 | if (!vConditions.empty()) | |||
6013 | { | |||
6014 | // Similar to other reference list array | |||
6015 | // handling, add/op the current value to | |||
6016 | // all array positions. | |||
6017 | for (auto & rVec : vRefArrayConditions) | |||
6018 | { | |||
6019 | if (rVec.empty()) | |||
6020 | rVec = vConditions; | |||
6021 | else | |||
6022 | { | |||
6023 | assert(rVec.size() == vConditions.size())(static_cast <bool> (rVec.size() == vConditions.size()) ? void (0) : __assert_fail ("rVec.size() == vConditions.size()" , "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" , 6023, __extension__ __PRETTY_FUNCTION__)); // see dimensions below | |||
6024 | for (size_t i=0, n = rVec.size(); i < n; ++i) | |||
6025 | { | |||
6026 | rVec[i] += vConditions[i]; | |||
6027 | } | |||
6028 | } | |||
6029 | } | |||
6030 | // Reset condition results. | |||
6031 | std::for_each( vConditions.begin(), vConditions.end(), | |||
6032 | [](sal_uInt32 & r){ r = 0.0; } ); | |||
6033 | } | |||
6034 | } | |||
6035 | nRefArrayPos = nRefInList; | |||
6036 | } | |||
6037 | ScRange aRange; | |||
6038 | PopDoubleRef( aRange, nParam, nRefInList); | |||
6039 | aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
6040 | } | |||
6041 | break; | |||
6042 | case svDoubleRef : | |||
6043 | PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); | |||
6044 | break; | |||
6045 | case svSingleRef : | |||
6046 | PopSingleRef( nCol1, nRow1, nTab1 ); | |||
6047 | nCol2 = nCol1; | |||
6048 | nRow2 = nRow1; | |||
6049 | nTab2 = nTab1; | |||
6050 | break; | |||
6051 | case svMatrix: | |||
6052 | case svExternalSingleRef: | |||
6053 | case svExternalDoubleRef: | |||
6054 | { | |||
6055 | pQueryMatrix = GetMatrix(); | |||
6056 | if (!pQueryMatrix) | |||
6057 | { | |||
6058 | PushError( FormulaError::IllegalParameter); | |||
6059 | return; | |||
6060 | } | |||
6061 | nCol1 = 0; | |||
6062 | nRow1 = 0; | |||
6063 | nTab1 = 0; | |||
6064 | SCSIZE nC, nR; | |||
6065 | pQueryMatrix->GetDimensions( nC, nR); | |||
6066 | nCol2 = static_cast<SCCOL>(nC - 1); | |||
6067 | nRow2 = static_cast<SCROW>(nR - 1); | |||
6068 | nTab2 = 0; | |||
6069 | } | |||
6070 | break; | |||
6071 | default: | |||
6072 | PushError( FormulaError::IllegalParameter); | |||
6073 | return; | |||
6074 | } | |||
6075 | if ( nTab1 != nTab2 ) | |||
6076 | { | |||
6077 | PushError( FormulaError::IllegalArgument); | |||
6078 | return; | |||
6079 | } | |||
6080 | ||||
6081 | if (bRangeReduce) | |||
6082 | { | |||
6083 | // All reference ranges must be of the same size as the main range. | |||
6084 | if( aMainRange.aEnd.Col() - aMainRange.aStart.Col() != nCol2 - nCol1 | |||
6085 | || aMainRange.aEnd.Row() - aMainRange.aStart.Row() != nRow2 - nRow1) | |||
6086 | { | |||
6087 | PushError ( FormulaError::IllegalArgument); | |||
6088 | return; | |||
6089 | } | |||
6090 | nCol1 += nStartColDiff; | |||
6091 | nRow1 += nStartRowDiff; | |||
6092 | ||||
6093 | nCol2 += nEndColDiff; | |||
6094 | nRow2 += nEndRowDiff; | |||
6095 | } | |||
6096 | ||||
6097 | // All reference ranges must be of same dimension and size. | |||
6098 | if (!nDimensionCols) | |||
6099 | nDimensionCols = nCol2 - nCol1 + 1; | |||
6100 | if (!nDimensionRows) | |||
6101 | nDimensionRows = nRow2 - nRow1 + 1; | |||
6102 | if ((nDimensionCols != (nCol2 - nCol1 + 1)) || (nDimensionRows != (nRow2 - nRow1 + 1))) | |||
6103 | { | |||
6104 | PushError ( FormulaError::IllegalArgument); | |||
6105 | return; | |||
6106 | } | |||
6107 | ||||
6108 | // recalculate matrix values | |||
6109 | if (nGlobalError != FormulaError::NONE) | |||
6110 | { | |||
6111 | PushError( nGlobalError); | |||
6112 | return; | |||
6113 | } | |||
6114 | ||||
6115 | // initialize temporary result matrix | |||
6116 | if (vConditions.empty()) | |||
6117 | vConditions.resize( nDimensionCols * nDimensionRows, 0); | |||
6118 | ||||
6119 | ScQueryParam rParam; | |||
6120 | rParam.nRow1 = nRow1; | |||
6121 | rParam.nRow2 = nRow2; | |||
6122 | ||||
6123 | ScQueryEntry& rEntry = rParam.GetEntry(0); | |||
6124 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
6125 | rEntry.bDoQuery = true; | |||
6126 | if (!bIsString) | |||
6127 | { | |||
6128 | rItem.meType = ScQueryEntry::ByValue; | |||
6129 | rItem.mfVal = fVal; | |||
6130 | rEntry.eOp = SC_EQUAL; | |||
6131 | } | |||
6132 | else | |||
6133 | { | |||
6134 | rParam.FillInExcelSyntax(mrDoc.GetSharedStringPool(), aString.getString(), 0, pFormatter); | |||
6135 | if (rItem.meType == ScQueryEntry::ByString) | |||
6136 | rParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); | |||
6137 | } | |||
6138 | rParam.nCol1 = nCol1; | |||
6139 | rParam.nCol2 = nCol2; | |||
6140 | rEntry.nField = nCol1; | |||
6141 | SCCOL nColDiff = -nCol1; | |||
6142 | SCROW nRowDiff = -nRow1; | |||
6143 | if (pQueryMatrix) | |||
6144 | { | |||
6145 | // Never case-sensitive. | |||
6146 | sc::CompareOptions aOptions(mrDoc, rEntry, rParam.eSearchType); | |||
6147 | ScMatrixRef pResultMatrix = QueryMat( pQueryMatrix, aOptions); | |||
6148 | if (nGlobalError != FormulaError::NONE || !pResultMatrix) | |||
6149 | { | |||
6150 | PushError( FormulaError::IllegalParameter); | |||
6151 | return; | |||
6152 | } | |||
6153 | ||||
6154 | // result matrix is filled with boolean values. | |||
6155 | std::vector<double> aResValues; | |||
6156 | pResultMatrix->GetDoubleArray(aResValues); | |||
6157 | if (vConditions.size() != aResValues.size()) | |||
6158 | { | |||
6159 | PushError( FormulaError::IllegalParameter); | |||
6160 | return; | |||
6161 | } | |||
6162 | ||||
6163 | std::vector<double>::const_iterator itThisRes = aResValues.begin(); | |||
6164 | for (auto& rCondition : vConditions) | |||
6165 | { | |||
6166 | rCondition += *itThisRes; | |||
6167 | ++itThisRes; | |||
6168 | } | |||
6169 | } | |||
6170 | else | |||
6171 | { | |||
6172 | ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, rParam, false); | |||
6173 | // Increment Entry.nField in iterator when switching to next column. | |||
6174 | aCellIter.SetAdvanceQueryParamEntryField( true ); | |||
6175 | if ( aCellIter.GetFirst() ) | |||
6176 | { | |||
6177 | do | |||
6178 | { | |||
6179 | size_t nC = aCellIter.GetCol() + nColDiff; | |||
6180 | size_t nR = aCellIter.GetRow() + nRowDiff; | |||
6181 | ++vConditions[nC * nDimensionRows + nR]; | |||
6182 | } while ( aCellIter.GetNext() ); | |||
6183 | } | |||
6184 | } | |||
6185 | if (nRefArrayPos != std::numeric_limits<size_t>::max()) | |||
6186 | { | |||
6187 | // Apply condition result to reference list array result position. | |||
6188 | std::vector<sal_uInt32>& rVec = vRefArrayConditions[nRefArrayPos]; | |||
6189 | if (rVec.empty()) | |||
6190 | rVec = vConditions; | |||
6191 | else | |||
6192 | { | |||
6193 | assert(rVec.size() == vConditions.size())(static_cast <bool> (rVec.size() == vConditions.size()) ? void (0) : __assert_fail ("rVec.size() == vConditions.size()" , "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" , 6193, __extension__ __PRETTY_FUNCTION__)); // see dimensions above | |||
6194 | for (size_t i=0, n = rVec.size(); i < n; ++i) | |||
6195 | { | |||
6196 | rVec[i] += vConditions[i]; | |||
6197 | } | |||
6198 | } | |||
6199 | // Reset conditions vector. | |||
6200 | // When leaving an svRefList this has to be emptied not set to | |||
6201 | // 0.0 because it's checked when entering an svRefList. | |||
6202 | if (nRefInList == 0) | |||
6203 | std::vector<sal_uInt32>().swap( vConditions); | |||
6204 | else | |||
6205 | std::for_each( vConditions.begin(), vConditions.end(), [](sal_uInt32 & r){ r = 0.0; } ); | |||
6206 | } | |||
6207 | } | |||
6208 | nParamCount -= 2; | |||
6209 | } | |||
6210 | ||||
6211 | if (!vRefArrayConditions.empty() && !vConditions.empty()) | |||
6212 | { | |||
6213 | // Add/op the last current value to all array positions. | |||
6214 | for (auto & rVec : vRefArrayConditions) | |||
6215 | { | |||
6216 | if (rVec.empty()) | |||
6217 | rVec = vConditions; | |||
6218 | else | |||
6219 | { | |||
6220 | assert(rVec.size() == vConditions.size())(static_cast <bool> (rVec.size() == vConditions.size()) ? void (0) : __assert_fail ("rVec.size() == vConditions.size()" , "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" , 6220, __extension__ __PRETTY_FUNCTION__)); // see dimensions above | |||
6221 | for (size_t i=0, n = rVec.size(); i < n; ++i) | |||
6222 | { | |||
6223 | rVec[i] += vConditions[i]; | |||
6224 | } | |||
6225 | } | |||
6226 | } | |||
6227 | } | |||
6228 | ||||
6229 | if (nGlobalError != FormulaError::NONE) | |||
6230 | { | |||
6231 | PushError( nGlobalError); | |||
6232 | return; // bail out | |||
6233 | } | |||
6234 | ||||
6235 | sc::ParamIfsResult aRes; | |||
6236 | ScMatrixRef xResMat; | |||
6237 | ||||
6238 | // main range - only for AVERAGEIFS, SUMIFS, MINIFS and MAXIFS | |||
6239 | if (nParamCount == 1) | |||
6240 | { | |||
6241 | short nParam = nParamCount; | |||
6242 | size_t nRefInList = 0; | |||
6243 | size_t nRefArrayPos = std::numeric_limits<size_t>::max(); | |||
6244 | bool bRefArrayMain = false; | |||
6245 | while (nParam-- == nParamCount) | |||
6246 | { | |||
6247 | bool bNull = true; | |||
6248 | SCCOL nMainCol1 = 0; | |||
6249 | SCROW nMainRow1 = 0; | |||
6250 | SCTAB nMainTab1 = 0; | |||
6251 | SCCOL nMainCol2 = 0; | |||
6252 | SCROW nMainRow2 = 0; | |||
6253 | SCTAB nMainTab2 = 0; | |||
6254 | ScMatrixRef pMainMatrix; | |||
6255 | switch ( GetStackType() ) | |||
6256 | { | |||
6257 | case svRefList : | |||
6258 | { | |||
6259 | const ScRefListToken* p = dynamic_cast<const ScRefListToken*>(pStack[sp-1]); | |||
6260 | if (p && p->IsArrayResult()) | |||
6261 | { | |||
6262 | if (vRefArrayConditions.empty()) | |||
6263 | { | |||
6264 | // Replicate conditions if there wasn't a | |||
6265 | // reference list array for criteria | |||
6266 | // evaluation. | |||
6267 | vRefArrayConditions.resize( nRefArrayRows); | |||
6268 | for (auto & rVec : vRefArrayConditions) | |||
6269 | { | |||
6270 | rVec = vConditions; | |||
6271 | } | |||
6272 | } | |||
6273 | ||||
6274 | bRefArrayMain = true; | |||
6275 | nRefArrayPos = nRefInList; | |||
6276 | } | |||
6277 | ScRange aRange; | |||
6278 | PopDoubleRef( aRange, nParam, nRefInList); | |||
6279 | aRange.GetVars( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2); | |||
6280 | } | |||
6281 | break; | |||
6282 | case svDoubleRef : | |||
6283 | PopDoubleRef( nMainCol1, nMainRow1, nMainTab1, nMainCol2, nMainRow2, nMainTab2 ); | |||
6284 | break; | |||
6285 | case svSingleRef : | |||
6286 | PopSingleRef( nMainCol1, nMainRow1, nMainTab1 ); | |||
6287 | nMainCol2 = nMainCol1; | |||
6288 | nMainRow2 = nMainRow1; | |||
6289 | nMainTab2 = nMainTab1; | |||
6290 | break; | |||
6291 | case svMatrix: | |||
6292 | case svExternalSingleRef: | |||
6293 | case svExternalDoubleRef: | |||
6294 | { | |||
6295 | pMainMatrix = GetMatrix(); | |||
6296 | if (!pMainMatrix) | |||
6297 | { | |||
6298 | PushError( FormulaError::IllegalParameter); | |||
6299 | return; | |||
6300 | } | |||
6301 | nMainCol1 = 0; | |||
6302 | nMainRow1 = 0; | |||
6303 | nMainTab1 = 0; | |||
6304 | SCSIZE nC, nR; | |||
6305 | pMainMatrix->GetDimensions( nC, nR); | |||
6306 | nMainCol2 = static_cast<SCCOL>(nC - 1); | |||
6307 | nMainRow2 = static_cast<SCROW>(nR - 1); | |||
6308 | nMainTab2 = 0; | |||
6309 | } | |||
6310 | break; | |||
6311 | // Treat a scalar value as 1x1 matrix. | |||
6312 | case svDouble: | |||
6313 | pMainMatrix = GetNewMat(1,1); | |||
6314 | nMainCol1 = nMainCol2 = 0; | |||
6315 | nMainRow1 = nMainRow2 = 0; | |||
6316 | nMainTab1 = nMainTab2 = 0; | |||
6317 | pMainMatrix->PutDouble( GetDouble(), 0, 0); | |||
6318 | break; | |||
6319 | case svString: | |||
6320 | pMainMatrix = GetNewMat(1,1); | |||
6321 | nMainCol1 = nMainCol2 = 0; | |||
6322 | nMainRow1 = nMainRow2 = 0; | |||
6323 | nMainTab1 = nMainTab2 = 0; | |||
6324 | pMainMatrix->PutString( GetString(), 0, 0); | |||
6325 | break; | |||
6326 | default: | |||
6327 | PopError(); | |||
6328 | PushError( FormulaError::IllegalParameter); | |||
6329 | return; | |||
6330 | } | |||
6331 | if ( nMainTab1 != nMainTab2 ) | |||
6332 | { | |||
6333 | PushError( FormulaError::IllegalArgument); | |||
6334 | return; | |||
6335 | } | |||
6336 | ||||
6337 | if (bRangeReduce) | |||
6338 | { | |||
6339 | nMainCol1 += nStartColDiff; | |||
6340 | nMainRow1 += nStartRowDiff; | |||
6341 | ||||
6342 | nMainCol2 += nEndColDiff; | |||
6343 | nMainRow2 += nEndRowDiff; | |||
6344 | } | |||
6345 | ||||
6346 | // All reference ranges must be of same dimension and size. | |||
6347 | if ((nDimensionCols != (nMainCol2 - nMainCol1 + 1)) || (nDimensionRows != (nMainRow2 - nMainRow1 + 1))) | |||
6348 | { | |||
6349 | PushError ( FormulaError::IllegalArgument); | |||
6350 | return; | |||
6351 | } | |||
6352 | ||||
6353 | if (nGlobalError != FormulaError::NONE) | |||
6354 | { | |||
6355 | PushError( nGlobalError); | |||
6356 | return; // bail out | |||
6357 | } | |||
6358 | ||||
6359 | // end-result calculation | |||
6360 | ||||
6361 | // This gets weird... if conditions were calculated using a | |||
6362 | // reference list array but the main calculation range is not a | |||
6363 | // reference list array, then the conditions of the array are | |||
6364 | // applied to the main range each in turn to form the array result. | |||
6365 | ||||
6366 | size_t nRefArrayMainPos = (bRefArrayMain ? nRefArrayPos : | |||
6367 | (vRefArrayConditions.empty() ? std::numeric_limits<size_t>::max() : 0)); | |||
6368 | const bool bAppliedArray = (!bRefArrayMain && nRefArrayMainPos == 0); | |||
6369 | ||||
6370 | if (nRefArrayMainPos == 0) | |||
6371 | xResMat = GetNewMat( 1, nRefArrayRows); | |||
6372 | ||||
6373 | if (pMainMatrix) | |||
6374 | { | |||
6375 | std::vector<double> aMainValues; | |||
6376 | pMainMatrix->GetDoubleArray(aMainValues, false); // Map empty values to NaN's. | |||
6377 | ||||
6378 | do | |||
6379 | { | |||
6380 | if (nRefArrayMainPos < vRefArrayConditions.size()) | |||
6381 | vConditions = vRefArrayConditions[nRefArrayMainPos]; | |||
6382 | ||||
6383 | if (vConditions.size() != aMainValues.size()) | |||
6384 | { | |||
6385 | PushError( FormulaError::IllegalArgument); | |||
6386 | return; | |||
6387 | } | |||
6388 | ||||
6389 | std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin(), itResEnd = vConditions.end(); | |||
6390 | std::vector<double>::const_iterator itMain = aMainValues.begin(); | |||
6391 | for (; itRes != itResEnd; ++itRes, ++itMain) | |||
6392 | { | |||
6393 | if (*itRes != nQueryCount) | |||
6394 | continue; | |||
6395 | ||||
6396 | fVal = *itMain; | |||
6397 | if (GetDoubleErrorValue(fVal) == FormulaError::ElementNaN) | |||
6398 | continue; | |||
6399 | ||||
6400 | ++aRes.mfCount; | |||
6401 | if (bNull && fVal != 0.0) | |||
6402 | { | |||
6403 | bNull = false; | |||
6404 | aRes.mfMem = fVal; | |||
6405 | } | |||
6406 | else | |||
6407 | aRes.mfSum += fVal; | |||
6408 | if ( aRes.mfMin > fVal ) | |||
6409 | aRes.mfMin = fVal; | |||
6410 | if ( aRes.mfMax < fVal ) | |||
6411 | aRes.mfMax = fVal; | |||
6412 | } | |||
6413 | if (nRefArrayMainPos != std::numeric_limits<size_t>::max()) | |||
6414 | { | |||
6415 | xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos); | |||
6416 | aRes = sc::ParamIfsResult(); | |||
6417 | } | |||
6418 | } | |||
6419 | while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows); | |||
6420 | } | |||
6421 | else | |||
6422 | { | |||
6423 | ScAddress aAdr; | |||
6424 | aAdr.SetTab( nMainTab1 ); | |||
6425 | do | |||
6426 | { | |||
6427 | if (nRefArrayMainPos < vRefArrayConditions.size()) | |||
6428 | vConditions = vRefArrayConditions[nRefArrayMainPos]; | |||
6429 | ||||
6430 | std::vector<sal_uInt32>::const_iterator itRes = vConditions.begin(); | |||
6431 | for (SCCOL nCol = 0; nCol < nDimensionCols; ++nCol) | |||
6432 | { | |||
6433 | for (SCROW nRow = 0; nRow < nDimensionRows; ++nRow, ++itRes) | |||
6434 | { | |||
6435 | if (*itRes == nQueryCount) | |||
6436 | { | |||
6437 | aAdr.SetCol( nCol + nMainCol1); | |||
6438 | aAdr.SetRow( nRow + nMainRow1); | |||
6439 | ScRefCellValue aCell(mrDoc, aAdr); | |||
6440 | if (aCell.hasNumeric()) | |||
6441 | { | |||
6442 | fVal = GetCellValue(aAdr, aCell); | |||
6443 | ++aRes.mfCount; | |||
6444 | if ( bNull && fVal != 0.0 ) | |||
6445 | { | |||
6446 | bNull = false; | |||
6447 | aRes.mfMem = fVal; | |||
6448 | } | |||
6449 | else | |||
6450 | aRes.mfSum += fVal; | |||
6451 | if ( aRes.mfMin > fVal ) | |||
6452 | aRes.mfMin = fVal; | |||
6453 | if ( aRes.mfMax < fVal ) | |||
6454 | aRes.mfMax = fVal; | |||
6455 | } | |||
6456 | } | |||
6457 | } | |||
6458 | } | |||
6459 | if (nRefArrayMainPos != std::numeric_limits<size_t>::max()) | |||
6460 | { | |||
6461 | xResMat->PutDouble( ResultFunc( aRes), 0, nRefArrayMainPos); | |||
6462 | aRes = sc::ParamIfsResult(); | |||
6463 | } | |||
6464 | } | |||
6465 | while (bAppliedArray && ++nRefArrayMainPos < nRefArrayRows); | |||
6466 | } | |||
6467 | } | |||
6468 | } | |||
6469 | else | |||
6470 | { | |||
6471 | // COUNTIFS only. | |||
6472 | if (vRefArrayConditions.empty()) | |||
6473 | { | |||
6474 | for (auto const & rCond : vConditions) | |||
6475 | { | |||
6476 | if (rCond == nQueryCount) | |||
6477 | ++aRes.mfCount; | |||
6478 | } | |||
6479 | } | |||
6480 | else | |||
6481 | { | |||
6482 | xResMat = GetNewMat( 1, nRefArrayRows); | |||
6483 | for (size_t i=0, n = vRefArrayConditions.size(); i < n; ++i) | |||
6484 | { | |||
6485 | double fCount = 0.0; | |||
6486 | for (auto const & rCond : vRefArrayConditions[i]) | |||
6487 | { | |||
6488 | if (rCond == nQueryCount) | |||
6489 | ++fCount; | |||
6490 | } | |||
6491 | if (fCount) | |||
6492 | xResMat->PutDouble( fCount, 0, i); | |||
6493 | } | |||
6494 | } | |||
6495 | } | |||
6496 | ||||
6497 | if (xResMat) | |||
6498 | PushMatrix( xResMat); | |||
6499 | else | |||
6500 | PushDouble( ResultFunc( aRes)); | |||
6501 | } | |||
6502 | ||||
6503 | void ScInterpreter::ScSumIfs() | |||
6504 | { | |||
6505 | // ScMutationGuard aShouldFail(pDok, ScMutationGuardFlags::CORE); | |||
6506 | sal_uInt8 nParamCount = GetByte(); | |||
6507 | ||||
6508 | if (nParamCount < 3 || (nParamCount % 2 != 1)) | |||
6509 | { | |||
6510 | PushError( FormulaError::ParameterExpected); | |||
6511 | return; | |||
6512 | } | |||
6513 | ||||
6514 | auto ResultFunc = []( const sc::ParamIfsResult& rRes ) | |||
6515 | { | |||
6516 | return rtl::math::approxAdd(rRes.mfSum, rRes.mfMem); | |||
6517 | }; | |||
6518 | IterateParametersIfs(ResultFunc); | |||
6519 | } | |||
6520 | ||||
6521 | void ScInterpreter::ScAverageIfs() | |||
6522 | { | |||
6523 | sal_uInt8 nParamCount = GetByte(); | |||
6524 | ||||
6525 | if (nParamCount < 3 || (nParamCount % 2 != 1)) | |||
6526 | { | |||
6527 | PushError( FormulaError::ParameterExpected); | |||
6528 | return; | |||
6529 | } | |||
6530 | ||||
6531 | auto ResultFunc = []( const sc::ParamIfsResult& rRes ) | |||
6532 | { | |||
6533 | return sc::div( rtl::math::approxAdd( rRes.mfSum, rRes.mfMem), rRes.mfCount); | |||
6534 | }; | |||
6535 | IterateParametersIfs(ResultFunc); | |||
6536 | } | |||
6537 | ||||
6538 | void ScInterpreter::ScCountIfs() | |||
6539 | { | |||
6540 | sal_uInt8 nParamCount = GetByte(); | |||
6541 | ||||
6542 | if (nParamCount < 2 || (nParamCount % 2 != 0)) | |||
6543 | { | |||
6544 | PushError( FormulaError::ParameterExpected); | |||
6545 | return; | |||
6546 | } | |||
6547 | ||||
6548 | auto ResultFunc = []( const sc::ParamIfsResult& rRes ) | |||
6549 | { | |||
6550 | return rRes.mfCount; | |||
6551 | }; | |||
6552 | IterateParametersIfs(ResultFunc); | |||
6553 | } | |||
6554 | ||||
6555 | void ScInterpreter::ScMinIfs_MS() | |||
6556 | { | |||
6557 | sal_uInt8 nParamCount = GetByte(); | |||
6558 | ||||
6559 | if (nParamCount < 3 || (nParamCount % 2 != 1)) | |||
6560 | { | |||
6561 | PushError( FormulaError::ParameterExpected); | |||
6562 | return; | |||
6563 | } | |||
6564 | ||||
6565 | auto ResultFunc = []( const sc::ParamIfsResult& rRes ) | |||
6566 | { | |||
6567 | return (rRes.mfMin < std::numeric_limits<double>::max()) ? rRes.mfMin : 0.0; | |||
6568 | }; | |||
6569 | IterateParametersIfs(ResultFunc); | |||
6570 | } | |||
6571 | ||||
6572 | ||||
6573 | void ScInterpreter::ScMaxIfs_MS() | |||
6574 | { | |||
6575 | sal_uInt8 nParamCount = GetByte(); | |||
6576 | ||||
6577 | if (nParamCount < 3 || (nParamCount % 2 != 1)) | |||
6578 | { | |||
6579 | PushError( FormulaError::ParameterExpected); | |||
6580 | return; | |||
6581 | } | |||
6582 | ||||
6583 | auto ResultFunc = []( const sc::ParamIfsResult& rRes ) | |||
6584 | { | |||
6585 | return (rRes.mfMax > std::numeric_limits<double>::lowest()) ? rRes.mfMax : 0.0; | |||
6586 | }; | |||
6587 | IterateParametersIfs(ResultFunc); | |||
6588 | } | |||
6589 | ||||
6590 | void ScInterpreter::ScLookup() | |||
6591 | { | |||
6592 | sal_uInt8 nParamCount = GetByte(); | |||
6593 | if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) | |||
6594 | return ; | |||
6595 | ||||
6596 | ScMatrixRef pDataMat = nullptr, pResMat = nullptr; | |||
6597 | SCCOL nCol1 = 0, nCol2 = 0, nResCol1 = 0, nResCol2 = 0; | |||
6598 | SCROW nRow1 = 0, nRow2 = 0, nResRow1 = 0, nResRow2 = 0; | |||
6599 | SCTAB nTab1 = 0, nResTab = 0; | |||
6600 | SCSIZE nLenMajor = 0; // length of major direction | |||
6601 | bool bVertical = true; // whether to lookup vertically or horizontally | |||
6602 | ||||
6603 | // The third parameter, result array, for double, string and single reference. | |||
6604 | double fResVal = 0.0; | |||
6605 | svl::SharedString aResStr; | |||
6606 | ScAddress aResAdr; | |||
6607 | StackVar eResArrayType = svUnknown; | |||
6608 | ||||
6609 | if (nParamCount == 3) | |||
6610 | { | |||
6611 | eResArrayType = GetStackType(); | |||
6612 | switch (eResArrayType) | |||
6613 | { | |||
6614 | case svDoubleRef: | |||
6615 | { | |||
6616 | SCTAB nTabJunk; | |||
6617 | PopDoubleRef(nResCol1, nResRow1, nResTab, | |||
6618 | nResCol2, nResRow2, nTabJunk); | |||
6619 | if (nResTab != nTabJunk || | |||
6620 | ((nResRow2 - nResRow1) > 0 && (nResCol2 - nResCol1) > 0)) | |||
6621 | { | |||
6622 | // The result array must be a vector. | |||
6623 | PushIllegalParameter(); | |||
6624 | return; | |||
6625 | } | |||
6626 | } | |||
6627 | break; | |||
6628 | case svMatrix: | |||
6629 | case svExternalSingleRef: | |||
6630 | case svExternalDoubleRef: | |||
6631 | { | |||
6632 | pResMat = GetMatrix(); | |||
6633 | if (!pResMat) | |||
6634 | { | |||
6635 | PushIllegalParameter(); | |||
6636 | return; | |||
6637 | } | |||
6638 | SCSIZE nC, nR; | |||
6639 | pResMat->GetDimensions(nC, nR); | |||
6640 | if (nC != 1 && nR != 1) | |||
6641 | { | |||
6642 | // Result matrix must be a vector. | |||
6643 | PushIllegalParameter(); | |||
6644 | return; | |||
6645 | } | |||
6646 | } | |||
6647 | break; | |||
6648 | case svDouble: | |||
6649 | fResVal = GetDouble(); | |||
6650 | break; | |||
6651 | case svString: | |||
6652 | aResStr = GetString(); | |||
6653 | break; | |||
6654 | case svSingleRef: | |||
6655 | PopSingleRef( aResAdr ); | |||
6656 | break; | |||
6657 | default: | |||
6658 | PushIllegalParameter(); | |||
6659 | return; | |||
6660 | } | |||
6661 | } | |||
6662 | ||||
6663 | // For double, string and single reference. | |||
6664 | double fDataVal = 0.0; | |||
6665 | svl::SharedString aDataStr; | |||
6666 | ScAddress aDataAdr; | |||
6667 | bool bValueData = false; | |||
6668 | ||||
6669 | // Get the data-result range and also determine whether this is vertical | |||
6670 | // lookup or horizontal lookup. | |||
6671 | ||||
6672 | StackVar eDataArrayType = GetStackType(); | |||
6673 | switch (eDataArrayType) | |||
6674 | { | |||
6675 | case svDoubleRef: | |||
6676 | { | |||
6677 | SCTAB nTabJunk; | |||
6678 | PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTabJunk); | |||
6679 | if (nTab1 != nTabJunk) | |||
6680 | { | |||
6681 | PushIllegalParameter(); | |||
6682 | return; | |||
6683 | } | |||
6684 | bVertical = (nRow2 - nRow1) >= (nCol2 - nCol1); | |||
6685 | nLenMajor = bVertical ? nRow2 - nRow1 + 1 : nCol2 - nCol1 + 1; | |||
6686 | } | |||
6687 | break; | |||
6688 | case svMatrix: | |||
6689 | case svExternalSingleRef: | |||
6690 | case svExternalDoubleRef: | |||
6691 | { | |||
6692 | pDataMat = GetMatrix(); | |||
6693 | if (!pDataMat) | |||
6694 | { | |||
6695 | PushIllegalParameter(); | |||
6696 | return; | |||
6697 | } | |||
6698 | ||||
6699 | SCSIZE nC, nR; | |||
6700 | pDataMat->GetDimensions(nC, nR); | |||
6701 | bVertical = (nR >= nC); | |||
6702 | nLenMajor = bVertical ? nR : nC; | |||
6703 | } | |||
6704 | break; | |||
6705 | case svDouble: | |||
6706 | { | |||
6707 | fDataVal = GetDouble(); | |||
6708 | bValueData = true; | |||
6709 | } | |||
6710 | break; | |||
6711 | case svString: | |||
6712 | { | |||
6713 | aDataStr = GetString(); | |||
6714 | } | |||
6715 | break; | |||
6716 | case svSingleRef: | |||
6717 | { | |||
6718 | PopSingleRef( aDataAdr ); | |||
6719 | ScRefCellValue aCell(mrDoc, aDataAdr); | |||
6720 | if (aCell.hasEmptyValue()) | |||
6721 | { | |||
6722 | // Empty cells aren't found anywhere, bail out early. | |||
6723 | SetError( FormulaError::NotAvailable); | |||
6724 | } | |||
6725 | else if (aCell.hasNumeric()) | |||
6726 | { | |||
6727 | fDataVal = GetCellValue(aDataAdr, aCell); | |||
6728 | bValueData = true; | |||
6729 | } | |||
6730 | else | |||
6731 | GetCellString(aDataStr, aCell); | |||
6732 | } | |||
6733 | break; | |||
6734 | default: | |||
6735 | SetError( FormulaError::IllegalParameter); | |||
6736 | } | |||
6737 | ||||
6738 | if (nGlobalError != FormulaError::NONE) | |||
6739 | { | |||
6740 | PushError( nGlobalError); | |||
6741 | return; | |||
6742 | } | |||
6743 | ||||
6744 | // Get the lookup value. | |||
6745 | ||||
6746 | ScQueryParam aParam; | |||
6747 | ScQueryEntry& rEntry = aParam.GetEntry(0); | |||
6748 | if ( !FillEntry(rEntry) ) | |||
6749 | return; | |||
6750 | ||||
6751 | if ( eDataArrayType == svDouble || eDataArrayType == svString || | |||
6752 | eDataArrayType == svSingleRef ) | |||
6753 | { | |||
6754 | // Delta position for a single value is always 0. | |||
6755 | ||||
6756 | // Found if data <= query, but not if query is string and found data is | |||
6757 | // numeric or vice versa. This is how Excel does it but doesn't | |||
6758 | // document it. | |||
6759 | ||||
6760 | bool bFound = false; | |||
6761 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
6762 | ||||
6763 | if ( bValueData ) | |||
6764 | { | |||
6765 | if (rItem.meType == ScQueryEntry::ByString) | |||
6766 | bFound = false; | |||
6767 | else | |||
6768 | bFound = (fDataVal <= rItem.mfVal); | |||
6769 | } | |||
6770 | else | |||
6771 | { | |||
6772 | if (rItem.meType != ScQueryEntry::ByString) | |||
6773 | bFound = false; | |||
6774 | else | |||
6775 | bFound = (ScGlobal::GetCollator()->compareString(aDataStr.getString(), rItem.maString.getString()) <= 0); | |||
6776 | } | |||
6777 | ||||
6778 | if (!bFound) | |||
6779 | { | |||
6780 | PushNA(); | |||
6781 | return; | |||
6782 | } | |||
6783 | ||||
6784 | if (pResMat) | |||
6785 | { | |||
6786 | if (pResMat->IsValue( 0, 0 )) | |||
6787 | PushDouble(pResMat->GetDouble( 0, 0 )); | |||
6788 | else | |||
6789 | PushString(pResMat->GetString(0, 0)); | |||
6790 | } | |||
6791 | else if (nParamCount == 3) | |||
6792 | { | |||
6793 | switch (eResArrayType) | |||
6794 | { | |||
6795 | case svDouble: | |||
6796 | PushDouble( fResVal ); | |||
6797 | break; | |||
6798 | case svString: | |||
6799 | PushString( aResStr ); | |||
6800 | break; | |||
6801 | case svDoubleRef: | |||
6802 | aResAdr.Set( nResCol1, nResRow1, nResTab); | |||
6803 | [[fallthrough]]; | |||
6804 | case svSingleRef: | |||
6805 | PushCellResultToken( true, aResAdr, nullptr, nullptr); | |||
6806 | break; | |||
6807 | default: | |||
6808 | OSL_FAIL( "ScInterpreter::ScLookup: unhandled eResArrayType, single value data")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "6808" ": "), "%s", "ScInterpreter::ScLookup: unhandled eResArrayType, single value data" ); } } while (false); | |||
6809 | } | |||
6810 | } | |||
6811 | else | |||
6812 | { | |||
6813 | switch (eDataArrayType) | |||
6814 | { | |||
6815 | case svDouble: | |||
6816 | PushDouble( fDataVal ); | |||
6817 | break; | |||
6818 | case svString: | |||
6819 | PushString( aDataStr ); | |||
6820 | break; | |||
6821 | case svSingleRef: | |||
6822 | PushCellResultToken( true, aDataAdr, nullptr, nullptr); | |||
6823 | break; | |||
6824 | default: | |||
6825 | OSL_FAIL( "ScInterpreter::ScLookup: unhandled eDataArrayType, single value data")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "6825" ": "), "%s", "ScInterpreter::ScLookup: unhandled eDataArrayType, single value data" ); } } while (false); | |||
6826 | } | |||
6827 | } | |||
6828 | return; | |||
6829 | } | |||
6830 | ||||
6831 | // Now, perform the search to compute the delta position (nDelta). | |||
6832 | ||||
6833 | if (pDataMat) | |||
6834 | { | |||
6835 | // Data array is given as a matrix. | |||
6836 | rEntry.bDoQuery = true; | |||
6837 | rEntry.eOp = SC_LESS_EQUAL; | |||
6838 | bool bFound = false; | |||
6839 | ||||
6840 | SCSIZE nC, nR; | |||
6841 | pDataMat->GetDimensions(nC, nR); | |||
6842 | ||||
6843 | // Do not propagate errors from matrix while copying to vector. | |||
6844 | pDataMat->SetErrorInterpreter( nullptr); | |||
6845 | ||||
6846 | // Excel has an undocumented behaviour in that it seems to internally | |||
6847 | // sort an interim array (i.e. error values specifically #DIV/0! are | |||
6848 | // sorted to the end) or ignore error values that makes these "get last | |||
6849 | // non-empty" searches work, e.g. =LOOKUP(2,1/NOT(ISBLANK(A:A)),A:A) | |||
6850 | // see tdf#117016 | |||
6851 | // Instead of sorting a million entries of which mostly only a bunch of | |||
6852 | // rows are filled and moving error values to the end which most are | |||
6853 | // already anyway, assume the matrix to be sorted except error values | |||
6854 | // and omit the coded DoubleError values. | |||
6855 | // Do this only for a numeric matrix (that includes errors coded as | |||
6856 | // doubles), which covers the case in question. | |||
6857 | /* TODO: it's unclear whether this really matches Excel behaviour in | |||
6858 | * all constellations or if there are cases that include unsorted error | |||
6859 | * values and thus yield arbitrary binary search results or something | |||
6860 | * different or whether there are cases where error values are also | |||
6861 | * omitted from mixed numeric/string arrays or if it's not an interim | |||
6862 | * matrix but a cell range reference instead. */ | |||
6863 | const bool bOmitErrorValues = (eDataArrayType == svMatrix && pDataMat->IsNumeric()); | |||
6864 | ||||
6865 | // In case of non-vector matrix, only search the first row or column. | |||
6866 | ScMatrixRef pDataMat2; | |||
6867 | std::vector<SCCOLROW> vIndex; | |||
6868 | if (bOmitErrorValues) | |||
6869 | { | |||
6870 | std::vector<double> vArray; | |||
6871 | VectorMatrixAccessor aMatAcc(*pDataMat, bVertical); | |||
6872 | const SCSIZE nElements = aMatAcc.GetElementCount(); | |||
6873 | for (SCSIZE i=0; i < nElements; ++i) | |||
6874 | { | |||
6875 | const double fVal = aMatAcc.GetDouble(i); | |||
6876 | if (std::isfinite(fVal)) | |||
6877 | { | |||
6878 | vArray.push_back(fVal); | |||
6879 | vIndex.push_back(i); | |||
6880 | } | |||
6881 | } | |||
6882 | if (vArray.empty()) | |||
6883 | { | |||
6884 | PushNA(); | |||
6885 | return; | |||
6886 | } | |||
6887 | const size_t nElems = vArray.size(); | |||
6888 | if (nElems == nElements) | |||
6889 | { | |||
6890 | // No error value omitted, use as is. | |||
6891 | pDataMat2 = pDataMat; | |||
6892 | std::vector<SCCOLROW>().swap( vIndex); | |||
6893 | } | |||
6894 | else | |||
6895 | { | |||
6896 | nLenMajor = nElems; | |||
6897 | if (bVertical) | |||
6898 | { | |||
6899 | ScMatrixRef pTempMat = GetNewMat( 1, nElems); | |||
6900 | pTempMat->PutDoubleVector( vArray, 0, 0); | |||
6901 | pDataMat2 = pTempMat; | |||
6902 | } | |||
6903 | else | |||
6904 | { | |||
6905 | ScMatrixRef pTempMat = GetNewMat( nElems, 1); | |||
6906 | for (size_t i=0; i < nElems; ++i) | |||
6907 | pTempMat->PutDouble( vArray[i], i, 0); | |||
6908 | pDataMat2 = pTempMat; | |||
6909 | } | |||
6910 | } | |||
6911 | } | |||
6912 | else | |||
6913 | { | |||
6914 | // Just use as is with the VectorMatrixAccessor. | |||
6915 | pDataMat2 = pDataMat; | |||
6916 | } | |||
6917 | ||||
6918 | // Do not propagate errors from matrix while searching. | |||
6919 | pDataMat2->SetErrorInterpreter( nullptr); | |||
6920 | ||||
6921 | VectorMatrixAccessor aMatAcc2(*pDataMat2, bVertical); | |||
6922 | ||||
6923 | // binary search for non-equality mode (the source data is | |||
6924 | // assumed to be sorted in ascending order). | |||
6925 | ||||
6926 | SCCOLROW nDelta = -1; | |||
6927 | ||||
6928 | SCSIZE nFirst = 0, nLast = nLenMajor-1; //, nHitIndex = 0; | |||
6929 | for (SCSIZE nLen = nLast-nFirst; nLen > 0; nLen = nLast-nFirst) | |||
6930 | { | |||
6931 | SCSIZE nMid = nFirst + nLen/2; | |||
6932 | sal_Int32 nCmp = lcl_CompareMatrix2Query( nMid, aMatAcc2, rEntry); | |||
6933 | if (nCmp == 0) | |||
6934 | { | |||
6935 | // exact match. find the last item with the same value. | |||
6936 | lcl_GetLastMatch( nMid, aMatAcc2, nLenMajor, false); | |||
6937 | nDelta = nMid; | |||
6938 | bFound = true; | |||
6939 | break; | |||
6940 | } | |||
6941 | ||||
6942 | if (nLen == 1) // first and last items are next to each other. | |||
6943 | { | |||
6944 | nDelta = nCmp < 0 ? nLast - 1 : nFirst - 1; | |||
6945 | // If already the 1st item is greater there's nothing found. | |||
6946 | bFound = (nDelta >= 0); | |||
6947 | break; | |||
6948 | } | |||
6949 | ||||
6950 | if (nCmp < 0) | |||
6951 | nFirst = nMid; | |||
6952 | else | |||
6953 | nLast = nMid; | |||
6954 | } | |||
6955 | ||||
6956 | if (nDelta == static_cast<SCCOLROW>(nLenMajor-2)) // last item | |||
6957 | { | |||
6958 | sal_Int32 nCmp = lcl_CompareMatrix2Query(nDelta+1, aMatAcc2, rEntry); | |||
6959 | if (nCmp <= 0) | |||
6960 | { | |||
6961 | // either the last item is an exact match or the real | |||
6962 | // hit is beyond the last item. | |||
6963 | nDelta += 1; | |||
6964 | bFound = true; | |||
6965 | } | |||
6966 | } | |||
6967 | else if (nDelta > 0) // valid hit must be 2nd item or higher | |||
6968 | { | |||
6969 | // non-exact match | |||
6970 | bFound = true; | |||
6971 | } | |||
6972 | ||||
6973 | // With 0-9 < A-Z, if query is numeric and data found is string, or | |||
6974 | // vice versa, the (yet another undocumented) Excel behavior is to | |||
6975 | // return #N/A instead. | |||
6976 | ||||
6977 | if (bFound) | |||
6978 | { | |||
6979 | if (!vIndex.empty()) | |||
6980 | nDelta = vIndex[nDelta]; | |||
6981 | ||||
6982 | VectorMatrixAccessor aMatAcc(*pDataMat, bVertical); | |||
6983 | SCCOLROW i = nDelta; | |||
6984 | SCSIZE n = aMatAcc.GetElementCount(); | |||
6985 | if (o3tl::make_unsigned(i) >= n) | |||
6986 | i = static_cast<SCCOLROW>(n); | |||
6987 | bool bByString = rEntry.GetQueryItem().meType == ScQueryEntry::ByString; | |||
6988 | if (bByString == aMatAcc.IsValue(i)) | |||
6989 | bFound = false; | |||
6990 | } | |||
6991 | ||||
6992 | if (!bFound) | |||
6993 | { | |||
6994 | PushNA(); | |||
6995 | return; | |||
6996 | } | |||
6997 | ||||
6998 | // Now that we've found the delta, push the result back to the cell. | |||
6999 | ||||
7000 | if (pResMat) | |||
7001 | { | |||
7002 | VectorMatrixAccessor aResMatAcc(*pResMat, bVertical); | |||
7003 | // result array is matrix. | |||
7004 | if (o3tl::make_unsigned(nDelta) >= aResMatAcc.GetElementCount()) | |||
7005 | { | |||
7006 | PushNA(); | |||
7007 | return; | |||
7008 | } | |||
7009 | if (aResMatAcc.IsValue(nDelta)) | |||
7010 | PushDouble(aResMatAcc.GetDouble(nDelta)); | |||
7011 | else | |||
7012 | PushString(aResMatAcc.GetString(nDelta)); | |||
7013 | } | |||
7014 | else if (nParamCount == 3) | |||
7015 | { | |||
7016 | // result array is cell range. | |||
7017 | ScAddress aAdr; | |||
7018 | aAdr.SetTab(nResTab); | |||
7019 | bool bResVertical = (nResRow2 - nResRow1) > 0; | |||
7020 | if (bResVertical) | |||
7021 | { | |||
7022 | SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta); | |||
7023 | if (nTempRow > mrDoc.MaxRow()) | |||
7024 | { | |||
7025 | PushDouble(0); | |||
7026 | return; | |||
7027 | } | |||
7028 | aAdr.SetCol(nResCol1); | |||
7029 | aAdr.SetRow(nTempRow); | |||
7030 | } | |||
7031 | else | |||
7032 | { | |||
7033 | SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta); | |||
7034 | if (nTempCol > mrDoc.MaxCol()) | |||
7035 | { | |||
7036 | PushDouble(0); | |||
7037 | return; | |||
7038 | } | |||
7039 | aAdr.SetCol(nTempCol); | |||
7040 | aAdr.SetRow(nResRow1); | |||
7041 | } | |||
7042 | PushCellResultToken(true, aAdr, nullptr, nullptr); | |||
7043 | } | |||
7044 | else | |||
7045 | { | |||
7046 | // No result array. Use the data array to get the final value from. | |||
7047 | // Propagate errors from matrix again. | |||
7048 | pDataMat->SetErrorInterpreter( this); | |||
7049 | if (bVertical) | |||
7050 | { | |||
7051 | if (pDataMat->IsValue(nC-1, nDelta)) | |||
7052 | PushDouble(pDataMat->GetDouble(nC-1, nDelta)); | |||
7053 | else | |||
7054 | PushString(pDataMat->GetString(nC-1, nDelta)); | |||
7055 | } | |||
7056 | else | |||
7057 | { | |||
7058 | if (pDataMat->IsValue(nDelta, nR-1)) | |||
7059 | PushDouble(pDataMat->GetDouble(nDelta, nR-1)); | |||
7060 | else | |||
7061 | PushString(pDataMat->GetString(nDelta, nR-1)); | |||
7062 | } | |||
7063 | } | |||
7064 | ||||
7065 | return; | |||
7066 | } | |||
7067 | ||||
7068 | // Perform cell range search. | |||
7069 | ||||
7070 | aParam.nCol1 = nCol1; | |||
7071 | aParam.nRow1 = nRow1; | |||
7072 | aParam.nCol2 = bVertical ? nCol1 : nCol2; | |||
7073 | aParam.nRow2 = bVertical ? nRow2 : nRow1; | |||
7074 | aParam.bByRow = bVertical; | |||
7075 | ||||
7076 | rEntry.bDoQuery = true; | |||
7077 | rEntry.eOp = SC_LESS_EQUAL; | |||
7078 | rEntry.nField = nCol1; | |||
7079 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
7080 | if (rItem.meType == ScQueryEntry::ByString) | |||
7081 | aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); | |||
7082 | ||||
7083 | ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, aParam, false); | |||
7084 | SCCOL nC; | |||
7085 | SCROW nR; | |||
7086 | // Advance Entry.nField in iterator upon switching columns if | |||
7087 | // lookup in row. | |||
7088 | aCellIter.SetAdvanceQueryParamEntryField(!bVertical); | |||
7089 | if ( !aCellIter.FindEqualOrSortedLastInRange(nC, nR) ) | |||
7090 | { | |||
7091 | PushNA(); | |||
7092 | return; | |||
7093 | } | |||
7094 | ||||
7095 | SCCOLROW nDelta = bVertical ? static_cast<SCSIZE>(nR-nRow1) : static_cast<SCSIZE>(nC-nCol1); | |||
7096 | ||||
7097 | if (pResMat) | |||
7098 | { | |||
7099 | VectorMatrixAccessor aResMatAcc(*pResMat, bVertical); | |||
7100 | // Use the matrix result array. | |||
7101 | if (aResMatAcc.IsValue(nDelta)) | |||
7102 | PushDouble(aResMatAcc.GetDouble(nDelta)); | |||
7103 | else | |||
7104 | PushString(aResMatAcc.GetString(nDelta)); | |||
7105 | } | |||
7106 | else if (nParamCount == 3) | |||
7107 | { | |||
7108 | switch (eResArrayType) | |||
7109 | { | |||
7110 | case svDoubleRef: | |||
7111 | { | |||
7112 | // Use the result array vector. Note that the result array is assumed | |||
7113 | // to be a vector (i.e. 1-dimensional array). | |||
7114 | ||||
7115 | ScAddress aAdr; | |||
7116 | aAdr.SetTab(nResTab); | |||
7117 | bool bResVertical = (nResRow2 - nResRow1) > 0; | |||
7118 | if (bResVertical) | |||
7119 | { | |||
7120 | SCROW nTempRow = static_cast<SCROW>(nResRow1 + nDelta); | |||
7121 | if (nTempRow > mrDoc.MaxRow()) | |||
7122 | { | |||
7123 | PushDouble(0); | |||
7124 | return; | |||
7125 | } | |||
7126 | aAdr.SetCol(nResCol1); | |||
7127 | aAdr.SetRow(nTempRow); | |||
7128 | } | |||
7129 | else | |||
7130 | { | |||
7131 | SCCOL nTempCol = static_cast<SCCOL>(nResCol1 + nDelta); | |||
7132 | if (nTempCol > mrDoc.MaxCol()) | |||
7133 | { | |||
7134 | PushDouble(0); | |||
7135 | return; | |||
7136 | } | |||
7137 | aAdr.SetCol(nTempCol); | |||
7138 | aAdr.SetRow(nResRow1); | |||
7139 | } | |||
7140 | PushCellResultToken( true, aAdr, nullptr, nullptr); | |||
7141 | } | |||
7142 | break; | |||
7143 | case svDouble: | |||
7144 | case svString: | |||
7145 | case svSingleRef: | |||
7146 | { | |||
7147 | if (nDelta != 0) | |||
7148 | PushNA(); | |||
7149 | else | |||
7150 | { | |||
7151 | switch (eResArrayType) | |||
7152 | { | |||
7153 | case svDouble: | |||
7154 | PushDouble( fResVal ); | |||
7155 | break; | |||
7156 | case svString: | |||
7157 | PushString( aResStr ); | |||
7158 | break; | |||
7159 | case svSingleRef: | |||
7160 | PushCellResultToken( true, aResAdr, nullptr, nullptr); | |||
7161 | break; | |||
7162 | default: | |||
7163 | ; // nothing | |||
7164 | } | |||
7165 | } | |||
7166 | } | |||
7167 | break; | |||
7168 | default: | |||
7169 | OSL_FAIL( "ScInterpreter::ScLookup: unhandled eResArrayType, range search")do { if (true && (((sal_Bool)1))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "7169" ": "), "%s", "ScInterpreter::ScLookup: unhandled eResArrayType, range search" ); } } while (false); | |||
7170 | } | |||
7171 | } | |||
7172 | else | |||
7173 | { | |||
7174 | // Regardless of whether or not the result array exists, the last | |||
7175 | // array is always used as the "result" array. | |||
7176 | ||||
7177 | ScAddress aAdr; | |||
7178 | aAdr.SetTab(nTab1); | |||
7179 | if (bVertical) | |||
7180 | { | |||
7181 | SCROW nTempRow = static_cast<SCROW>(nRow1 + nDelta); | |||
7182 | if (nTempRow > mrDoc.MaxRow()) | |||
7183 | { | |||
7184 | PushDouble(0); | |||
7185 | return; | |||
7186 | } | |||
7187 | aAdr.SetCol(nCol2); | |||
7188 | aAdr.SetRow(nTempRow); | |||
7189 | } | |||
7190 | else | |||
7191 | { | |||
7192 | SCCOL nTempCol = static_cast<SCCOL>(nCol1 + nDelta); | |||
7193 | if (nTempCol > mrDoc.MaxCol()) | |||
7194 | { | |||
7195 | PushDouble(0); | |||
7196 | return; | |||
7197 | } | |||
7198 | aAdr.SetCol(nTempCol); | |||
7199 | aAdr.SetRow(nRow2); | |||
7200 | } | |||
7201 | PushCellResultToken(true, aAdr, nullptr, nullptr); | |||
7202 | } | |||
7203 | } | |||
7204 | ||||
7205 | void ScInterpreter::ScHLookup() | |||
7206 | { | |||
7207 | CalculateLookup(true); | |||
7208 | } | |||
7209 | ||||
7210 | void ScInterpreter::CalculateLookup(bool bHLookup) | |||
7211 | { | |||
7212 | sal_uInt8 nParamCount = GetByte(); | |||
7213 | if (!MustHaveParamCount(nParamCount, 3, 4)) | |||
7214 | return; | |||
7215 | ||||
7216 | // Optional 4th argument to declare whether or not the range is sorted. | |||
7217 | bool bSorted = true; | |||
7218 | if (nParamCount == 4) | |||
7219 | bSorted = GetBool(); | |||
7220 | ||||
7221 | // Index of column to search. | |||
7222 | double fIndex = ::rtl::math::approxFloor( GetDouble() ) - 1.0; | |||
7223 | ||||
7224 | ScMatrixRef pMat = nullptr; | |||
7225 | SCSIZE nC = 0, nR = 0; | |||
7226 | SCCOL nCol1 = 0; | |||
7227 | SCROW nRow1 = 0; | |||
7228 | SCTAB nTab1 = 0; | |||
7229 | SCCOL nCol2 = 0; | |||
7230 | SCROW nRow2 = 0; | |||
7231 | StackVar eType = GetStackType(); | |||
7232 | if (eType == svDoubleRef) | |||
7233 | { | |||
7234 | SCTAB nTab2; | |||
7235 | PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
7236 | if (nTab1 != nTab2) | |||
7237 | { | |||
7238 | PushIllegalParameter(); | |||
7239 | return; | |||
7240 | } | |||
7241 | } | |||
7242 | else if (eType == svSingleRef) | |||
7243 | { | |||
7244 | PopSingleRef(nCol1, nRow1, nTab1); | |||
7245 | nCol2 = nCol1; | |||
7246 | nRow2 = nRow1; | |||
7247 | } | |||
7248 | else if (eType == svMatrix || eType == svExternalDoubleRef || eType == svExternalSingleRef) | |||
7249 | { | |||
7250 | pMat = GetMatrix(); | |||
7251 | ||||
7252 | if (pMat) | |||
7253 | pMat->GetDimensions(nC, nR); | |||
7254 | else | |||
7255 | { | |||
7256 | PushIllegalParameter(); | |||
7257 | return; | |||
7258 | } | |||
7259 | } | |||
7260 | else | |||
7261 | { | |||
7262 | PushIllegalParameter(); | |||
7263 | return; | |||
7264 | } | |||
7265 | ||||
7266 | if ( fIndex < 0.0 || (bHLookup ? (pMat ? (fIndex >= nR) : (fIndex+nRow1 > nRow2)) : (pMat ? (fIndex >= nC) : (fIndex+nCol1 > nCol2)) ) ) | |||
7267 | { | |||
7268 | PushIllegalArgument(); | |||
7269 | return; | |||
7270 | } | |||
7271 | ||||
7272 | SCROW nZIndex = static_cast<SCROW>(fIndex); | |||
7273 | SCCOL nSpIndex = static_cast<SCCOL>(fIndex); | |||
7274 | ||||
7275 | if (!pMat) | |||
7276 | { | |||
7277 | nZIndex += nRow1; // value row | |||
7278 | nSpIndex = sal::static_int_cast<SCCOL>( nSpIndex + nCol1 ); // value column | |||
7279 | } | |||
7280 | ||||
7281 | if (nGlobalError != FormulaError::NONE) | |||
7282 | { | |||
7283 | PushIllegalParameter(); | |||
7284 | return; | |||
7285 | } | |||
7286 | ||||
7287 | ScQueryParam aParam; | |||
7288 | aParam.nCol1 = nCol1; | |||
7289 | aParam.nRow1 = nRow1; | |||
7290 | if ( bHLookup ) | |||
7291 | { | |||
7292 | aParam.nCol2 = nCol2; | |||
7293 | aParam.nRow2 = nRow1; // search only in the first row | |||
7294 | aParam.bByRow = false; | |||
7295 | } | |||
7296 | else | |||
7297 | { | |||
7298 | aParam.nCol2 = nCol1; // search only in the first column | |||
7299 | aParam.nRow2 = nRow2; | |||
7300 | aParam.nTab = nTab1; | |||
7301 | } | |||
7302 | ||||
7303 | ScQueryEntry& rEntry = aParam.GetEntry(0); | |||
7304 | rEntry.bDoQuery = true; | |||
7305 | if ( bSorted ) | |||
7306 | rEntry.eOp = SC_LESS_EQUAL; | |||
7307 | if ( !FillEntry(rEntry) ) | |||
7308 | return; | |||
7309 | ||||
7310 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
7311 | if (rItem.meType == ScQueryEntry::ByString) | |||
7312 | aParam.eSearchType = DetectSearchType(rItem.maString.getString(), mrDoc); | |||
7313 | if (pMat) | |||
7314 | { | |||
7315 | SCSIZE nMatCount = bHLookup ? nC : nR; | |||
7316 | SCSIZE nDelta = SCSIZE_MAX; | |||
7317 | if (rItem.meType == ScQueryEntry::ByString) | |||
7318 | { | |||
7319 | //!!!!!!! | |||
7320 | //TODO: enable regex on matrix strings | |||
7321 | //!!!!!!! | |||
7322 | svl::SharedString aParamStr = rItem.maString; | |||
7323 | if ( bSorted ) | |||
7324 | { | |||
7325 | CollatorWrapper* pCollator = ScGlobal::GetCollator(); | |||
7326 | for (SCSIZE i = 0; i < nMatCount; i++) | |||
7327 | { | |||
7328 | if (bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i)) | |||
7329 | { | |||
7330 | sal_Int32 nRes = | |||
7331 | pCollator->compareString( | |||
7332 | bHLookup ? pMat->GetString(i,0).getString() : pMat->GetString(0,i).getString(), aParamStr.getString()); | |||
7333 | if (nRes <= 0) | |||
7334 | nDelta = i; | |||
7335 | else if (i>0) // #i2168# ignore first mismatch | |||
7336 | i = nMatCount+1; | |||
7337 | } | |||
7338 | else | |||
7339 | nDelta = i; | |||
7340 | } | |||
7341 | } | |||
7342 | else | |||
7343 | { | |||
7344 | if (bHLookup) | |||
7345 | { | |||
7346 | for (SCSIZE i = 0; i < nMatCount; i++) | |||
7347 | { | |||
7348 | if (pMat->IsStringOrEmpty(i, 0)) | |||
7349 | { | |||
7350 | if (pMat->GetString(i,0).getDataIgnoreCase() == aParamStr.getDataIgnoreCase()) | |||
7351 | { | |||
7352 | nDelta = i; | |||
7353 | i = nMatCount + 1; | |||
7354 | } | |||
7355 | } | |||
7356 | } | |||
7357 | } | |||
7358 | else | |||
7359 | { | |||
7360 | nDelta = pMat->MatchStringInColumns(aParamStr, 0, 0); | |||
7361 | } | |||
7362 | } | |||
7363 | } | |||
7364 | else | |||
7365 | { | |||
7366 | if ( bSorted ) | |||
7367 | { | |||
7368 | // #i2168# ignore strings | |||
7369 | for (SCSIZE i = 0; i < nMatCount; i++) | |||
7370 | { | |||
7371 | if (!(bHLookup ? pMat->IsStringOrEmpty(i, 0) : pMat->IsStringOrEmpty(0, i))) | |||
7372 | { | |||
7373 | if ((bHLookup ? pMat->GetDouble(i,0) : pMat->GetDouble(0,i)) <= rItem.mfVal) | |||
7374 | nDelta = i; | |||
7375 | else | |||
7376 | i = nMatCount+1; | |||
7377 | } | |||
7378 | } | |||
7379 | } | |||
7380 | else | |||
7381 | { | |||
7382 | if (bHLookup) | |||
7383 | { | |||
7384 | for (SCSIZE i = 0; i < nMatCount; i++) | |||
7385 | { | |||
7386 | if (! pMat->IsStringOrEmpty(i, 0) ) | |||
7387 | { | |||
7388 | if ( pMat->GetDouble(i,0) == rItem.mfVal) | |||
7389 | { | |||
7390 | nDelta = i; | |||
7391 | i = nMatCount + 1; | |||
7392 | } | |||
7393 | } | |||
7394 | } | |||
7395 | } | |||
7396 | else | |||
7397 | { | |||
7398 | nDelta = pMat->MatchDoubleInColumns(rItem.mfVal, 0, 0); | |||
7399 | } | |||
7400 | } | |||
7401 | } | |||
7402 | if ( nDelta != SCSIZE_MAX ) | |||
7403 | { | |||
7404 | SCSIZE nX = static_cast<SCSIZE>(nSpIndex); | |||
7405 | SCSIZE nY = nDelta; | |||
7406 | if ( bHLookup ) | |||
7407 | { | |||
7408 | nX = nDelta; | |||
7409 | nY = static_cast<SCSIZE>(nZIndex); | |||
7410 | } | |||
7411 | assert( nX < nC && nY < nR )(static_cast <bool> (nX < nC && nY < nR) ? void (0) : __assert_fail ("nX < nC && nY < nR" , "/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" , 7411, __extension__ __PRETTY_FUNCTION__)); | |||
7412 | if ( pMat->IsStringOrEmpty( nX, nY) ) | |||
7413 | PushString(pMat->GetString( nX,nY).getString()); | |||
7414 | else | |||
7415 | PushDouble(pMat->GetDouble( nX,nY)); | |||
7416 | } | |||
7417 | else | |||
7418 | PushNA(); | |||
7419 | } | |||
7420 | else | |||
7421 | { | |||
7422 | rEntry.nField = nCol1; | |||
7423 | bool bFound = false; | |||
7424 | SCCOL nCol = 0; | |||
7425 | SCROW nRow = 0; | |||
7426 | if ( bSorted ) | |||
7427 | rEntry.eOp = SC_LESS_EQUAL; | |||
7428 | if ( bHLookup ) | |||
7429 | { | |||
7430 | ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab1, aParam, false); | |||
7431 | // advance Entry.nField in Iterator upon switching columns | |||
7432 | aCellIter.SetAdvanceQueryParamEntryField( true ); | |||
7433 | if ( bSorted ) | |||
7434 | { | |||
7435 | SCROW nRow1_temp; | |||
7436 | bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow1_temp ); | |||
7437 | } | |||
7438 | else if ( aCellIter.GetFirst() ) | |||
7439 | { | |||
7440 | bFound = true; | |||
7441 | nCol = aCellIter.GetCol(); | |||
7442 | } | |||
7443 | nRow = nZIndex; | |||
7444 | } | |||
7445 | else | |||
7446 | { | |||
7447 | ScAddress aResultPos( nCol1, nRow1, nTab1); | |||
7448 | bFound = LookupQueryWithCache( aResultPos, aParam); | |||
7449 | nRow = aResultPos.Row(); | |||
7450 | nCol = nSpIndex; | |||
7451 | } | |||
7452 | ||||
7453 | if ( bFound ) | |||
7454 | { | |||
7455 | ScAddress aAdr( nCol, nRow, nTab1 ); | |||
7456 | PushCellResultToken( true, aAdr, nullptr, nullptr); | |||
7457 | } | |||
7458 | else | |||
7459 | PushNA(); | |||
7460 | } | |||
7461 | } | |||
7462 | ||||
7463 | bool ScInterpreter::FillEntry(ScQueryEntry& rEntry) | |||
7464 | { | |||
7465 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
7466 | switch ( GetStackType() ) | |||
7467 | { | |||
7468 | case svDouble: | |||
7469 | { | |||
7470 | rItem.meType = ScQueryEntry::ByValue; | |||
7471 | rItem.mfVal = GetDouble(); | |||
7472 | } | |||
7473 | break; | |||
7474 | case svString: | |||
7475 | { | |||
7476 | rItem.meType = ScQueryEntry::ByString; | |||
7477 | rItem.maString = GetString(); | |||
7478 | } | |||
7479 | break; | |||
7480 | case svDoubleRef : | |||
7481 | case svSingleRef : | |||
7482 | { | |||
7483 | ScAddress aAdr; | |||
7484 | if ( !PopDoubleRefOrSingleRef( aAdr ) ) | |||
7485 | { | |||
7486 | PushInt(0); | |||
7487 | return false; | |||
7488 | } | |||
7489 | ScRefCellValue aCell(mrDoc, aAdr); | |||
7490 | if (aCell.hasNumeric()) | |||
7491 | { | |||
7492 | rItem.meType = ScQueryEntry::ByValue; | |||
7493 | rItem.mfVal = GetCellValue(aAdr, aCell); | |||
7494 | } | |||
7495 | else | |||
7496 | { | |||
7497 | GetCellString(rItem.maString, aCell); | |||
7498 | rItem.meType = ScQueryEntry::ByString; | |||
7499 | } | |||
7500 | } | |||
7501 | break; | |||
7502 | case svExternalDoubleRef: | |||
7503 | case svExternalSingleRef: | |||
7504 | case svMatrix: | |||
7505 | { | |||
7506 | svl::SharedString aStr; | |||
7507 | const ScMatValType nType = GetDoubleOrStringFromMatrix(rItem.mfVal, aStr); | |||
7508 | rItem.maString = aStr; | |||
7509 | rItem.meType = ScMatrix::IsNonValueType(nType) ? | |||
7510 | ScQueryEntry::ByString : ScQueryEntry::ByValue; | |||
7511 | } | |||
7512 | break; | |||
7513 | default: | |||
7514 | { | |||
7515 | PushIllegalParameter(); | |||
7516 | return false; | |||
7517 | } | |||
7518 | } // switch ( GetStackType() ) | |||
7519 | return true; | |||
7520 | } | |||
7521 | ||||
7522 | void ScInterpreter::ScVLookup() | |||
7523 | { | |||
7524 | CalculateLookup(false); | |||
7525 | } | |||
7526 | ||||
7527 | void ScInterpreter::ScSubTotal() | |||
7528 | { | |||
7529 | sal_uInt8 nParamCount = GetByte(); | |||
7530 | if ( !MustHaveParamCountMin( nParamCount, 2 ) ) | |||
7531 | return; | |||
7532 | ||||
7533 | // We must fish the 1st parameter deep from the stack! And push it on top. | |||
7534 | const FormulaToken* p = pStack[ sp - nParamCount ]; | |||
7535 | PushWithoutError( *p ); | |||
7536 | sal_Int32 nFunc = GetInt32(); | |||
7537 | mnSubTotalFlags |= SubtotalFlags::IgnoreNestedStAg | SubtotalFlags::IgnoreFiltered; | |||
7538 | if (nFunc > 100) | |||
7539 | { | |||
7540 | // For opcodes 101 through 111, we need to skip hidden cells. | |||
7541 | // Other than that these opcodes are identical to 1 through 11. | |||
7542 | mnSubTotalFlags |= SubtotalFlags::IgnoreHidden; | |||
7543 | nFunc -= 100; | |||
7544 | } | |||
7545 | ||||
7546 | if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 11 ) | |||
7547 | PushIllegalArgument(); // simulate return on stack, not SetError(...) | |||
7548 | else | |||
7549 | { | |||
7550 | cPar = nParamCount - 1; | |||
7551 | switch( nFunc ) | |||
7552 | { | |||
7553 | case SUBTOTAL_FUNC_AVE : ScAverage(); break; | |||
7554 | case SUBTOTAL_FUNC_CNT : ScCount(); break; | |||
7555 | case SUBTOTAL_FUNC_CNT2 : ScCount2(); break; | |||
7556 | case SUBTOTAL_FUNC_MAX : ScMax(); break; | |||
7557 | case SUBTOTAL_FUNC_MIN : ScMin(); break; | |||
7558 | case SUBTOTAL_FUNC_PROD : ScProduct(); break; | |||
7559 | case SUBTOTAL_FUNC_STD : ScStDev(); break; | |||
7560 | case SUBTOTAL_FUNC_STDP : ScStDevP(); break; | |||
7561 | case SUBTOTAL_FUNC_SUM : ScSum(); break; | |||
7562 | case SUBTOTAL_FUNC_VAR : ScVar(); break; | |||
7563 | case SUBTOTAL_FUNC_VARP : ScVarP(); break; | |||
7564 | default : PushIllegalArgument(); break; | |||
7565 | } | |||
7566 | } | |||
7567 | mnSubTotalFlags = SubtotalFlags::NONE; | |||
7568 | // Get rid of the 1st (fished) parameter. | |||
7569 | FormulaConstTokenRef xRef( PopToken()); | |||
7570 | Pop(); | |||
7571 | PushTokenRef( xRef); | |||
7572 | } | |||
7573 | ||||
7574 | void ScInterpreter::ScAggregate() | |||
7575 | { | |||
7576 | sal_uInt8 nParamCount = GetByte(); | |||
7577 | if ( !MustHaveParamCountMin( nParamCount, 3 ) ) | |||
7578 | return; | |||
7579 | ||||
7580 | const FormulaError nErr = nGlobalError; | |||
7581 | nGlobalError = FormulaError::NONE; | |||
7582 | ||||
7583 | // fish the 1st parameter from the stack and push it on top. | |||
7584 | const FormulaToken* p = pStack[ sp - nParamCount ]; | |||
7585 | PushWithoutError( *p ); | |||
7586 | sal_Int32 nFunc = GetInt32(); | |||
7587 | // fish the 2nd parameter from the stack and push it on top. | |||
7588 | const FormulaToken* p2 = pStack[ sp - ( nParamCount - 1 ) ]; | |||
7589 | PushWithoutError( *p2 ); | |||
7590 | sal_Int32 nOption = GetInt32(); | |||
7591 | ||||
7592 | if ( nGlobalError != FormulaError::NONE || nFunc < 1 || nFunc > 19 ) | |||
7593 | { | |||
7594 | nGlobalError = nErr; | |||
7595 | PushIllegalArgument(); | |||
7596 | } | |||
7597 | else | |||
7598 | { | |||
7599 | switch ( nOption) | |||
7600 | { | |||
7601 | case 0 : // ignore nested SUBTOTAL and AGGREGATE functions | |||
7602 | mnSubTotalFlags = SubtotalFlags::IgnoreNestedStAg; | |||
7603 | break; | |||
7604 | case 1 : // ignore hidden rows, nested SUBTOTAL and AGGREGATE functions | |||
7605 | mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreNestedStAg; | |||
7606 | break; | |||
7607 | case 2 : // ignore error values, nested SUBTOTAL and AGGREGATE functions | |||
7608 | mnSubTotalFlags = SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg; | |||
7609 | break; | |||
7610 | case 3 : // ignore hidden rows, error values, nested SUBTOTAL and AGGREGATE functions | |||
7611 | mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal | SubtotalFlags::IgnoreNestedStAg; | |||
7612 | break; | |||
7613 | case 4 : // ignore nothing | |||
7614 | mnSubTotalFlags = SubtotalFlags::NONE; | |||
7615 | break; | |||
7616 | case 5 : // ignore hidden rows | |||
7617 | mnSubTotalFlags = SubtotalFlags::IgnoreHidden ; | |||
7618 | break; | |||
7619 | case 6 : // ignore error values | |||
7620 | mnSubTotalFlags = SubtotalFlags::IgnoreErrVal ; | |||
7621 | break; | |||
7622 | case 7 : // ignore hidden rows and error values | |||
7623 | mnSubTotalFlags = SubtotalFlags::IgnoreHidden | SubtotalFlags::IgnoreErrVal ; | |||
7624 | break; | |||
7625 | default : | |||
7626 | nGlobalError = nErr; | |||
7627 | PushIllegalArgument(); | |||
7628 | return; | |||
7629 | } | |||
7630 | ||||
7631 | if ((mnSubTotalFlags & SubtotalFlags::IgnoreErrVal) == SubtotalFlags::NONE) | |||
7632 | nGlobalError = nErr; | |||
7633 | ||||
7634 | cPar = nParamCount - 2; | |||
7635 | switch ( nFunc ) | |||
7636 | { | |||
7637 | case AGGREGATE_FUNC_AVE : ScAverage(); break; | |||
7638 | case AGGREGATE_FUNC_CNT : ScCount(); break; | |||
7639 | case AGGREGATE_FUNC_CNT2 : ScCount2(); break; | |||
7640 | case AGGREGATE_FUNC_MAX : ScMax(); break; | |||
7641 | case AGGREGATE_FUNC_MIN : ScMin(); break; | |||
7642 | case AGGREGATE_FUNC_PROD : ScProduct(); break; | |||
7643 | case AGGREGATE_FUNC_STD : ScStDev(); break; | |||
7644 | case AGGREGATE_FUNC_STDP : ScStDevP(); break; | |||
7645 | case AGGREGATE_FUNC_SUM : ScSum(); break; | |||
7646 | case AGGREGATE_FUNC_VAR : ScVar(); break; | |||
7647 | case AGGREGATE_FUNC_VARP : ScVarP(); break; | |||
7648 | case AGGREGATE_FUNC_MEDIAN : ScMedian(); break; | |||
7649 | case AGGREGATE_FUNC_MODSNGL : ScModalValue(); break; | |||
7650 | case AGGREGATE_FUNC_LARGE : ScLarge(); break; | |||
7651 | case AGGREGATE_FUNC_SMALL : ScSmall(); break; | |||
7652 | case AGGREGATE_FUNC_PERCINC : ScPercentile( true ); break; | |||
7653 | case AGGREGATE_FUNC_QRTINC : ScQuartile( true ); break; | |||
7654 | case AGGREGATE_FUNC_PERCEXC : ScPercentile( false ); break; | |||
7655 | case AGGREGATE_FUNC_QRTEXC : ScQuartile( false ); break; | |||
7656 | default: | |||
7657 | nGlobalError = nErr; | |||
7658 | PushIllegalArgument(); | |||
7659 | break; | |||
7660 | } | |||
7661 | mnSubTotalFlags = SubtotalFlags::NONE; | |||
7662 | } | |||
7663 | FormulaConstTokenRef xRef( PopToken()); | |||
7664 | // Get rid of the 1st and 2nd (fished) parameters. | |||
7665 | Pop(); | |||
7666 | Pop(); | |||
7667 | PushTokenRef( xRef); | |||
7668 | } | |||
7669 | ||||
7670 | std::unique_ptr<ScDBQueryParamBase> ScInterpreter::GetDBParams( bool& rMissingField ) | |||
7671 | { | |||
7672 | bool bAllowMissingField = false; | |||
7673 | if ( rMissingField ) | |||
7674 | { | |||
7675 | bAllowMissingField = true; | |||
7676 | rMissingField = false; | |||
7677 | } | |||
7678 | if ( GetByte() == 3 ) | |||
7679 | { | |||
7680 | // First, get the query criteria range. | |||
7681 | ::std::unique_ptr<ScDBRangeBase> pQueryRef( PopDBDoubleRef() ); | |||
7682 | if (!pQueryRef) | |||
7683 | return nullptr; | |||
7684 | ||||
7685 | bool bByVal = true; | |||
7686 | double nVal = 0.0; | |||
7687 | svl::SharedString aStr; | |||
7688 | ScRange aMissingRange; | |||
7689 | bool bRangeFake = false; | |||
7690 | switch (GetStackType()) | |||
7691 | { | |||
7692 | case svDouble : | |||
7693 | nVal = ::rtl::math::approxFloor( GetDouble() ); | |||
7694 | if ( bAllowMissingField && nVal == 0.0 ) | |||
7695 | rMissingField = true; // fake missing parameter | |||
7696 | break; | |||
7697 | case svString : | |||
7698 | bByVal = false; | |||
7699 | aStr = GetString(); | |||
7700 | break; | |||
7701 | case svSingleRef : | |||
7702 | { | |||
7703 | ScAddress aAdr; | |||
7704 | PopSingleRef( aAdr ); | |||
7705 | ScRefCellValue aCell(mrDoc, aAdr); | |||
7706 | if (aCell.hasNumeric()) | |||
7707 | nVal = GetCellValue(aAdr, aCell); | |||
7708 | else | |||
7709 | { | |||
7710 | bByVal = false; | |||
7711 | GetCellString(aStr, aCell); | |||
7712 | } | |||
7713 | } | |||
7714 | break; | |||
7715 | case svDoubleRef : | |||
7716 | if ( bAllowMissingField ) | |||
7717 | { // fake missing parameter for old SO compatibility | |||
7718 | bRangeFake = true; | |||
7719 | PopDoubleRef( aMissingRange ); | |||
7720 | } | |||
7721 | else | |||
7722 | { | |||
7723 | PopError(); | |||
7724 | SetError( FormulaError::IllegalParameter ); | |||
7725 | } | |||
7726 | break; | |||
7727 | case svMissing : | |||
7728 | PopError(); | |||
7729 | if ( bAllowMissingField ) | |||
7730 | rMissingField = true; | |||
7731 | else | |||
7732 | SetError( FormulaError::IllegalParameter ); | |||
7733 | break; | |||
7734 | default: | |||
7735 | PopError(); | |||
7736 | SetError( FormulaError::IllegalParameter ); | |||
7737 | } | |||
7738 | ||||
7739 | if (nGlobalError != FormulaError::NONE) | |||
7740 | return nullptr; | |||
7741 | ||||
7742 | unique_ptr<ScDBRangeBase> pDBRef( PopDBDoubleRef() ); | |||
7743 | ||||
7744 | if (nGlobalError != FormulaError::NONE || !pDBRef) | |||
7745 | return nullptr; | |||
7746 | ||||
7747 | if ( bRangeFake ) | |||
7748 | { | |||
7749 | // range parameter must match entire database range | |||
7750 | if (pDBRef->isRangeEqual(aMissingRange)) | |||
7751 | rMissingField = true; | |||
7752 | else | |||
7753 | SetError( FormulaError::IllegalParameter ); | |||
7754 | } | |||
7755 | ||||
7756 | if (nGlobalError != FormulaError::NONE) | |||
7757 | return nullptr; | |||
7758 | ||||
7759 | SCCOL nField = pDBRef->getFirstFieldColumn(); | |||
7760 | if (rMissingField) | |||
7761 | ; // special case | |||
7762 | else if (bByVal) | |||
7763 | nField = pDBRef->findFieldColumn(static_cast<SCCOL>(nVal)); | |||
7764 | else | |||
7765 | { | |||
7766 | FormulaError nErr = FormulaError::NONE; | |||
7767 | nField = pDBRef->findFieldColumn(aStr.getString(), &nErr); | |||
7768 | SetError(nErr); | |||
7769 | } | |||
7770 | ||||
7771 | if (!mrDoc.ValidCol(nField)) | |||
7772 | return nullptr; | |||
7773 | ||||
7774 | unique_ptr<ScDBQueryParamBase> pParam( pDBRef->createQueryParam(pQueryRef.get()) ); | |||
7775 | ||||
7776 | if (pParam) | |||
7777 | { | |||
7778 | // An allowed missing field parameter sets the result field | |||
7779 | // to any of the query fields, just to be able to return | |||
7780 | // some cell from the iterator. | |||
7781 | if ( rMissingField ) | |||
7782 | nField = static_cast<SCCOL>(pParam->GetEntry(0).nField); | |||
7783 | pParam->mnField = nField; | |||
7784 | ||||
7785 | SCSIZE nCount = pParam->GetEntryCount(); | |||
7786 | for ( SCSIZE i=0; i < nCount; i++ ) | |||
7787 | { | |||
7788 | ScQueryEntry& rEntry = pParam->GetEntry(i); | |||
7789 | if (!rEntry.bDoQuery) | |||
7790 | break; | |||
7791 | ||||
7792 | ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
7793 | sal_uInt32 nIndex = 0; | |||
7794 | OUString aQueryStr = rItem.maString.getString(); | |||
7795 | bool bNumber = pFormatter->IsNumberFormat( | |||
7796 | aQueryStr, nIndex, rItem.mfVal); | |||
7797 | rItem.meType = bNumber ? ScQueryEntry::ByValue : ScQueryEntry::ByString; | |||
7798 | ||||
7799 | if (!bNumber && pParam->eSearchType == utl::SearchParam::SearchType::Normal) | |||
7800 | pParam->eSearchType = DetectSearchType(aQueryStr, mrDoc); | |||
7801 | } | |||
7802 | return pParam; | |||
7803 | } | |||
7804 | } | |||
7805 | return nullptr; | |||
7806 | } | |||
7807 | ||||
7808 | void ScInterpreter::DBIterator( ScIterFunc eFunc ) | |||
7809 | { | |||
7810 | double nErg = 0.0; | |||
7811 | double fMem = 0.0; | |||
7812 | sal_uLong nCount = 0; | |||
7813 | bool bMissingField = false; | |||
7814 | unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); | |||
7815 | if (pQueryParam) | |||
7816 | { | |||
7817 | if (!pQueryParam->IsValidFieldIndex()) | |||
7818 | { | |||
7819 | SetError(FormulaError::NoValue); | |||
7820 | return; | |||
7821 | } | |||
7822 | ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); | |||
7823 | ScDBQueryDataIterator::Value aValue; | |||
7824 | if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE ) | |||
7825 | { | |||
7826 | switch( eFunc ) | |||
7827 | { | |||
7828 | case ifPRODUCT: nErg = 1; break; | |||
7829 | case ifMAX: nErg = -MAXDOUBLE1.7e307; break; | |||
7830 | case ifMIN: nErg = MAXDOUBLE1.7e307; break; | |||
7831 | default: ; // nothing | |||
7832 | } | |||
7833 | ||||
7834 | bool bNull = true; | |||
7835 | do | |||
7836 | { | |||
7837 | nCount++; | |||
7838 | switch( eFunc ) | |||
7839 | { | |||
7840 | case ifAVERAGE: | |||
7841 | case ifSUM: | |||
7842 | if ( bNull && aValue.mfValue != 0.0 ) | |||
7843 | { | |||
7844 | bNull = false; | |||
7845 | fMem = aValue.mfValue; | |||
7846 | } | |||
7847 | else | |||
7848 | nErg += aValue.mfValue; | |||
7849 | break; | |||
7850 | case ifSUMSQ: | |||
7851 | nErg += aValue.mfValue * aValue.mfValue; | |||
7852 | break; | |||
7853 | case ifPRODUCT: | |||
7854 | nErg *= aValue.mfValue; | |||
7855 | break; | |||
7856 | case ifMAX: | |||
7857 | if( aValue.mfValue > nErg ) nErg = aValue.mfValue; | |||
7858 | break; | |||
7859 | case ifMIN: | |||
7860 | if( aValue.mfValue < nErg ) nErg = aValue.mfValue; | |||
7861 | break; | |||
7862 | default: ; // nothing | |||
7863 | } | |||
7864 | } | |||
7865 | while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE ); | |||
7866 | } | |||
7867 | SetError(aValue.mnError); | |||
7868 | } | |||
7869 | else | |||
7870 | SetError( FormulaError::IllegalParameter); | |||
7871 | switch( eFunc ) | |||
7872 | { | |||
7873 | case ifCOUNT: nErg = nCount; break; | |||
7874 | case ifSUM: nErg = ::rtl::math::approxAdd( nErg, fMem ); break; | |||
7875 | case ifAVERAGE: nErg = div(::rtl::math::approxAdd(nErg, fMem), nCount); break; | |||
7876 | default: ; // nothing | |||
7877 | } | |||
7878 | PushDouble( nErg ); | |||
7879 | } | |||
7880 | ||||
7881 | void ScInterpreter::ScDBSum() | |||
7882 | { | |||
7883 | DBIterator( ifSUM ); | |||
7884 | } | |||
7885 | ||||
7886 | void ScInterpreter::ScDBCount() | |||
7887 | { | |||
7888 | bool bMissingField = true; | |||
7889 | unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); | |||
7890 | if (pQueryParam) | |||
7891 | { | |||
7892 | sal_uLong nCount = 0; | |||
7893 | if ( bMissingField && pQueryParam->GetType() == ScDBQueryParamBase::INTERNAL ) | |||
7894 | { // count all matching records | |||
7895 | // TODO: currently the QueryIterators only return cell pointers of | |||
7896 | // existing cells, so if a query matches an empty cell there's | |||
7897 | // nothing returned, and therefore not counted! | |||
7898 | // Since this has ever been the case and this code here only came | |||
7899 | // into existence to fix #i6899 and it never worked before we'll | |||
7900 | // have to live with it until we reimplement the iterators to also | |||
7901 | // return empty cells, which would mean to adapt all callers of | |||
7902 | // iterators. | |||
7903 | ScDBQueryParamInternal* p = static_cast<ScDBQueryParamInternal*>(pQueryParam.get()); | |||
7904 | p->nCol2 = p->nCol1; // Don't forget to select only one column. | |||
7905 | SCTAB nTab = p->nTab; | |||
7906 | // ScQueryCellIterator doesn't make use of ScDBQueryParamBase::mnField, | |||
7907 | // so the source range has to be restricted, like before the introduction | |||
7908 | // of ScDBQueryParamBase. | |||
7909 | p->nCol1 = p->nCol2 = p->mnField; | |||
7910 | ScQueryCellIterator aCellIter(mrDoc, mrContext, nTab, *p, true); | |||
7911 | if ( aCellIter.GetFirst() ) | |||
7912 | { | |||
7913 | do | |||
7914 | { | |||
7915 | nCount++; | |||
7916 | } while ( aCellIter.GetNext() ); | |||
7917 | } | |||
7918 | } | |||
7919 | else | |||
7920 | { // count only matching records with a value in the "result" field | |||
7921 | if (!pQueryParam->IsValidFieldIndex()) | |||
7922 | { | |||
7923 | SetError(FormulaError::NoValue); | |||
7924 | return; | |||
7925 | } | |||
7926 | ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); | |||
7927 | ScDBQueryDataIterator::Value aValue; | |||
7928 | if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE ) | |||
7929 | { | |||
7930 | do | |||
7931 | { | |||
7932 | nCount++; | |||
7933 | } | |||
7934 | while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE ); | |||
7935 | } | |||
7936 | SetError(aValue.mnError); | |||
7937 | } | |||
7938 | PushDouble( nCount ); | |||
7939 | } | |||
7940 | else | |||
7941 | PushIllegalParameter(); | |||
7942 | } | |||
7943 | ||||
7944 | void ScInterpreter::ScDBCount2() | |||
7945 | { | |||
7946 | bool bMissingField = true; | |||
7947 | unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); | |||
7948 | if (pQueryParam) | |||
7949 | { | |||
7950 | if (!pQueryParam->IsValidFieldIndex()) | |||
7951 | { | |||
7952 | SetError(FormulaError::NoValue); | |||
7953 | return; | |||
7954 | } | |||
7955 | sal_uLong nCount = 0; | |||
7956 | pQueryParam->mbSkipString = false; | |||
7957 | ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); | |||
7958 | ScDBQueryDataIterator::Value aValue; | |||
7959 | if ( aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE ) | |||
7960 | { | |||
7961 | do | |||
7962 | { | |||
7963 | nCount++; | |||
7964 | } | |||
7965 | while ( aValIter.GetNext(aValue) && aValue.mnError == FormulaError::NONE ); | |||
7966 | } | |||
7967 | SetError(aValue.mnError); | |||
7968 | PushDouble( nCount ); | |||
7969 | } | |||
7970 | else | |||
7971 | PushIllegalParameter(); | |||
7972 | } | |||
7973 | ||||
7974 | void ScInterpreter::ScDBAverage() | |||
7975 | { | |||
7976 | DBIterator( ifAVERAGE ); | |||
7977 | } | |||
7978 | ||||
7979 | void ScInterpreter::ScDBMax() | |||
7980 | { | |||
7981 | DBIterator( ifMAX ); | |||
7982 | } | |||
7983 | ||||
7984 | void ScInterpreter::ScDBMin() | |||
7985 | { | |||
7986 | DBIterator( ifMIN ); | |||
7987 | } | |||
7988 | ||||
7989 | void ScInterpreter::ScDBProduct() | |||
7990 | { | |||
7991 | DBIterator( ifPRODUCT ); | |||
7992 | } | |||
7993 | ||||
7994 | void ScInterpreter::GetDBStVarParams( double& rVal, double& rValCount ) | |||
7995 | { | |||
7996 | std::vector<double> values; | |||
7997 | double vSum = 0.0; | |||
7998 | double vMean = 0.0; | |||
7999 | ||||
8000 | rValCount = 0.0; | |||
8001 | double fSum = 0.0; | |||
8002 | bool bMissingField = false; | |||
8003 | unique_ptr<ScDBQueryParamBase> pQueryParam( GetDBParams(bMissingField) ); | |||
8004 | if (pQueryParam) | |||
8005 | { | |||
8006 | if (!pQueryParam->IsValidFieldIndex()) | |||
8007 | { | |||
8008 | SetError(FormulaError::NoValue); | |||
8009 | return; | |||
8010 | } | |||
8011 | ScDBQueryDataIterator aValIter(mrDoc, mrContext, std::move(pQueryParam)); | |||
8012 | ScDBQueryDataIterator::Value aValue; | |||
8013 | if (aValIter.GetFirst(aValue) && aValue.mnError == FormulaError::NONE) | |||
8014 | { | |||
8015 | do | |||
8016 | { | |||
8017 | rValCount++; | |||
8018 | values.push_back(aValue.mfValue); | |||
8019 | fSum += aValue.mfValue; | |||
8020 | } | |||
8021 | while ((aValue.mnError == FormulaError::NONE) && aValIter.GetNext(aValue)); | |||
8022 | } | |||
8023 | SetError(aValue.mnError); | |||
8024 | } | |||
8025 | else | |||
8026 | SetError( FormulaError::IllegalParameter); | |||
8027 | ||||
8028 | vMean = fSum / values.size(); | |||
8029 | ||||
8030 | for (double v : values) | |||
8031 | vSum += (v - vMean) * (v - vMean); | |||
8032 | ||||
8033 | rVal = vSum; | |||
8034 | } | |||
8035 | ||||
8036 | void ScInterpreter::ScDBStdDev() | |||
8037 | { | |||
8038 | double fVal, fCount; | |||
8039 | GetDBStVarParams( fVal, fCount ); | |||
8040 | PushDouble( sqrt(fVal/(fCount-1))); | |||
8041 | } | |||
8042 | ||||
8043 | void ScInterpreter::ScDBStdDevP() | |||
8044 | { | |||
8045 | double fVal, fCount; | |||
8046 | GetDBStVarParams( fVal, fCount ); | |||
8047 | PushDouble( sqrt(fVal/fCount)); | |||
8048 | } | |||
8049 | ||||
8050 | void ScInterpreter::ScDBVar() | |||
8051 | { | |||
8052 | double fVal, fCount; | |||
8053 | GetDBStVarParams( fVal, fCount ); | |||
8054 | PushDouble(fVal/(fCount-1)); | |||
8055 | } | |||
8056 | ||||
8057 | void ScInterpreter::ScDBVarP() | |||
8058 | { | |||
8059 | double fVal, fCount; | |||
| ||||
8060 | GetDBStVarParams( fVal, fCount ); | |||
8061 | PushDouble(fVal/fCount); | |||
| ||||
8062 | } | |||
8063 | ||||
8064 | void ScInterpreter::ScIndirect() | |||
8065 | { | |||
8066 | sal_uInt8 nParamCount = GetByte(); | |||
8067 | if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) | |||
8068 | return; | |||
8069 | ||||
8070 | // Reference address syntax for INDIRECT is configurable. | |||
8071 | FormulaGrammar::AddressConvention eConv = maCalcConfig.meStringRefAddressSyntax; | |||
8072 | if (eConv == FormulaGrammar::CONV_UNSPECIFIED) | |||
8073 | // Use the current address syntax if unspecified. | |||
8074 | eConv = mrDoc.GetAddressConvention(); | |||
8075 | ||||
8076 | // either CONV_A1_XL_A1 was explicitly configured, or it wasn't possible | |||
8077 | // to determine which syntax to use during doc import | |||
8078 | bool bTryXlA1 = (eConv == FormulaGrammar::CONV_A1_XL_A1); | |||
8079 | ||||
8080 | if (nParamCount == 2 && 0.0 == GetDouble() ) | |||
8081 | { | |||
8082 | // Overwrite the config and try Excel R1C1. | |||
8083 | eConv = FormulaGrammar::CONV_XL_R1C1; | |||
8084 | bTryXlA1 = false; | |||
8085 | } | |||
8086 | ||||
8087 | ||||
8088 | const ScAddress::Details aDetails( bTryXlA1 ? FormulaGrammar::CONV_OOO : eConv, aPos ); | |||
8089 | const ScAddress::Details aDetailsXlA1( FormulaGrammar::CONV_XL_A1, aPos ); | |||
8090 | SCTAB nTab = aPos.Tab(); | |||
8091 | OUString sRefStr = GetString().getString(); | |||
8092 | ScRefAddress aRefAd, aRefAd2; | |||
8093 | ScAddress::ExternalInfo aExtInfo; | |||
8094 | if ( ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, aRefAd2, aDetails, &aExtInfo) || | |||
8095 | ( bTryXlA1 && ConvertDoubleRef(mrDoc, sRefStr, nTab, aRefAd, | |||
8096 | aRefAd2, aDetailsXlA1, &aExtInfo) ) ) | |||
8097 | { | |||
8098 | if (aExtInfo.mbExternal) | |||
8099 | { | |||
8100 | PushExternalDoubleRef( | |||
8101 | aExtInfo.mnFileId, aExtInfo.maTabName, | |||
8102 | aRefAd.Col(), aRefAd.Row(), aRefAd.Tab(), | |||
8103 | aRefAd2.Col(), aRefAd2.Row(), aRefAd2.Tab()); | |||
8104 | } | |||
8105 | else | |||
8106 | PushDoubleRef( aRefAd, aRefAd2); | |||
8107 | } | |||
8108 | else if ( ConvertSingleRef(mrDoc, sRefStr, nTab, aRefAd, aDetails, &aExtInfo) || | |||
8109 | ( bTryXlA1 && ConvertSingleRef (mrDoc, sRefStr, nTab, aRefAd, | |||
8110 | aDetailsXlA1, &aExtInfo) ) ) | |||
8111 | { | |||
8112 | if (aExtInfo.mbExternal) | |||
8113 | { | |||
8114 | PushExternalSingleRef( | |||
8115 | aExtInfo.mnFileId, aExtInfo.maTabName, aRefAd.Col(), aRefAd.Row(), aRefAd.Tab()); | |||
8116 | } | |||
8117 | else | |||
8118 | PushSingleRef( aRefAd); | |||
8119 | } | |||
8120 | else | |||
8121 | { | |||
8122 | do | |||
8123 | { | |||
8124 | ScRangeData* pData = ScRangeStringConverter::GetRangeDataFromString(sRefStr, nTab, mrDoc); | |||
8125 | if (!pData) | |||
8126 | break; | |||
8127 | ||||
8128 | // We need this in order to obtain a good range. | |||
8129 | pData->ValidateTabRefs(); | |||
8130 | ||||
8131 | ScRange aRange; | |||
8132 | ||||
8133 | // This is the usual way to treat named ranges containing | |||
8134 | // relative references. | |||
8135 | if (!pData->IsReference( aRange, aPos)) | |||
8136 | break; | |||
8137 | ||||
8138 | if (aRange.aStart == aRange.aEnd) | |||
8139 | PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(), | |||
8140 | aRange.aStart.Tab()); | |||
8141 | else | |||
8142 | PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(), | |||
8143 | aRange.aStart.Tab(), aRange.aEnd.Col(), | |||
8144 | aRange.aEnd.Row(), aRange.aEnd.Tab()); | |||
8145 | ||||
8146 | // success! | |||
8147 | return; | |||
8148 | } | |||
8149 | while (false); | |||
8150 | ||||
8151 | do | |||
8152 | { | |||
8153 | OUString aName( ScGlobal::getCharClassPtr()->uppercase( sRefStr)); | |||
8154 | ScDBCollection::NamedDBs& rDBs = mrDoc.GetDBCollection()->getNamedDBs(); | |||
8155 | const ScDBData* pData = rDBs.findByUpperName( aName); | |||
8156 | if (!pData) | |||
8157 | break; | |||
8158 | ||||
8159 | ScRange aRange; | |||
8160 | pData->GetArea( aRange); | |||
8161 | ||||
8162 | // In Excel, specifying a table name without [] resolves to the | |||
8163 | // same as with [], a range that excludes header and totals | |||
8164 | // rows and contains only data rows. Do the same. | |||
8165 | if (pData->HasHeader()) | |||
8166 | aRange.aStart.IncRow(); | |||
8167 | if (pData->HasTotals()) | |||
8168 | aRange.aEnd.IncRow(-1); | |||
8169 | ||||
8170 | if (aRange.aStart.Row() > aRange.aEnd.Row()) | |||
8171 | break; | |||
8172 | ||||
8173 | if (aRange.aStart == aRange.aEnd) | |||
8174 | PushSingleRef( aRange.aStart.Col(), aRange.aStart.Row(), | |||
8175 | aRange.aStart.Tab()); | |||
8176 | else | |||
8177 | PushDoubleRef( aRange.aStart.Col(), aRange.aStart.Row(), | |||
8178 | aRange.aStart.Tab(), aRange.aEnd.Col(), | |||
8179 | aRange.aEnd.Row(), aRange.aEnd.Tab()); | |||
8180 | ||||
8181 | // success! | |||
8182 | return; | |||
8183 | } | |||
8184 | while (false); | |||
8185 | ||||
8186 | // It may be even a TableRef. | |||
8187 | // Anything else that resolves to one reference could be added | |||
8188 | // here, but we don't want to compile every arbitrary string. This | |||
8189 | // is already nasty enough... | |||
8190 | sal_Int32 nIndex = sRefStr.indexOf('['); | |||
8191 | if (nIndex >= 0 && sRefStr.indexOf(']',nIndex+1) > nIndex) | |||
8192 | { | |||
8193 | do | |||
8194 | { | |||
8195 | ScCompiler aComp( mrDoc, aPos, mrDoc.GetGrammar()); | |||
8196 | aComp.SetRefConvention( eConv); // must be after grammar | |||
8197 | std::unique_ptr<ScTokenArray> pTokArr( aComp.CompileString( sRefStr)); | |||
8198 | ||||
8199 | // Whatever... use only the specific case. | |||
8200 | if (!pTokArr->HasOpCode( ocTableRef)) | |||
8201 | break; | |||
8202 | ||||
8203 | aComp.CompileTokenArray(); | |||
8204 | ||||
8205 | // A syntactically valid reference will generate exactly | |||
8206 | // one RPN token, a reference or error. Discard everything | |||
8207 | // else as error. | |||
8208 | if (pTokArr->GetCodeLen() != 1) | |||
8209 | break; | |||
8210 | ||||
8211 | ScTokenRef xTok( pTokArr->FirstRPNToken()); | |||
8212 | if (!xTok) | |||
8213 | break; | |||
8214 | ||||
8215 | switch (xTok->GetType()) | |||
8216 | { | |||
8217 | case svSingleRef: | |||
8218 | case svDoubleRef: | |||
8219 | case svError: | |||
8220 | PushTokenRef( xTok); | |||
8221 | // success! | |||
8222 | return; | |||
8223 | default: | |||
8224 | ; // nothing | |||
8225 | } | |||
8226 | } | |||
8227 | while (false); | |||
8228 | } | |||
8229 | ||||
8230 | PushError( FormulaError::NoRef); | |||
8231 | } | |||
8232 | } | |||
8233 | ||||
8234 | void ScInterpreter::ScAddressFunc() | |||
8235 | { | |||
8236 | OUString sTabStr; | |||
8237 | ||||
8238 | sal_uInt8 nParamCount = GetByte(); | |||
8239 | if( !MustHaveParamCount( nParamCount, 2, 5 ) ) | |||
8240 | return; | |||
8241 | ||||
8242 | if( nParamCount >= 5 ) | |||
8243 | sTabStr = GetString().getString(); | |||
8244 | ||||
8245 | FormulaGrammar::AddressConvention eConv = FormulaGrammar::CONV_OOO; // default | |||
8246 | if (nParamCount >= 4 && 0.0 == GetDoubleWithDefault( 1.0)) | |||
8247 | eConv = FormulaGrammar::CONV_XL_R1C1; | |||
8248 | else | |||
8249 | { | |||
8250 | // If A1 syntax is requested then the actual sheet separator and format | |||
8251 | // convention depends on the syntax configured for INDIRECT to match | |||
8252 | // that, and if it is unspecified then the document's address syntax. | |||
8253 | FormulaGrammar::AddressConvention eForceConv = maCalcConfig.meStringRefAddressSyntax; | |||
8254 | if (eForceConv == FormulaGrammar::CONV_UNSPECIFIED) | |||
8255 | eForceConv = mrDoc.GetAddressConvention(); | |||
8256 | if (eForceConv == FormulaGrammar::CONV_XL_A1 || eForceConv == FormulaGrammar::CONV_XL_R1C1) | |||
8257 | eConv = FormulaGrammar::CONV_XL_A1; // for anything Excel use Excel A1 | |||
8258 | } | |||
8259 | ||||
8260 | ScRefFlags nFlags = ScRefFlags::COL_ABS | ScRefFlags::ROW_ABS; // default | |||
8261 | if( nParamCount >= 3 ) | |||
8262 | { | |||
8263 | sal_Int32 n = GetInt32WithDefault(1); | |||
8264 | switch ( n ) | |||
8265 | { | |||
8266 | default : | |||
8267 | PushNoValue(); | |||
8268 | return; | |||
8269 | ||||
8270 | case 5: | |||
8271 | case 1 : break; // default | |||
8272 | case 6: | |||
8273 | case 2 : nFlags = ScRefFlags::ROW_ABS; break; | |||
8274 | case 7: | |||
8275 | case 3 : nFlags = ScRefFlags::COL_ABS; break; | |||
8276 | case 8: | |||
8277 | case 4 : nFlags = ScRefFlags::ZERO; break; // both relative | |||
8278 | } | |||
8279 | } | |||
8280 | nFlags |= ScRefFlags::VALID | ScRefFlags::ROW_VALID | ScRefFlags::COL_VALID; | |||
8281 | ||||
8282 | SCCOL nCol = static_cast<SCCOL>(GetInt16()); | |||
8283 | SCROW nRow = static_cast<SCROW>(GetInt32()); | |||
8284 | if( eConv == FormulaGrammar::CONV_XL_R1C1 ) | |||
8285 | { | |||
8286 | // YUCK! The XL interface actually treats rel R1C1 refs differently | |||
8287 | // than A1 | |||
8288 | if( !(nFlags & ScRefFlags::COL_ABS) ) | |||
8289 | nCol += aPos.Col() + 1; | |||
8290 | if( !(nFlags & ScRefFlags::ROW_ABS) ) | |||
8291 | nRow += aPos.Row() + 1; | |||
8292 | } | |||
8293 | ||||
8294 | --nCol; | |||
8295 | --nRow; | |||
8296 | if (nGlobalError != FormulaError::NONE || !mrDoc.ValidCol( nCol) || !mrDoc.ValidRow( nRow)) | |||
8297 | { | |||
8298 | PushIllegalArgument(); | |||
8299 | return; | |||
8300 | } | |||
8301 | ||||
8302 | const ScAddress::Details aDetails( eConv, aPos ); | |||
8303 | const ScAddress aAdr( nCol, nRow, 0); | |||
8304 | OUString aRefStr(aAdr.Format(nFlags, &mrDoc, aDetails)); | |||
8305 | ||||
8306 | if( nParamCount >= 5 && !sTabStr.isEmpty() ) | |||
8307 | { | |||
8308 | OUString aDoc; | |||
8309 | if (eConv == FormulaGrammar::CONV_OOO) | |||
8310 | { | |||
8311 | // Isolate Tab from 'Doc'#Tab | |||
8312 | sal_Int32 nPos = ScCompiler::GetDocTabPos( sTabStr); | |||
8313 | if (nPos != -1) | |||
8314 | { | |||
8315 | if (sTabStr[nPos+1] == '$') | |||
8316 | ++nPos; // also split 'Doc'#$Tab | |||
8317 | aDoc = sTabStr.copy( 0, nPos+1); | |||
8318 | sTabStr = sTabStr.copy( nPos+1); | |||
8319 | } | |||
8320 | } | |||
8321 | /* TODO: yet unsupported external reference in CONV_XL_R1C1 syntax may | |||
8322 | * need some extra handling to isolate Tab from Doc. */ | |||
8323 | if (sTabStr[0] != '\'' || !sTabStr.endsWith("'")) | |||
8324 | ScCompiler::CheckTabQuotes( sTabStr, eConv); | |||
8325 | if (!aDoc.isEmpty()) | |||
8326 | sTabStr = aDoc + sTabStr; | |||
8327 | sTabStr += (eConv == FormulaGrammar::CONV_XL_R1C1 || eConv == FormulaGrammar::CONV_XL_A1) ? | |||
8328 | OUStringLiteral(u"!") : OUStringLiteral(u"."); | |||
8329 | sTabStr += aRefStr; | |||
8330 | PushString( sTabStr ); | |||
8331 | } | |||
8332 | else | |||
8333 | PushString( aRefStr ); | |||
8334 | } | |||
8335 | ||||
8336 | void ScInterpreter::ScOffset() | |||
8337 | { | |||
8338 | sal_uInt8 nParamCount = GetByte(); | |||
8339 | if ( !MustHaveParamCount( nParamCount, 3, 5 ) ) | |||
8340 | return; | |||
8341 | ||||
8342 | bool bNewWidth = false; | |||
8343 | bool bNewHeight = false; | |||
8344 | sal_Int32 nColNew = 1, nRowNew = 1; | |||
8345 | if (nParamCount == 5) | |||
8346 | { | |||
8347 | if (IsMissing()) | |||
8348 | PopError(); | |||
8349 | else | |||
8350 | { | |||
8351 | nColNew = GetInt32(); | |||
8352 | bNewWidth = true; | |||
8353 | } | |||
8354 | } | |||
8355 | if (nParamCount >= 4) | |||
8356 | { | |||
8357 | if (IsMissing()) | |||
8358 | PopError(); | |||
8359 | else | |||
8360 | { | |||
8361 | nRowNew = GetInt32(); | |||
8362 | bNewHeight = true; | |||
8363 | } | |||
8364 | } | |||
8365 | sal_Int32 nColPlus = GetInt32(); | |||
8366 | sal_Int32 nRowPlus = GetInt32(); | |||
8367 | if (nGlobalError != FormulaError::NONE) | |||
8368 | { | |||
8369 | PushError( nGlobalError); | |||
8370 | return; | |||
8371 | } | |||
8372 | if (nColNew <= 0 || nRowNew <= 0) | |||
8373 | { | |||
8374 | PushIllegalArgument(); | |||
8375 | return; | |||
8376 | } | |||
8377 | SCCOL nCol1(0); | |||
8378 | SCROW nRow1(0); | |||
8379 | SCTAB nTab1(0); | |||
8380 | SCCOL nCol2(0); | |||
8381 | SCROW nRow2(0); | |||
8382 | SCTAB nTab2(0); | |||
8383 | switch (GetStackType()) | |||
8384 | { | |||
8385 | case svSingleRef: | |||
8386 | { | |||
8387 | PopSingleRef(nCol1, nRow1, nTab1); | |||
8388 | if (!bNewWidth && !bNewHeight) | |||
8389 | { | |||
8390 | nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1) + nColPlus); | |||
8391 | nRow1 = static_cast<SCROW>(static_cast<long>(nRow1) + nRowPlus); | |||
8392 | if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1)) | |||
8393 | PushIllegalArgument(); | |||
8394 | else | |||
8395 | PushSingleRef(nCol1, nRow1, nTab1); | |||
8396 | } | |||
8397 | else | |||
8398 | { | |||
8399 | nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColPlus); | |||
8400 | nRow1 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowPlus); | |||
8401 | nCol2 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColNew-1); | |||
8402 | nRow2 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowNew-1); | |||
8403 | if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) || | |||
8404 | !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2)) | |||
8405 | PushIllegalArgument(); | |||
8406 | else | |||
8407 | PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); | |||
8408 | } | |||
8409 | break; | |||
8410 | } | |||
8411 | case svExternalSingleRef: | |||
8412 | { | |||
8413 | sal_uInt16 nFileId; | |||
8414 | OUString aTabName; | |||
8415 | ScSingleRefData aRef; | |||
8416 | PopExternalSingleRef(nFileId, aTabName, aRef); | |||
8417 | ScAddress aAbsRef = aRef.toAbs(mrDoc, aPos); | |||
8418 | nCol1 = aAbsRef.Col(); | |||
8419 | nRow1 = aAbsRef.Row(); | |||
8420 | nTab1 = aAbsRef.Tab(); | |||
8421 | ||||
8422 | if (!bNewWidth && !bNewHeight) | |||
8423 | { | |||
8424 | nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1) + nColPlus); | |||
8425 | nRow1 = static_cast<SCROW>(static_cast<long>(nRow1) + nRowPlus); | |||
8426 | if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1)) | |||
8427 | PushIllegalArgument(); | |||
8428 | else | |||
8429 | PushExternalSingleRef(nFileId, aTabName, nCol1, nRow1, nTab1); | |||
8430 | } | |||
8431 | else | |||
8432 | { | |||
8433 | nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColPlus); | |||
8434 | nRow1 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowPlus); | |||
8435 | nCol2 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColNew-1); | |||
8436 | nRow2 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowNew-1); | |||
8437 | nTab2 = nTab1; | |||
8438 | if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) || | |||
8439 | !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2)) | |||
8440 | PushIllegalArgument(); | |||
8441 | else | |||
8442 | PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
8443 | } | |||
8444 | break; | |||
8445 | } | |||
8446 | case svDoubleRef: | |||
8447 | { | |||
8448 | PopDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
8449 | if (!bNewWidth) | |||
8450 | nColNew = nCol2 - nCol1 + 1; | |||
8451 | if (!bNewHeight) | |||
8452 | nRowNew = nRow2 - nRow1 + 1; | |||
8453 | nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColPlus); | |||
8454 | nRow1 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowPlus); | |||
8455 | nCol2 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColNew-1); | |||
8456 | nRow2 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowNew-1); | |||
8457 | if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) || | |||
8458 | !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2) | |||
8459 | PushIllegalArgument(); | |||
8460 | else | |||
8461 | PushDoubleRef(nCol1, nRow1, nTab1, nCol2, nRow2, nTab1); | |||
8462 | break; | |||
8463 | } | |||
8464 | case svExternalDoubleRef: | |||
8465 | { | |||
8466 | sal_uInt16 nFileId; | |||
8467 | OUString aTabName; | |||
8468 | ScComplexRefData aRef; | |||
8469 | PopExternalDoubleRef(nFileId, aTabName, aRef); | |||
8470 | ScRange aAbs = aRef.toAbs(mrDoc, aPos); | |||
8471 | nCol1 = aAbs.aStart.Col(); | |||
8472 | nRow1 = aAbs.aStart.Row(); | |||
8473 | nTab1 = aAbs.aStart.Tab(); | |||
8474 | nCol2 = aAbs.aEnd.Col(); | |||
8475 | nRow2 = aAbs.aEnd.Row(); | |||
8476 | nTab2 = aAbs.aEnd.Tab(); | |||
8477 | if (!bNewWidth) | |||
8478 | nColNew = nCol2 - nCol1 + 1; | |||
8479 | if (!bNewHeight) | |||
8480 | nRowNew = nRow2 - nRow1 + 1; | |||
8481 | nCol1 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColPlus); | |||
8482 | nRow1 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowPlus); | |||
8483 | nCol2 = static_cast<SCCOL>(static_cast<long>(nCol1)+nColNew-1); | |||
8484 | nRow2 = static_cast<SCROW>(static_cast<long>(nRow1)+nRowNew-1); | |||
8485 | if (!mrDoc.ValidCol(nCol1) || !mrDoc.ValidRow(nRow1) || | |||
8486 | !mrDoc.ValidCol(nCol2) || !mrDoc.ValidRow(nRow2) || nTab1 != nTab2) | |||
8487 | PushIllegalArgument(); | |||
8488 | else | |||
8489 | PushExternalDoubleRef(nFileId, aTabName, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
8490 | break; | |||
8491 | } | |||
8492 | default: | |||
8493 | PushIllegalParameter(); | |||
8494 | break; | |||
8495 | } // end switch | |||
8496 | } | |||
8497 | ||||
8498 | void ScInterpreter::ScIndex() | |||
8499 | { | |||
8500 | sal_uInt8 nParamCount = GetByte(); | |||
8501 | if ( !MustHaveParamCount( nParamCount, 1, 4 ) ) | |||
8502 | return; | |||
8503 | ||||
8504 | sal_uInt32 nArea; | |||
8505 | size_t nAreaCount; | |||
8506 | SCCOL nCol; | |||
8507 | SCROW nRow; | |||
8508 | if (nParamCount == 4) | |||
8509 | nArea = GetUInt32(); | |||
8510 | else | |||
8511 | nArea = 1; | |||
8512 | if (nParamCount >= 3) | |||
8513 | nCol = static_cast<SCCOL>(GetInt16()); | |||
8514 | else | |||
8515 | nCol = 0; | |||
8516 | if (nParamCount >= 2) | |||
8517 | nRow = static_cast<SCROW>(GetInt32()); | |||
8518 | else | |||
8519 | nRow = 0; | |||
8520 | if (GetStackType() == svRefList) | |||
8521 | nAreaCount = (sp ? pStack[sp-1]->GetRefList()->size() : 0); | |||
8522 | else | |||
8523 | nAreaCount = 1; // one reference or array or whatever | |||
8524 | if (nGlobalError != FormulaError::NONE || nAreaCount == 0 || static_cast<size_t>(nArea) > nAreaCount) | |||
8525 | { | |||
8526 | PushError( FormulaError::NoRef); | |||
8527 | return; | |||
8528 | } | |||
8529 | else if (nArea < 1 || nCol < 0 || nRow < 0) | |||
8530 | { | |||
8531 | PushIllegalArgument(); | |||
8532 | return; | |||
8533 | } | |||
8534 | switch (GetStackType()) | |||
8535 | { | |||
8536 | case svMatrix: | |||
8537 | case svExternalSingleRef: | |||
8538 | case svExternalDoubleRef: | |||
8539 | { | |||
8540 | if (nArea != 1) | |||
8541 | SetError(FormulaError::IllegalArgument); | |||
8542 | sal_uInt16 nOldSp = sp; | |||
8543 | ScMatrixRef pMat = GetMatrix(); | |||
8544 | if (pMat) | |||
8545 | { | |||
8546 | SCSIZE nC, nR; | |||
8547 | pMat->GetDimensions(nC, nR); | |||
8548 | // Access one element of a vector independent of col/row | |||
8549 | // orientation? | |||
8550 | bool bVector = ((nCol == 0 || nRow == 0) && (nC == 1 || nR == 1)); | |||
8551 | SCSIZE nElement = ::std::max( static_cast<SCSIZE>(nCol), | |||
8552 | static_cast<SCSIZE>(nRow)); | |||
8553 | if (nC == 0 || nR == 0 || | |||
8554 | (!bVector && (o3tl::make_unsigned(nCol) > nC || | |||
8555 | o3tl::make_unsigned(nRow) > nR)) || | |||
8556 | (bVector && nElement > nC * nR)) | |||
8557 | PushIllegalArgument(); | |||
8558 | else if (nCol == 0 && nRow == 0) | |||
8559 | sp = nOldSp; | |||
8560 | else if (bVector) | |||
8561 | { | |||
8562 | --nElement; | |||
8563 | if (pMat->IsStringOrEmpty( nElement)) | |||
8564 | PushString( pMat->GetString(nElement).getString()); | |||
8565 | else | |||
8566 | PushDouble( pMat->GetDouble( nElement)); | |||
8567 | } | |||
8568 | else if (nCol == 0) | |||
8569 | { | |||
8570 | ScMatrixRef pResMat = GetNewMat(nC, 1); | |||
8571 | if (pResMat) | |||
8572 | { | |||
8573 | SCSIZE nRowMinus1 = static_cast<SCSIZE>(nRow - 1); | |||
8574 | for (SCSIZE i = 0; i < nC; i++) | |||
8575 | if (!pMat->IsStringOrEmpty(i, nRowMinus1)) | |||
8576 | pResMat->PutDouble(pMat->GetDouble(i, | |||
8577 | nRowMinus1), i, 0); | |||
8578 | else | |||
8579 | pResMat->PutString(pMat->GetString(i, nRowMinus1), i, 0); | |||
8580 | ||||
8581 | PushMatrix(pResMat); | |||
8582 | } | |||
8583 | else | |||
8584 | PushIllegalArgument(); | |||
8585 | } | |||
8586 | else if (nRow == 0) | |||
8587 | { | |||
8588 | ScMatrixRef pResMat = GetNewMat(1, nR); | |||
8589 | if (pResMat) | |||
8590 | { | |||
8591 | SCSIZE nColMinus1 = static_cast<SCSIZE>(nCol - 1); | |||
8592 | for (SCSIZE i = 0; i < nR; i++) | |||
8593 | if (!pMat->IsStringOrEmpty(nColMinus1, i)) | |||
8594 | pResMat->PutDouble(pMat->GetDouble(nColMinus1, | |||
8595 | i), i); | |||
8596 | else | |||
8597 | pResMat->PutString(pMat->GetString(nColMinus1, i), i); | |||
8598 | PushMatrix(pResMat); | |||
8599 | } | |||
8600 | else | |||
8601 | PushIllegalArgument(); | |||
8602 | } | |||
8603 | else | |||
8604 | { | |||
8605 | if (!pMat->IsStringOrEmpty( static_cast<SCSIZE>(nCol-1), | |||
8606 | static_cast<SCSIZE>(nRow-1))) | |||
8607 | PushDouble( pMat->GetDouble( | |||
8608 | static_cast<SCSIZE>(nCol-1), | |||
8609 | static_cast<SCSIZE>(nRow-1))); | |||
8610 | else | |||
8611 | PushString( pMat->GetString( | |||
8612 | static_cast<SCSIZE>(nCol-1), | |||
8613 | static_cast<SCSIZE>(nRow-1)).getString()); | |||
8614 | } | |||
8615 | } | |||
8616 | } | |||
8617 | break; | |||
8618 | case svSingleRef: | |||
8619 | { | |||
8620 | SCCOL nCol1 = 0; | |||
8621 | SCROW nRow1 = 0; | |||
8622 | SCTAB nTab1 = 0; | |||
8623 | PopSingleRef( nCol1, nRow1, nTab1); | |||
8624 | if (nCol > 1 || nRow > 1) | |||
8625 | PushIllegalArgument(); | |||
8626 | else | |||
8627 | PushSingleRef( nCol1, nRow1, nTab1); | |||
8628 | } | |||
8629 | break; | |||
8630 | case svDoubleRef: | |||
8631 | case svRefList: | |||
8632 | { | |||
8633 | SCCOL nCol1 = 0; | |||
8634 | SCROW nRow1 = 0; | |||
8635 | SCTAB nTab1 = 0; | |||
8636 | SCCOL nCol2 = 0; | |||
8637 | SCROW nRow2 = 0; | |||
8638 | SCTAB nTab2 = 0; | |||
8639 | bool bRowArray = false; | |||
8640 | if (GetStackType() == svRefList) | |||
8641 | { | |||
8642 | FormulaConstTokenRef xRef = PopToken(); | |||
8643 | if (nGlobalError != FormulaError::NONE || !xRef) | |||
8644 | { | |||
8645 | PushIllegalParameter(); | |||
8646 | return; | |||
8647 | } | |||
8648 | ScRange aRange( ScAddress::UNINITIALIZED); | |||
8649 | DoubleRefToRange( (*(xRef->GetRefList()))[nArea-1], aRange); | |||
8650 | aRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
8651 | if ( nParamCount == 2 && nRow1 == nRow2 ) | |||
8652 | bRowArray = true; | |||
8653 | } | |||
8654 | else | |||
8655 | { | |||
8656 | PopDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); | |||
8657 | if ( nParamCount == 2 && nRow1 == nRow2 ) | |||
8658 | bRowArray = true; | |||
8659 | } | |||
8660 | if ( nTab1 != nTab2 || | |||
8661 | (nCol > 0 && nCol1+nCol-1 > nCol2) || | |||
8662 | (nRow > 0 && nRow1+nRow-1 > nRow2 && !bRowArray ) || | |||
8663 | ( nRow > nCol2 - nCol1 + 1 && bRowArray )) | |||
8664 | PushIllegalArgument(); | |||
8665 | else if (nCol == 0 && nRow == 0) | |||
8666 | { | |||
8667 | if ( nCol1 == nCol2 && nRow1 == nRow2 ) | |||
8668 | PushSingleRef( nCol1, nRow1, nTab1 ); | |||
8669 | else | |||
8670 | PushDoubleRef( nCol1, nRow1, nTab1, nCol2, nRow2, nTab1 ); | |||
8671 | } | |||
8672 | else if (nRow == 0) | |||
8673 | { | |||
8674 | if ( nRow1 == nRow2 ) | |||
8675 | PushSingleRef( nCol1+nCol-1, nRow1, nTab1 ); | |||
8676 | else | |||
8677 | PushDoubleRef( nCol1+nCol-1, nRow1, nTab1, | |||
8678 | nCol1+nCol-1, nRow2, nTab1 ); | |||
8679 | } | |||
8680 | else if (nCol == 0) | |||
8681 | { | |||
8682 | if ( nCol1 == nCol2 ) | |||
8683 | PushSingleRef( nCol1, nRow1+nRow-1, nTab1 ); | |||
8684 | else if ( bRowArray ) | |||
8685 | { | |||
8686 | nCol =static_cast<SCCOL>(nRow); | |||
8687 | nRow = 1; | |||
8688 | PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); | |||
8689 | } | |||
8690 | else | |||
8691 | PushDoubleRef( nCol1, nRow1+nRow-1, nTab1, | |||
8692 | nCol2, nRow1+nRow-1, nTab1); | |||
8693 | } | |||
8694 | else | |||
8695 | PushSingleRef( nCol1+nCol-1, nRow1+nRow-1, nTab1); | |||
8696 | } | |||
8697 | break; | |||
8698 | default: | |||
8699 | PushIllegalParameter(); | |||
8700 | } | |||
8701 | } | |||
8702 | ||||
8703 | void ScInterpreter::ScMultiArea() | |||
8704 | { | |||
8705 | // Legacy support, convert to RefList | |||
8706 | sal_uInt8 nParamCount = GetByte(); | |||
8707 | if (MustHaveParamCountMin( nParamCount, 1)) | |||
8708 | { | |||
8709 | while (nGlobalError == FormulaError::NONE && nParamCount-- > 1) | |||
8710 | { | |||
8711 | ScUnionFunc(); | |||
8712 | } | |||
8713 | } | |||
8714 | } | |||
8715 | ||||
8716 | void ScInterpreter::ScAreas() | |||
8717 | { | |||
8718 | sal_uInt8 nParamCount = GetByte(); | |||
8719 | if (!MustHaveParamCount( nParamCount, 1)) | |||
8720 | return; | |||
8721 | ||||
8722 | size_t nCount = 0; | |||
8723 | switch (GetStackType()) | |||
8724 | { | |||
8725 | case svSingleRef: | |||
8726 | { | |||
8727 | FormulaConstTokenRef xT = PopToken(); | |||
8728 | ValidateRef( *xT->GetSingleRef()); | |||
8729 | ++nCount; | |||
8730 | } | |||
8731 | break; | |||
8732 | case svDoubleRef: | |||
8733 | { | |||
8734 | FormulaConstTokenRef xT = PopToken(); | |||
8735 | ValidateRef( *xT->GetDoubleRef()); | |||
8736 | ++nCount; | |||
8737 | } | |||
8738 | break; | |||
8739 | case svRefList: | |||
8740 | { | |||
8741 | FormulaConstTokenRef xT = PopToken(); | |||
8742 | ValidateRef( *(xT->GetRefList())); | |||
8743 | nCount += xT->GetRefList()->size(); | |||
8744 | } | |||
8745 | break; | |||
8746 | default: | |||
8747 | SetError( FormulaError::IllegalParameter); | |||
8748 | } | |||
8749 | PushDouble( double(nCount)); | |||
8750 | } | |||
8751 | ||||
8752 | void ScInterpreter::ScCurrency() | |||
8753 | { | |||
8754 | sal_uInt8 nParamCount = GetByte(); | |||
8755 | if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) | |||
8756 | return; | |||
8757 | ||||
8758 | OUString aStr; | |||
8759 | double fDec; | |||
8760 | if (nParamCount == 2) | |||
8761 | { | |||
8762 | fDec = ::rtl::math::approxFloor(GetDouble()); | |||
8763 | if (fDec < -15.0 || fDec > 15.0) | |||
8764 | { | |||
8765 | PushIllegalArgument(); | |||
8766 | return; | |||
8767 | } | |||
8768 | } | |||
8769 | else | |||
8770 | fDec = 2.0; | |||
8771 | double fVal = GetDouble(); | |||
8772 | double fFac; | |||
8773 | if ( fDec != 0.0 ) | |||
8774 | fFac = pow( double(10), fDec ); | |||
8775 | else | |||
8776 | fFac = 1.0; | |||
8777 | if (fVal < 0.0) | |||
8778 | fVal = ceil(fVal*fFac-0.5)/fFac; | |||
8779 | else | |||
8780 | fVal = floor(fVal*fFac+0.5)/fFac; | |||
8781 | const Color* pColor = nullptr; | |||
8782 | if ( fDec < 0.0 ) | |||
8783 | fDec = 0.0; | |||
8784 | sal_uLong nIndex = pFormatter->GetStandardFormat( | |||
8785 | SvNumFormatType::CURRENCY, | |||
8786 | ScGlobal::eLnge); | |||
8787 | if ( static_cast<sal_uInt16>(fDec) != pFormatter->GetFormatPrecision( nIndex ) ) | |||
8788 | { | |||
8789 | OUString sFormatString = pFormatter->GenerateFormat( | |||
8790 | nIndex, | |||
8791 | ScGlobal::eLnge, | |||
8792 | true, // with thousands separator | |||
8793 | false, // not red | |||
8794 | static_cast<sal_uInt16>(fDec));// decimal places | |||
8795 | if (!pFormatter->GetPreviewString(sFormatString, | |||
8796 | fVal, | |||
8797 | aStr, | |||
8798 | &pColor, | |||
8799 | ScGlobal::eLnge)) | |||
8800 | SetError(FormulaError::IllegalArgument); | |||
8801 | } | |||
8802 | else | |||
8803 | { | |||
8804 | pFormatter->GetOutputString(fVal, nIndex, aStr, &pColor); | |||
8805 | } | |||
8806 | PushString(aStr); | |||
8807 | } | |||
8808 | ||||
8809 | void ScInterpreter::ScReplace() | |||
8810 | { | |||
8811 | if ( !MustHaveParamCount( GetByte(), 4 ) ) | |||
8812 | return; | |||
8813 | ||||
8814 | OUString aNewStr = GetString().getString(); | |||
8815 | sal_Int32 nCount = GetStringPositionArgument(); | |||
8816 | sal_Int32 nPos = GetStringPositionArgument(); | |||
8817 | OUString aOldStr = GetString().getString(); | |||
8818 | if (nPos < 1 || nCount < 0) | |||
8819 | PushIllegalArgument(); | |||
8820 | else | |||
8821 | { | |||
8822 | sal_Int32 nLen = aOldStr.getLength(); | |||
8823 | if (nPos > nLen + 1) | |||
8824 | nPos = nLen + 1; | |||
8825 | if (nCount > nLen - nPos + 1) | |||
8826 | nCount = nLen - nPos + 1; | |||
8827 | sal_Int32 nIdx = 0; | |||
8828 | sal_Int32 nCnt = 0; | |||
8829 | while ( nIdx < nLen && nPos > nCnt + 1 ) | |||
8830 | { | |||
8831 | aOldStr.iterateCodePoints( &nIdx ); | |||
8832 | ++nCnt; | |||
8833 | } | |||
8834 | sal_Int32 nStart = nIdx; | |||
8835 | while ( nIdx < nLen && nPos + nCount - 1 > nCnt ) | |||
8836 | { | |||
8837 | aOldStr.iterateCodePoints( &nIdx ); | |||
8838 | ++nCnt; | |||
8839 | } | |||
8840 | aOldStr = aOldStr.replaceAt( nStart, nIdx - nStart, "" ); | |||
8841 | if ( CheckStringResultLen( aOldStr, aNewStr ) ) | |||
8842 | aOldStr = aOldStr.replaceAt( nStart, 0, aNewStr ); | |||
8843 | PushString( aOldStr ); | |||
8844 | } | |||
8845 | } | |||
8846 | ||||
8847 | void ScInterpreter::ScFixed() | |||
8848 | { | |||
8849 | sal_uInt8 nParamCount = GetByte(); | |||
8850 | if ( !MustHaveParamCount( nParamCount, 1, 3 ) ) | |||
8851 | return; | |||
8852 | ||||
8853 | OUString aStr; | |||
8854 | double fDec; | |||
8855 | bool bThousand; | |||
8856 | if (nParamCount == 3) | |||
8857 | bThousand = !GetBool(); // Param true: no thousands separator | |||
8858 | else | |||
8859 | bThousand = true; | |||
8860 | if (nParamCount >= 2) | |||
8861 | { | |||
8862 | fDec = ::rtl::math::approxFloor(GetDoubleWithDefault( 2.0 )); | |||
8863 | if (fDec < -15.0 || fDec > 15.0) | |||
8864 | { | |||
8865 | PushIllegalArgument(); | |||
8866 | return; | |||
8867 | } | |||
8868 | } | |||
8869 | else | |||
8870 | fDec = 2.0; | |||
8871 | double fVal = GetDouble(); | |||
8872 | double fFac; | |||
8873 | if ( fDec != 0.0 ) | |||
8874 | fFac = pow( double(10), fDec ); | |||
8875 | else | |||
8876 | fFac = 1.0; | |||
8877 | if (fVal < 0.0) | |||
8878 | fVal = ceil(fVal*fFac-0.5)/fFac; | |||
8879 | else | |||
8880 | fVal = floor(fVal*fFac+0.5)/fFac; | |||
8881 | const Color* pColor = nullptr; | |||
8882 | if (fDec < 0.0) | |||
8883 | fDec = 0.0; | |||
8884 | sal_uLong nIndex = pFormatter->GetStandardFormat( | |||
8885 | SvNumFormatType::NUMBER, | |||
8886 | ScGlobal::eLnge); | |||
8887 | OUString sFormatString = pFormatter->GenerateFormat( | |||
8888 | nIndex, | |||
8889 | ScGlobal::eLnge, | |||
8890 | bThousand, // with thousands separator | |||
8891 | false, // not red | |||
8892 | static_cast<sal_uInt16>(fDec));// decimal places | |||
8893 | if (!pFormatter->GetPreviewString(sFormatString, | |||
8894 | fVal, | |||
8895 | aStr, | |||
8896 | &pColor, | |||
8897 | ScGlobal::eLnge)) | |||
8898 | PushIllegalArgument(); | |||
8899 | else | |||
8900 | PushString(aStr); | |||
8901 | } | |||
8902 | ||||
8903 | void ScInterpreter::ScFind() | |||
8904 | { | |||
8905 | sal_uInt8 nParamCount = GetByte(); | |||
8906 | if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) | |||
8907 | return; | |||
8908 | ||||
8909 | sal_Int32 nCnt; | |||
8910 | if (nParamCount == 3) | |||
8911 | nCnt = GetDouble(); | |||
8912 | else | |||
8913 | nCnt = 1; | |||
8914 | OUString sStr = GetString().getString(); | |||
8915 | if (nCnt < 1 || nCnt > sStr.getLength()) | |||
8916 | PushNoValue(); | |||
8917 | else | |||
8918 | { | |||
8919 | sal_Int32 nPos = sStr.indexOf(GetString().getString(), nCnt - 1); | |||
8920 | if (nPos == -1) | |||
8921 | PushNoValue(); | |||
8922 | else | |||
8923 | { | |||
8924 | sal_Int32 nIdx = 0; | |||
8925 | nCnt = 0; | |||
8926 | while ( nIdx <= nPos ) | |||
8927 | { | |||
8928 | sStr.iterateCodePoints( &nIdx ); | |||
8929 | ++nCnt; | |||
8930 | } | |||
8931 | PushDouble( static_cast<double>(nCnt) ); | |||
8932 | } | |||
8933 | } | |||
8934 | } | |||
8935 | ||||
8936 | void ScInterpreter::ScExact() | |||
8937 | { | |||
8938 | nFuncFmtType = SvNumFormatType::LOGICAL; | |||
8939 | if ( MustHaveParamCount( GetByte(), 2 ) ) | |||
8940 | { | |||
8941 | svl::SharedString s1 = GetString(); | |||
8942 | svl::SharedString s2 = GetString(); | |||
8943 | PushInt( int(s1.getData() == s2.getData()) ); | |||
8944 | } | |||
8945 | } | |||
8946 | ||||
8947 | void ScInterpreter::ScLeft() | |||
8948 | { | |||
8949 | sal_uInt8 nParamCount = GetByte(); | |||
8950 | if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) | |||
8951 | return; | |||
8952 | ||||
8953 | sal_Int32 n; | |||
8954 | if (nParamCount == 2) | |||
8955 | { | |||
8956 | n = GetStringPositionArgument(); | |||
8957 | if (n < 0) | |||
8958 | { | |||
8959 | PushIllegalArgument(); | |||
8960 | return ; | |||
8961 | } | |||
8962 | } | |||
8963 | else | |||
8964 | n = 1; | |||
8965 | OUString aStr = GetString().getString(); | |||
8966 | sal_Int32 nIdx = 0; | |||
8967 | sal_Int32 nCnt = 0; | |||
8968 | while ( nIdx < aStr.getLength() && n > nCnt++ ) | |||
8969 | aStr.iterateCodePoints( &nIdx ); | |||
8970 | aStr = aStr.copy( 0, nIdx ); | |||
8971 | PushString( aStr ); | |||
8972 | } | |||
8973 | ||||
8974 | namespace { | |||
8975 | ||||
8976 | struct UBlockScript { | |||
8977 | UBlockCode from; | |||
8978 | UBlockCode to; | |||
8979 | }; | |||
8980 | ||||
8981 | } | |||
8982 | ||||
8983 | const UBlockScript scriptList[] = { | |||
8984 | {UBLOCK_HANGUL_JAMO, UBLOCK_HANGUL_JAMO}, | |||
8985 | {UBLOCK_CJK_RADICALS_SUPPLEMENT, UBLOCK_HANGUL_SYLLABLES}, | |||
8986 | {UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS,UBLOCK_CJK_RADICALS_SUPPLEMENT }, | |||
8987 | {UBLOCK_IDEOGRAPHIC_DESCRIPTION_CHARACTERS,UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS}, | |||
8988 | {UBLOCK_CJK_COMPATIBILITY_FORMS, UBLOCK_CJK_COMPATIBILITY_FORMS}, | |||
8989 | {UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS, UBLOCK_HALFWIDTH_AND_FULLWIDTH_FORMS}, | |||
8990 | {UBLOCK_CJK_UNIFIED_IDEOGRAPHS_EXTENSION_B, UBLOCK_CJK_COMPATIBILITY_IDEOGRAPHS_SUPPLEMENT}, | |||
8991 | {UBLOCK_CJK_STROKES, UBLOCK_CJK_STROKES} | |||
8992 | }; | |||
8993 | static bool IsDBCS(sal_Unicode currentChar) | |||
8994 | { | |||
8995 | // for the locale of ja-JP, character U+0x005c and U+0x20ac should be ScriptType::Asian | |||
8996 | if( (currentChar == 0x005c || currentChar == 0x20ac) && | |||
8997 | (MsLangId::getSystemLanguage() == LANGUAGE_JAPANESELanguageType(0x0411)) ) | |||
8998 | return true; | |||
8999 | sal_uInt16 i; | |||
9000 | bool bRet = false; | |||
9001 | UBlockCode block = ublock_getCodeublock_getCode_67(currentChar); | |||
9002 | for ( i = 0; i < SAL_N_ELEMENTS(scriptList)(sizeof(sal_n_array_size(scriptList))); i++) { | |||
9003 | if (block <= scriptList[i].to) break; | |||
9004 | } | |||
9005 | bRet = (i < SAL_N_ELEMENTS(scriptList)(sizeof(sal_n_array_size(scriptList))) && block >= scriptList[i].from); | |||
9006 | return bRet; | |||
9007 | } | |||
9008 | static sal_Int32 lcl_getLengthB( const OUString &str, sal_Int32 nPos ) | |||
9009 | { | |||
9010 | sal_Int32 index = 0; | |||
9011 | sal_Int32 length = 0; | |||
9012 | while ( index < nPos ) | |||
9013 | { | |||
9014 | if (IsDBCS(str[index])) | |||
9015 | length += 2; | |||
9016 | else | |||
9017 | length++; | |||
9018 | index++; | |||
9019 | } | |||
9020 | return length; | |||
9021 | } | |||
9022 | static sal_Int32 getLengthB(const OUString &str) | |||
9023 | { | |||
9024 | if(str.isEmpty()) | |||
9025 | return 0; | |||
9026 | else | |||
9027 | return lcl_getLengthB( str, str.getLength() ); | |||
9028 | } | |||
9029 | void ScInterpreter::ScLenB() | |||
9030 | { | |||
9031 | PushDouble( getLengthB(GetString().getString()) ); | |||
9032 | } | |||
9033 | static OUString lcl_RightB(const OUString &rStr, sal_Int32 n) | |||
9034 | { | |||
9035 | if( n < getLengthB(rStr) ) | |||
9036 | { | |||
9037 | OUStringBuffer aBuf(rStr); | |||
9038 | sal_Int32 index = aBuf.getLength(); | |||
9039 | while(index-- >= 0) | |||
9040 | { | |||
9041 | if(0 == n) | |||
9042 | { | |||
9043 | aBuf.remove( 0, index + 1); | |||
9044 | break; | |||
9045 | } | |||
9046 | if(-1 == n) | |||
9047 | { | |||
9048 | aBuf.remove( 0, index + 2 ); | |||
9049 | aBuf.insert( 0, " "); | |||
9050 | break; | |||
9051 | } | |||
9052 | if(IsDBCS(aBuf[index])) | |||
9053 | n -= 2; | |||
9054 | else | |||
9055 | n--; | |||
9056 | } | |||
9057 | return aBuf.makeStringAndClear(); | |||
9058 | } | |||
9059 | return rStr; | |||
9060 | } | |||
9061 | void ScInterpreter::ScRightB() | |||
9062 | { | |||
9063 | sal_uInt8 nParamCount = GetByte(); | |||
9064 | if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) | |||
9065 | return; | |||
9066 | ||||
9067 | sal_Int32 n; | |||
9068 | if (nParamCount == 2) | |||
9069 | { | |||
9070 | n = GetStringPositionArgument(); | |||
9071 | if (n < 0) | |||
9072 | { | |||
9073 | PushIllegalArgument(); | |||
9074 | return ; | |||
9075 | } | |||
9076 | } | |||
9077 | else | |||
9078 | n = 1; | |||
9079 | OUString aStr(lcl_RightB(GetString().getString(), n)); | |||
9080 | PushString( aStr ); | |||
9081 | } | |||
9082 | static OUString lcl_LeftB(const OUString &rStr, sal_Int32 n) | |||
9083 | { | |||
9084 | if( n < getLengthB(rStr) ) | |||
9085 | { | |||
9086 | OUStringBuffer aBuf(rStr); | |||
9087 | sal_Int32 index = -1; | |||
9088 | while(index++ < aBuf.getLength()) | |||
9089 | { | |||
9090 | if(0 == n) | |||
9091 | { | |||
9092 | aBuf.truncate(index); | |||
9093 | break; | |||
9094 | } | |||
9095 | if(-1 == n) | |||
9096 | { | |||
9097 | aBuf.truncate( index - 1 ); | |||
9098 | aBuf.append(" "); | |||
9099 | break; | |||
9100 | } | |||
9101 | if(IsDBCS(aBuf[index])) | |||
9102 | n -= 2; | |||
9103 | else | |||
9104 | n--; | |||
9105 | } | |||
9106 | return aBuf.makeStringAndClear(); | |||
9107 | } | |||
9108 | return rStr; | |||
9109 | } | |||
9110 | void ScInterpreter::ScLeftB() | |||
9111 | { | |||
9112 | sal_uInt8 nParamCount = GetByte(); | |||
9113 | if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) | |||
9114 | return; | |||
9115 | ||||
9116 | sal_Int32 n; | |||
9117 | if (nParamCount == 2) | |||
9118 | { | |||
9119 | n = GetStringPositionArgument(); | |||
9120 | if (n < 0) | |||
9121 | { | |||
9122 | PushIllegalArgument(); | |||
9123 | return ; | |||
9124 | } | |||
9125 | } | |||
9126 | else | |||
9127 | n = 1; | |||
9128 | OUString aStr(lcl_LeftB(GetString().getString(), n)); | |||
9129 | PushString( aStr ); | |||
9130 | } | |||
9131 | void ScInterpreter::ScMidB() | |||
9132 | { | |||
9133 | if ( !MustHaveParamCount( GetByte(), 3 ) ) | |||
9134 | return; | |||
9135 | ||||
9136 | const sal_Int32 nCount = GetStringPositionArgument(); | |||
9137 | const sal_Int32 nStart = GetStringPositionArgument(); | |||
9138 | OUString aStr = GetString().getString(); | |||
9139 | if (nStart < 1 || nCount < 0) | |||
9140 | PushIllegalArgument(); | |||
9141 | else | |||
9142 | { | |||
9143 | ||||
9144 | aStr = lcl_LeftB(aStr, nStart + nCount - 1); | |||
9145 | sal_Int32 nCnt = getLengthB(aStr) - nStart + 1; | |||
9146 | aStr = lcl_RightB(aStr, std::max<sal_Int32>(nCnt,0)); | |||
9147 | PushString(aStr); | |||
9148 | } | |||
9149 | } | |||
9150 | ||||
9151 | void ScInterpreter::ScReplaceB() | |||
9152 | { | |||
9153 | if ( !MustHaveParamCount( GetByte(), 4 ) ) | |||
9154 | return; | |||
9155 | ||||
9156 | OUString aNewStr = GetString().getString(); | |||
9157 | const sal_Int32 nCount = GetStringPositionArgument(); | |||
9158 | const sal_Int32 nPos = GetStringPositionArgument(); | |||
9159 | OUString aOldStr = GetString().getString(); | |||
9160 | int nLen = getLengthB( aOldStr ); | |||
9161 | if (nPos < 1.0 || nPos > nLen || nCount < 0.0 || nPos + nCount -1 > nLen) | |||
9162 | PushIllegalArgument(); | |||
9163 | else | |||
9164 | { | |||
9165 | // REPLACEB(aOldStr;nPos;nCount;aNewStr) is the same as | |||
9166 | // LEFTB(aOldStr;nPos-1) & aNewStr & RIGHT(aOldStr;LENB(aOldStr)-(nPos - 1)-nCount) | |||
9167 | OUString aStr1 = lcl_LeftB( aOldStr, nPos - 1 ); | |||
9168 | OUString aStr3 = lcl_RightB( aOldStr, nLen - nPos - nCount + 1); | |||
9169 | ||||
9170 | PushString( aStr1 + aNewStr + aStr3 ); | |||
9171 | } | |||
9172 | } | |||
9173 | ||||
9174 | void ScInterpreter::ScFindB() | |||
9175 | { | |||
9176 | sal_uInt8 nParamCount = GetByte(); | |||
9177 | if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) | |||
9178 | return; | |||
9179 | ||||
9180 | sal_Int32 nStart; | |||
9181 | if ( nParamCount == 3 ) | |||
9182 | nStart = GetStringPositionArgument(); | |||
9183 | else | |||
9184 | nStart = 1; | |||
9185 | OUString aStr = GetString().getString(); | |||
9186 | int nLen = getLengthB( aStr ); | |||
9187 | OUString asStr = GetString().getString(); | |||
9188 | int nsLen = getLengthB( asStr ); | |||
9189 | if ( nStart < 1 || nStart > nLen - nsLen + 1 ) | |||
9190 | PushIllegalArgument(); | |||
9191 | else | |||
9192 | { | |||
9193 | // create a string from sStr starting at nStart | |||
9194 | OUString aBuf = lcl_RightB( aStr, nLen - nStart + 1 ); | |||
9195 | // search aBuf for asStr | |||
9196 | sal_Int32 nPos = aBuf.indexOf( asStr, 0 ); | |||
9197 | if ( nPos == -1 ) | |||
9198 | PushNoValue(); | |||
9199 | else | |||
9200 | { | |||
9201 | // obtain byte value of nPos | |||
9202 | int nBytePos = lcl_getLengthB( aBuf, nPos ); | |||
9203 | PushDouble( nBytePos + nStart ); | |||
9204 | } | |||
9205 | } | |||
9206 | } | |||
9207 | ||||
9208 | void ScInterpreter::ScSearchB() | |||
9209 | { | |||
9210 | sal_uInt8 nParamCount = GetByte(); | |||
9211 | if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) | |||
9212 | return; | |||
9213 | ||||
9214 | sal_Int32 nStart; | |||
9215 | if ( nParamCount == 3 ) | |||
9216 | { | |||
9217 | nStart = GetStringPositionArgument(); | |||
9218 | if( nStart < 1 ) | |||
9219 | { | |||
9220 | PushIllegalArgument(); | |||
9221 | return; | |||
9222 | } | |||
9223 | } | |||
9224 | else | |||
9225 | nStart = 1; | |||
9226 | OUString aStr = GetString().getString(); | |||
9227 | sal_Int32 nLen = getLengthB( aStr ); | |||
9228 | OUString asStr = GetString().getString(); | |||
9229 | sal_Int32 nsLen = nStart - 1; | |||
9230 | if( nsLen >= nLen ) | |||
9231 | PushNoValue(); | |||
9232 | else | |||
9233 | { | |||
9234 | // create a string from sStr starting at nStart | |||
9235 | OUString aSubStr( lcl_RightB( aStr, nLen - nStart + 1 ) ); | |||
9236 | // search aSubStr for asStr | |||
9237 | sal_Int32 nPos = 0; | |||
9238 | sal_Int32 nEndPos = aSubStr.getLength(); | |||
9239 | utl::SearchParam::SearchType eSearchType = DetectSearchType( asStr, mrDoc ); | |||
9240 | utl::SearchParam sPar( asStr, eSearchType, false, '~', false ); | |||
9241 | utl::TextSearch sT( sPar, *ScGlobal::getCharClassPtr() ); | |||
9242 | if ( !sT.SearchForward( aSubStr, &nPos, &nEndPos ) ) | |||
9243 | PushNoValue(); | |||
9244 | else | |||
9245 | { | |||
9246 | // obtain byte value of nPos | |||
9247 | int nBytePos = lcl_getLengthB( aSubStr, nPos ); | |||
9248 | PushDouble( nBytePos + nStart ); | |||
9249 | } | |||
9250 | } | |||
9251 | } | |||
9252 | ||||
9253 | void ScInterpreter::ScRight() | |||
9254 | { | |||
9255 | sal_uInt8 nParamCount = GetByte(); | |||
9256 | if ( !MustHaveParamCount( nParamCount, 1, 2 ) ) | |||
9257 | return; | |||
9258 | ||||
9259 | sal_Int32 n; | |||
9260 | if (nParamCount == 2) | |||
9261 | { | |||
9262 | n = GetStringPositionArgument(); | |||
9263 | if (n < 0) | |||
9264 | { | |||
9265 | PushIllegalArgument(); | |||
9266 | return ; | |||
9267 | } | |||
9268 | } | |||
9269 | else | |||
9270 | n = 1; | |||
9271 | OUString aStr = GetString().getString(); | |||
9272 | sal_Int32 nLen = aStr.getLength(); | |||
9273 | if ( nLen <= n ) | |||
9274 | PushString( aStr ); | |||
9275 | else | |||
9276 | { | |||
9277 | sal_Int32 nIdx = nLen; | |||
9278 | sal_Int32 nCnt = 0; | |||
9279 | while ( nIdx > 0 && n > nCnt ) | |||
9280 | { | |||
9281 | aStr.iterateCodePoints( &nIdx, -1 ); | |||
9282 | ++nCnt; | |||
9283 | } | |||
9284 | aStr = aStr.copy( nIdx, nLen - nIdx ); | |||
9285 | PushString( aStr ); | |||
9286 | } | |||
9287 | } | |||
9288 | ||||
9289 | void ScInterpreter::ScSearch() | |||
9290 | { | |||
9291 | sal_uInt8 nParamCount = GetByte(); | |||
9292 | if ( !MustHaveParamCount( nParamCount, 2, 3 ) ) | |||
9293 | return; | |||
9294 | ||||
9295 | sal_Int32 nStart; | |||
9296 | if (nParamCount == 3) | |||
9297 | { | |||
9298 | nStart = GetStringPositionArgument(); | |||
9299 | if( nStart < 1 ) | |||
9300 | { | |||
9301 | PushIllegalArgument(); | |||
9302 | return; | |||
9303 | } | |||
9304 | } | |||
9305 | else | |||
9306 | nStart = 1; | |||
9307 | OUString sStr = GetString().getString(); | |||
9308 | OUString SearchStr = GetString().getString(); | |||
9309 | sal_Int32 nPos = nStart - 1; | |||
9310 | sal_Int32 nEndPos = sStr.getLength(); | |||
9311 | if( nPos >= nEndPos ) | |||
9312 | PushNoValue(); | |||
9313 | else | |||
9314 | { | |||
9315 | utl::SearchParam::SearchType eSearchType = DetectSearchType( SearchStr, mrDoc ); | |||
9316 | utl::SearchParam sPar(SearchStr, eSearchType, false, '~', false); | |||
9317 | utl::TextSearch sT( sPar, *ScGlobal::getCharClassPtr() ); | |||
9318 | bool bBool = sT.SearchForward(sStr, &nPos, &nEndPos); | |||
9319 | if (!bBool) | |||
9320 | PushNoValue(); | |||
9321 | else | |||
9322 | { | |||
9323 | sal_Int32 nIdx = 0; | |||
9324 | sal_Int32 nCnt = 0; | |||
9325 | while ( nIdx <= nPos ) | |||
9326 | { | |||
9327 | sStr.iterateCodePoints( &nIdx ); | |||
9328 | ++nCnt; | |||
9329 | } | |||
9330 | PushDouble( static_cast<double>(nCnt) ); | |||
9331 | } | |||
9332 | } | |||
9333 | } | |||
9334 | ||||
9335 | void ScInterpreter::ScRegex() | |||
9336 | { | |||
9337 | const sal_uInt8 nParamCount = GetByte(); | |||
9338 | if (!MustHaveParamCount( nParamCount, 2, 4)) | |||
9339 | return; | |||
9340 | ||||
9341 | // Flags are supported only for replacement, search match flags can be | |||
9342 | // individually and much more flexible set in the regular expression | |||
9343 | // pattern using (?ismwx-ismwx) | |||
9344 | bool bGlobalReplacement = false; | |||
9345 | sal_Int32 nOccurrence = 1; // default first occurrence, if any | |||
9346 | if (nParamCount == 4) | |||
9347 | { | |||
9348 | // Argument can be either string or double. | |||
9349 | double fOccurrence; | |||
9350 | svl::SharedString aFlagsString; | |||
9351 | bool bDouble; | |||
9352 | if (!IsMissing()) | |||
9353 | bDouble = GetDoubleOrString( fOccurrence, aFlagsString); | |||
9354 | else | |||
9355 | { | |||
9356 | // For an omitted argument keep the default. | |||
9357 | PopError(); | |||
9358 | bDouble = true; | |||
9359 | fOccurrence = nOccurrence; | |||
9360 | } | |||
9361 | if (nGlobalError != FormulaError::NONE) | |||
9362 | { | |||
9363 | PushError( nGlobalError); | |||
9364 | return; | |||
9365 | } | |||
9366 | if (bDouble) | |||
9367 | { | |||
9368 | if (!CheckStringPositionArgument( fOccurrence)) | |||
9369 | { | |||
9370 | PushError( FormulaError::IllegalArgument); | |||
9371 | return; | |||
9372 | } | |||
9373 | nOccurrence = static_cast<sal_Int32>(fOccurrence); | |||
9374 | } | |||
9375 | else | |||
9376 | { | |||
9377 | const OUString aFlags( aFlagsString.getString()); | |||
9378 | // Empty flags string is valid => no flag set. | |||
9379 | if (aFlags.getLength() > 1) | |||
9380 | { | |||
9381 | // Only one flag supported. | |||
9382 | PushIllegalArgument(); | |||
9383 | return; | |||
9384 | } | |||
9385 | if (aFlags.getLength() == 1) | |||
9386 | { | |||
9387 | if (aFlags.indexOf('g') >= 0) | |||
9388 | bGlobalReplacement = true; | |||
9389 | else | |||
9390 | { | |||
9391 | // Unsupported flag. | |||
9392 | PushIllegalArgument(); | |||
9393 | return; | |||
9394 | } | |||
9395 | } | |||
9396 | } | |||
9397 | } | |||
9398 | ||||
9399 | bool bReplacement = false; | |||
9400 | OUString aReplacement; | |||
9401 | if (nParamCount >= 3) | |||
9402 | { | |||
9403 | // A missing argument is not an empty string to replace the match. | |||
9404 | // nOccurrence==0 forces no replacement, so simply discard the | |||
9405 | // argument. | |||
9406 | if (IsMissing() || nOccurrence == 0) | |||
9407 | PopError(); | |||
9408 | else | |||
9409 | { | |||
9410 | aReplacement = GetString().getString(); | |||
9411 | bReplacement = true; | |||
9412 | } | |||
9413 | } | |||
9414 | // If bGlobalReplacement==true and bReplacement==false then | |||
9415 | // bGlobalReplacement is silently ignored. | |||
9416 | ||||
9417 | OUString aExpression = GetString().getString(); | |||
9418 | OUString aText = GetString().getString(); | |||
9419 | ||||
9420 | if (nGlobalError != FormulaError::NONE) | |||
9421 | { | |||
9422 | PushError( nGlobalError); | |||
9423 | return; | |||
9424 | } | |||
9425 | ||||
9426 | // 0-th match or replacement is none, return original string early. | |||
9427 | if (nOccurrence == 0) | |||
9428 | { | |||
9429 | PushString( aText); | |||
9430 | return; | |||
9431 | } | |||
9432 | ||||
9433 | const icu::UnicodeString aIcuExpression( | |||
9434 | reinterpret_cast<const UChar*>(aExpression.getStr()), aExpression.getLength()); | |||
9435 | UErrorCode status = U_ZERO_ERROR; | |||
9436 | icu::RegexMatcher aRegexMatcher( aIcuExpression, 0, status); | |||
9437 | if (U_FAILURE(status)) | |||
9438 | { | |||
9439 | // Invalid regex. | |||
9440 | PushIllegalArgument(); | |||
9441 | return; | |||
9442 | } | |||
9443 | // Guard against pathological patterns, limit steps of engine, see | |||
9444 | // https://unicode-org.github.io/icu-docs/apidoc/released/icu4c/classicu_1_1RegexMatcher.html#a6ebcfcab4fe6a38678c0291643a03a00 | |||
9445 | aRegexMatcher.setTimeLimit( 23*1000, status); | |||
9446 | ||||
9447 | const icu::UnicodeString aIcuText( reinterpret_cast<const UChar*>(aText.getStr()), aText.getLength()); | |||
9448 | aRegexMatcher.reset( aIcuText); | |||
9449 | ||||
9450 | if (!bReplacement) | |||
9451 | { | |||
9452 | // Find n-th occurrence. | |||
9453 | sal_Int32 nCount = 0; | |||
9454 | #if (U_ICU_VERSION_MAJOR_NUM67 < 55) | |||
9455 | int32_t nStartPos = 0; | |||
9456 | while (aRegexMatcher.find(nStartPos, status) && U_SUCCESS(status) && ++nCount < nOccurrence) | |||
9457 | #else | |||
9458 | while (aRegexMatcher.find(status) && U_SUCCESS(status) && ++nCount < nOccurrence) | |||
9459 | #endif | |||
9460 | ; | |||
9461 | if (U_FAILURE(status)) | |||
9462 | { | |||
9463 | // Some error. | |||
9464 | PushIllegalArgument(); | |||
9465 | return; | |||
9466 | } | |||
9467 | // n-th match found? | |||
9468 | if (nCount != nOccurrence) | |||
9469 | { | |||
9470 | PushError( FormulaError::NotAvailable); | |||
9471 | return; | |||
9472 | } | |||
9473 | // Extract matched text. | |||
9474 | icu::UnicodeString aMatch( aRegexMatcher.group( status)); | |||
9475 | if (U_FAILURE(status)) | |||
9476 | { | |||
9477 | // Some error. | |||
9478 | PushIllegalArgument(); | |||
9479 | return; | |||
9480 | } | |||
9481 | OUString aResult( reinterpret_cast<const sal_Unicode*>(aMatch.getBuffer()), aMatch.length()); | |||
9482 | PushString( aResult); | |||
9483 | return; | |||
9484 | } | |||
9485 | ||||
9486 | const icu::UnicodeString aIcuReplacement( | |||
9487 | reinterpret_cast<const UChar*>(aReplacement.getStr()), aReplacement.getLength()); | |||
9488 | icu::UnicodeString aReplaced; | |||
9489 | if (bGlobalReplacement) | |||
9490 | // Replace all occurrences of match with replacement. | |||
9491 | aReplaced = aRegexMatcher.replaceAll( aIcuReplacement, status); | |||
9492 | else if (nOccurrence == 1) | |||
9493 | // Replace first occurrence of match with replacement. | |||
9494 | aReplaced = aRegexMatcher.replaceFirst( aIcuReplacement, status); | |||
9495 | else | |||
9496 | { | |||
9497 | // Replace n-th occurrence of match with replacement. | |||
9498 | sal_Int32 nCount = 0; | |||
9499 | #if (U_ICU_VERSION_MAJOR_NUM67 < 55) | |||
9500 | int32_t nStartPos = 0; | |||
9501 | while (aRegexMatcher.find(nStartPos, status) && U_SUCCESS(status)) | |||
9502 | #else | |||
9503 | while (aRegexMatcher.find(status) && U_SUCCESS(status)) | |||
9504 | #endif | |||
9505 | { | |||
9506 | // XXX NOTE: After several RegexMatcher::find() the | |||
9507 | // RegexMatcher::appendReplacement() still starts at the | |||
9508 | // beginning (or after the last appendReplacement() position | |||
9509 | // which is none here) and copies the original text up to the | |||
9510 | // current found match and then replaces the found match. | |||
9511 | if (++nCount == nOccurrence) | |||
9512 | { | |||
9513 | aRegexMatcher.appendReplacement( aReplaced, aIcuReplacement, status); | |||
9514 | break; | |||
9515 | } | |||
9516 | } | |||
9517 | aRegexMatcher.appendTail( aReplaced); | |||
9518 | } | |||
9519 | if (U_FAILURE(status)) | |||
9520 | { | |||
9521 | // Some error, e.g. extraneous $1 without group. | |||
9522 | PushIllegalArgument(); | |||
9523 | return; | |||
9524 | } | |||
9525 | OUString aResult( reinterpret_cast<const sal_Unicode*>(aReplaced.getBuffer()), aReplaced.length()); | |||
9526 | PushString( aResult); | |||
9527 | } | |||
9528 | ||||
9529 | void ScInterpreter::ScMid() | |||
9530 | { | |||
9531 | if ( !MustHaveParamCount( GetByte(), 3 ) ) | |||
9532 | return; | |||
9533 | ||||
9534 | const sal_Int32 nSubLen = GetStringPositionArgument(); | |||
9535 | const sal_Int32 nStart = GetStringPositionArgument(); | |||
9536 | OUString aStr = GetString().getString(); | |||
9537 | if ( nStart < 1 || nSubLen < 0 ) | |||
9538 | PushIllegalArgument(); | |||
9539 | else | |||
9540 | { | |||
9541 | sal_Int32 nLen = aStr.getLength(); | |||
9542 | sal_Int32 nIdx = 0; | |||
9543 | sal_Int32 nCnt = 0; | |||
9544 | while ( nIdx < nLen && nStart - 1 > nCnt ) | |||
9545 | { | |||
9546 | aStr.iterateCodePoints( &nIdx ); | |||
9547 | ++nCnt; | |||
9548 | } | |||
9549 | sal_Int32 nIdx0 = nIdx; //start position | |||
9550 | ||||
9551 | while ( nIdx < nLen && nStart + nSubLen - 1 > nCnt ) | |||
9552 | { | |||
9553 | aStr.iterateCodePoints( &nIdx ); | |||
9554 | ++nCnt; | |||
9555 | } | |||
9556 | aStr = aStr.copy( nIdx0, nIdx - nIdx0 ); | |||
9557 | PushString( aStr ); | |||
9558 | } | |||
9559 | } | |||
9560 | ||||
9561 | void ScInterpreter::ScText() | |||
9562 | { | |||
9563 | if ( !MustHaveParamCount( GetByte(), 2 ) ) | |||
9564 | return; | |||
9565 | ||||
9566 | OUString sFormatString = GetString().getString(); | |||
9567 | svl::SharedString aStr; | |||
9568 | bool bString = false; | |||
9569 | double fVal = 0.0; | |||
9570 | switch (GetStackType()) | |||
9571 | { | |||
9572 | case svError: | |||
9573 | PopError(); | |||
9574 | break; | |||
9575 | case svDouble: | |||
9576 | fVal = PopDouble(); | |||
9577 | break; | |||
9578 | default: | |||
9579 | { | |||
9580 | FormulaConstTokenRef xTok( PopToken()); | |||
9581 | if (nGlobalError == FormulaError::NONE) | |||
9582 | { | |||
9583 | PushTokenRef( xTok); | |||
9584 | // Temporarily override the ConvertStringToValue() | |||
9585 | // error for GetCellValue() / GetCellValueOrZero() | |||
9586 | FormulaError nSErr = mnStringNoValueError; | |||
9587 | mnStringNoValueError = FormulaError::NotNumericString; | |||
9588 | fVal = GetDouble(); | |||
9589 | mnStringNoValueError = nSErr; | |||
9590 | if (nGlobalError == FormulaError::NotNumericString) | |||
9591 | { | |||
9592 | // Not numeric. | |||
9593 | nGlobalError = FormulaError::NONE; | |||
9594 | PushTokenRef( xTok); | |||
9595 | aStr = GetString(); | |||
9596 | bString = true; | |||
9597 | } | |||
9598 | } | |||
9599 | } | |||
9600 | } | |||
9601 | if (nGlobalError != FormulaError::NONE) | |||
9602 | PushError( nGlobalError); | |||
9603 | else if (sFormatString.isEmpty()) | |||
9604 | { | |||
9605 | // Mimic the Excel behaviour that | |||
9606 | // * anything numeric returns an empty string | |||
9607 | // * text convertible to numeric returns an empty string | |||
9608 | // * any other text returns that text | |||
9609 | // Conversion was detected above. | |||
9610 | if (bString) | |||
9611 | PushString( aStr); | |||
9612 | else | |||
9613 | PushString( OUString()); | |||
9614 | } | |||
9615 | else | |||
9616 | { | |||
9617 | OUString aResult; | |||
9618 | const Color* pColor = nullptr; | |||
9619 | LanguageType eCellLang; | |||
9620 | const ScPatternAttr* pPattern = mrDoc.GetPattern( | |||
9621 | aPos.Col(), aPos.Row(), aPos.Tab() ); | |||
9622 | if ( pPattern ) | |||
9623 | eCellLang = pPattern->GetItem( ATTR_LANGUAGE_FORMAT ).GetValue(); | |||
9624 | else | |||
9625 | eCellLang = ScGlobal::eLnge; | |||
9626 | if (bString) | |||
9627 | { | |||
9628 | if (!pFormatter->GetPreviewString( sFormatString, aStr.getString(), | |||
9629 | aResult, &pColor, eCellLang)) | |||
9630 | PushIllegalArgument(); | |||
9631 | else | |||
9632 | PushString( aResult); | |||
9633 | } | |||
9634 | else | |||
9635 | { | |||
9636 | if (!pFormatter->GetPreviewStringGuess( sFormatString, fVal, | |||
9637 | aResult, &pColor, eCellLang)) | |||
9638 | PushIllegalArgument(); | |||
9639 | else | |||
9640 | PushString( aResult); | |||
9641 | } | |||
9642 | } | |||
9643 | } | |||
9644 | ||||
9645 | void ScInterpreter::ScSubstitute() | |||
9646 | { | |||
9647 | sal_uInt8 nParamCount = GetByte(); | |||
9648 | if ( !MustHaveParamCount( nParamCount, 3, 4 ) ) | |||
9649 | return; | |||
9650 | ||||
9651 | sal_Int32 nCnt; | |||
9652 | if (nParamCount == 4) | |||
9653 | { | |||
9654 | nCnt = GetStringPositionArgument(); | |||
9655 | if (nCnt < 1) | |||
9656 | { | |||
9657 | PushIllegalArgument(); | |||
9658 | return; | |||
9659 | } | |||
9660 | } | |||
9661 | else | |||
9662 | nCnt = 0; | |||
9663 | OUString sNewStr = GetString().getString(); | |||
9664 | OUString sOldStr = GetString().getString(); | |||
9665 | OUString sStr = GetString().getString(); | |||
9666 | sal_Int32 nPos = 0; | |||
9667 | sal_Int32 nCount = 0; | |||
9668 | sal_Int32 nNewLen = sNewStr.getLength(); | |||
9669 | sal_Int32 nOldLen = sOldStr.getLength(); | |||
9670 | while( true ) | |||
9671 | { | |||
9672 | nPos = sStr.indexOf( sOldStr, nPos ); | |||
9673 | if (nPos != -1) | |||
9674 | { | |||
9675 | nCount++; | |||
9676 | if( !nCnt || nCount == nCnt ) | |||
9677 | { | |||
9678 | sStr = sStr.replaceAt(nPos,nOldLen, ""); | |||
9679 | if ( CheckStringResultLen( sStr, sNewStr ) ) | |||
9680 | { | |||
9681 | sStr = sStr.replaceAt(nPos, 0, sNewStr); | |||
9682 | nPos = sal::static_int_cast<sal_Int32>( nPos + nNewLen ); | |||
9683 | } | |||
9684 | else | |||
9685 | break; | |||
9686 | } | |||
9687 | else | |||
9688 | nPos++; | |||
9689 | } | |||
9690 | else | |||
9691 | break; | |||
9692 | } | |||
9693 | PushString( sStr ); | |||
9694 | } | |||
9695 | ||||
9696 | void ScInterpreter::ScRept() | |||
9697 | { | |||
9698 | if ( !MustHaveParamCount( GetByte(), 2 ) ) | |||
9699 | return; | |||
9700 | ||||
9701 | sal_Int32 nCnt = GetStringPositionArgument(); | |||
9702 | OUString aStr = GetString().getString(); | |||
9703 | if (nCnt < 0) | |||
9704 | PushIllegalArgument(); | |||
9705 | else if (static_cast<double>(nCnt) * aStr.getLength() > kScInterpreterMaxStrLen) | |||
9706 | { | |||
9707 | PushError( FormulaError::StringOverflow ); | |||
9708 | } | |||
9709 | else if (nCnt == 0) | |||
9710 | PushString( EMPTY_OUSTRINGScGlobal::GetEmptyOUString() ); | |||
9711 | else | |||
9712 | { | |||
9713 | const sal_Int32 nLen = aStr.getLength(); | |||
9714 | OUStringBuffer aRes(nCnt*nLen); | |||
9715 | while( nCnt-- ) | |||
9716 | aRes.append(aStr); | |||
9717 | PushString( aRes.makeStringAndClear() ); | |||
9718 | } | |||
9719 | } | |||
9720 | ||||
9721 | void ScInterpreter::ScConcat() | |||
9722 | { | |||
9723 | sal_uInt8 nParamCount = GetByte(); | |||
9724 | OUString aRes; | |||
9725 | while( nParamCount-- > 0) | |||
9726 | { | |||
9727 | OUString aStr = GetString().getString(); | |||
9728 | if (CheckStringResultLen( aRes, aStr)) | |||
9729 | aRes = aStr + aRes; | |||
9730 | else | |||
9731 | break; | |||
9732 | } | |||
9733 | PushString( aRes ); | |||
9734 | } | |||
9735 | ||||
9736 | FormulaError ScInterpreter::GetErrorType() | |||
9737 | { | |||
9738 | FormulaError nErr; | |||
9739 | FormulaError nOldError = nGlobalError; | |||
9740 | nGlobalError = FormulaError::NONE; | |||
9741 | switch ( GetStackType() ) | |||
9742 | { | |||
9743 | case svRefList : | |||
9744 | { | |||
9745 | FormulaConstTokenRef x = PopToken(); | |||
9746 | if (nGlobalError != FormulaError::NONE) | |||
9747 | nErr = nGlobalError; | |||
9748 | else | |||
9749 | { | |||
9750 | const ScRefList* pRefList = x->GetRefList(); | |||
9751 | size_t n = pRefList->size(); | |||
9752 | if (!n) | |||
9753 | nErr = FormulaError::NoRef; | |||
9754 | else if (n > 1) | |||
9755 | nErr = FormulaError::NoValue; | |||
9756 | else | |||
9757 | { | |||
9758 | ScRange aRange; | |||
9759 | DoubleRefToRange( (*pRefList)[0], aRange); | |||
9760 | if (nGlobalError != FormulaError::NONE) | |||
9761 | nErr = nGlobalError; | |||
9762 | else | |||
9763 | { | |||
9764 | ScAddress aAdr; | |||
9765 | if ( DoubleRefToPosSingleRef( aRange, aAdr ) ) | |||
9766 | nErr = mrDoc.GetErrCode( aAdr ); | |||
9767 | else | |||
9768 | nErr = nGlobalError; | |||
9769 | } | |||
9770 | } | |||
9771 | } | |||
9772 | } | |||
9773 | break; | |||
9774 | case svDoubleRef : | |||
9775 | { | |||
9776 | ScRange aRange; | |||
9777 | PopDoubleRef( aRange ); | |||
9778 | if ( nGlobalError != FormulaError::NONE ) | |||
9779 | nErr = nGlobalError; | |||
9780 | else | |||
9781 | { | |||
9782 | ScAddress aAdr; | |||
9783 | if ( DoubleRefToPosSingleRef( aRange, aAdr ) ) | |||
9784 | nErr = mrDoc.GetErrCode( aAdr ); | |||
9785 | else | |||
9786 | nErr = nGlobalError; | |||
9787 | } | |||
9788 | } | |||
9789 | break; | |||
9790 | case svSingleRef : | |||
9791 | { | |||
9792 | ScAddress aAdr; | |||
9793 | PopSingleRef( aAdr ); | |||
9794 | if ( nGlobalError != FormulaError::NONE ) | |||
9795 | nErr = nGlobalError; | |||
9796 | else | |||
9797 | nErr = mrDoc.GetErrCode( aAdr ); | |||
9798 | } | |||
9799 | break; | |||
9800 | default: | |||
9801 | PopError(); | |||
9802 | nErr = nGlobalError; | |||
9803 | } | |||
9804 | nGlobalError = nOldError; | |||
9805 | return nErr; | |||
9806 | } | |||
9807 | ||||
9808 | void ScInterpreter::ScErrorType() | |||
9809 | { | |||
9810 | FormulaError nErr = GetErrorType(); | |||
9811 | if ( nErr != FormulaError::NONE ) | |||
9812 | { | |||
9813 | nGlobalError = FormulaError::NONE; | |||
9814 | PushDouble( static_cast<double>(nErr) ); | |||
9815 | } | |||
9816 | else | |||
9817 | { | |||
9818 | PushNA(); | |||
9819 | } | |||
9820 | } | |||
9821 | ||||
9822 | void ScInterpreter::ScErrorType_ODF() | |||
9823 | { | |||
9824 | FormulaError nErr = GetErrorType(); | |||
9825 | sal_uInt16 nErrType; | |||
9826 | ||||
9827 | switch ( nErr ) | |||
9828 | { | |||
9829 | case FormulaError::ParameterExpected : // #NULL! | |||
9830 | nErrType = 1; | |||
9831 | break; | |||
9832 | case FormulaError::DivisionByZero : // #DIV/0! | |||
9833 | nErrType = 2; | |||
9834 | break; | |||
9835 | case FormulaError::NoValue : // #VALUE! | |||
9836 | nErrType = 3; | |||
9837 | break; | |||
9838 | case FormulaError::NoRef : // #REF! | |||
9839 | nErrType = 4; | |||
9840 | break; | |||
9841 | case FormulaError::NoName : // #NAME? | |||
9842 | nErrType = 5; | |||
9843 | break; | |||
9844 | case FormulaError::IllegalFPOperation : // #NUM! | |||
9845 | nErrType = 6; | |||
9846 | break; | |||
9847 | case FormulaError::NotAvailable : // #N/A | |||
9848 | nErrType = 7; | |||
9849 | break; | |||
9850 | /* | |||
9851 | #GETTING_DATA is a message that can appear in Excel when a large or | |||
9852 | complex worksheet is being calculated. In Excel 2007 and newer, | |||
9853 | operations are grouped so more complicated cells may finish after | |||
9854 | earlier ones do. While the calculations are still processing, the | |||
9855 | unfinished cells may display #GETTING_DATA. | |||
9856 | Because the message is temporary and disappears when the calculations | |||
9857 | complete, this isn’t a true error. | |||
9858 | No calc error code known (yet). | |||
9859 | ||||
9860 | case : // GETTING_DATA | |||
9861 | nErrType = 8; | |||
9862 | break; | |||
9863 | */ | |||
9864 | default : | |||
9865 | nErrType = 0; | |||
9866 | break; | |||
9867 | } | |||
9868 | ||||
9869 | if ( nErrType ) | |||
9870 | { | |||
9871 | nGlobalError =FormulaError::NONE; | |||
9872 | PushDouble( nErrType ); | |||
9873 | } | |||
9874 | else | |||
9875 | PushNA(); | |||
9876 | } | |||
9877 | ||||
9878 | bool ScInterpreter::MayBeRegExp( const OUString& rStr, bool bIgnoreWildcards ) | |||
9879 | { | |||
9880 | if ( rStr.isEmpty() || (rStr.getLength() == 1 && !rStr.startsWith(".")) ) | |||
9881 | return false; // single meta characters can not be a regexp | |||
9882 | // First two characters are wildcard '?' and '*' characters. | |||
9883 | static const sal_Unicode cre[] = { '?','*','+','.','[',']','^','$','\\','<','>','(',')','|', 0 }; | |||
9884 | const sal_Unicode* const pre = bIgnoreWildcards ? cre + 2 : cre; | |||
9885 | const sal_Unicode* p1 = rStr.getStr(); | |||
9886 | sal_Unicode c1; | |||
9887 | while ( ( c1 = *p1++ ) != 0 ) | |||
9888 | { | |||
9889 | const sal_Unicode* p2 = pre; | |||
9890 | while ( *p2 ) | |||
9891 | { | |||
9892 | if ( c1 == *p2++ ) | |||
9893 | return true; | |||
9894 | } | |||
9895 | } | |||
9896 | return false; | |||
9897 | } | |||
9898 | ||||
9899 | bool ScInterpreter::MayBeWildcard( const OUString& rStr ) | |||
9900 | { | |||
9901 | // Wildcards with '~' escape, if there are no wildcards then an escaped | |||
9902 | // character does not make sense, but it modifies the search pattern in an | |||
9903 | // Excel compatible wildcard search... | |||
9904 | static const sal_Unicode cw[] = { '*','?','~', 0 }; | |||
9905 | const sal_Unicode* p1 = rStr.getStr(); | |||
9906 | sal_Unicode c1; | |||
9907 | while ( ( c1 = *p1++ ) != 0 ) | |||
9908 | { | |||
9909 | const sal_Unicode* p2 = cw; | |||
9910 | while ( *p2 ) | |||
9911 | { | |||
9912 | if ( c1 == *p2++ ) | |||
9913 | return true; | |||
9914 | } | |||
9915 | } | |||
9916 | return false; | |||
9917 | } | |||
9918 | ||||
9919 | utl::SearchParam::SearchType ScInterpreter::DetectSearchType( const OUString& rStr, const ScDocument& rDoc ) | |||
9920 | { | |||
9921 | if (rDoc.GetDocOptions().IsFormulaWildcardsEnabled()) | |||
9922 | return MayBeWildcard( rStr ) ? utl::SearchParam::SearchType::Wildcard : utl::SearchParam::SearchType::Normal; | |||
9923 | if (rDoc.GetDocOptions().IsFormulaRegexEnabled()) | |||
9924 | return MayBeRegExp( rStr ) ? utl::SearchParam::SearchType::Regexp : utl::SearchParam::SearchType::Normal; | |||
9925 | return utl::SearchParam::SearchType::Normal; | |||
9926 | } | |||
9927 | ||||
9928 | static bool lcl_LookupQuery( ScAddress & o_rResultPos, ScDocument& rDoc, const ScInterpreterContext& rContext, | |||
9929 | const ScQueryParam & rParam, const ScQueryEntry & rEntry ) | |||
9930 | { | |||
9931 | bool bFound = false; | |||
9932 | ScQueryCellIterator aCellIter( rDoc, rContext, rParam.nTab, rParam, false); | |||
9933 | if (rEntry.eOp != SC_EQUAL) | |||
9934 | { | |||
9935 | // range lookup <= or >= | |||
9936 | SCCOL nCol; | |||
9937 | SCROW nRow; | |||
9938 | bFound = aCellIter.FindEqualOrSortedLastInRange( nCol, nRow); | |||
9939 | if (bFound) | |||
9940 | { | |||
9941 | o_rResultPos.SetCol( nCol); | |||
9942 | o_rResultPos.SetRow( nRow); | |||
9943 | } | |||
9944 | } | |||
9945 | else if (aCellIter.GetFirst()) | |||
9946 | { | |||
9947 | // EQUAL | |||
9948 | bFound = true; | |||
9949 | o_rResultPos.SetCol( aCellIter.GetCol()); | |||
9950 | o_rResultPos.SetRow( aCellIter.GetRow()); | |||
9951 | } | |||
9952 | return bFound; | |||
9953 | } | |||
9954 | ||||
9955 | // tdf#121052: | |||
9956 | // =VLOOKUP(SearchCriterion; RangeArray; Index; Sorted) | |||
9957 | // [SearchCriterion] is the value searched for in the first column of the array. | |||
9958 | // [RangeArray] is the reference, which is to comprise at least two columns. | |||
9959 | // [Index] is the number of the column in the array that contains the value to be returned. The first column has the number 1. | |||
9960 | // | |||
9961 | // Prerequisite of lcl_getPrevRowWithEmptyValueLookup(): | |||
9962 | // Value referenced by [SearchCriterion] is empty. | |||
9963 | // lcl_getPrevRowWithEmptyValueLookup() performs following checks: | |||
9964 | // - if we run query with "exact match" mode (i.e. VLOOKUP) | |||
9965 | // - and if we already have the same lookup done before but for another row | |||
9966 | // which is also had empty [SearchCriterion] | |||
9967 | // | |||
9968 | // then | |||
9969 | // we could say, that for current row we could reuse results of the cached call which was done for the row2 | |||
9970 | // In this case we return row index, which is >= 0. | |||
9971 | // | |||
9972 | // Elsewhere | |||
9973 | // -1 is returned, which will lead to default behavior => | |||
9974 | // complete lookup will be done in RangeArray inside lcl_LookupQuery() method. | |||
9975 | // | |||
9976 | // This method was added only for speed up to avoid several useless complete | |||
9977 | // lookups inside [RangeArray] for searching empty strings. | |||
9978 | // | |||
9979 | static SCROW lcl_getPrevRowWithEmptyValueLookup( const ScLookupCache& rCache, | |||
9980 | const ScLookupCache::QueryCriteria& rCriteria, const ScQueryParam & rParam) | |||
9981 | { | |||
9982 | // is lookup value empty? | |||
9983 | const ScQueryEntry& rEntry = rParam.GetEntry(0); | |||
9984 | const ScQueryEntry::Item& rItem = rEntry.GetQueryItem(); | |||
9985 | if (! rItem.maString.getString().isEmpty()) | |||
9986 | return -1; // not found | |||
9987 | ||||
9988 | // try to find the row index for which we have already performed lookup | |||
9989 | // and have some result of it inside cache | |||
9990 | return rCache.lookup( rCriteria ); | |||
9991 | } | |||
9992 | ||||
9993 | bool ScInterpreter::LookupQueryWithCache( ScAddress & o_rResultPos, | |||
9994 | const ScQueryParam & rParam ) const | |||
9995 | { | |||
9996 | bool bFound = false; | |||
9997 | const ScQueryEntry& rEntry = rParam.GetEntry(0); | |||
9998 | bool bColumnsMatch = (rParam.nCol1 == rEntry.nField); | |||
9999 | OSL_ENSURE( bColumnsMatch, "ScInterpreter::LookupQueryWithCache: columns don't match")do { if (true && (!(bColumnsMatch))) { sal_detail_logFormat ((SAL_DETAIL_LOG_LEVEL_WARN), ("legacy.osl"), ("/home/maarten/src/libreoffice/core/sc/source/core/tool/interpr1.cxx" ":" "9999" ": "), "%s", "ScInterpreter::LookupQueryWithCache: columns don't match" ); } } while (false); | |||
10000 | // At least all volatile functions that generate indirect references have | |||
10001 | // to force non-cached lookup. | |||
10002 | /* TODO: We could further classify volatile functions into reference | |||
10003 | * generating and not reference generating functions to have to force less | |||
10004 | * direct lookups here. We could even further attribute volatility per | |||
10005 | * parameter so it would affect only the lookup range parameter. */ | |||
10006 | if (!bColumnsMatch || GetVolatileType() != NOT_VOLATILE) | |||
10007 | bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry); | |||
10008 | else | |||
10009 | { | |||
10010 | ScRange aLookupRange( rParam.nCol1, rParam.nRow1, rParam.nTab, | |||
10011 | rParam.nCol2, rParam.nRow2, rParam.nTab); | |||
10012 | ScLookupCache& rCache = mrDoc.GetLookupCache( aLookupRange, &mrContext ); | |||
10013 | ScLookupCache::QueryCriteria aCriteria( rEntry); | |||
10014 | ScLookupCache::Result eCacheResult = rCache.lookup( o_rResultPos, | |||
10015 | aCriteria, aPos); | |||
10016 | ||||
10017 | // tdf#121052: Slow load of cells with VLOOKUP with references to empty cells | |||
10018 | // This check was added only for speed up to avoid several useless complete | |||
10019 | // lookups inside [RangeArray] for searching empty strings. | |||
10020 | if (eCacheResult == ScLookupCache::NOT_CACHED && aCriteria.isEmptyStringQuery()) | |||
10021 | { | |||
10022 | const SCROW nPrevRowWithEmptyValueLookup = lcl_getPrevRowWithEmptyValueLookup(rCache, aCriteria, rParam); | |||
10023 | if (nPrevRowWithEmptyValueLookup >= 0) | |||
10024 | { | |||
10025 | // make the same lookup using cache with different row index | |||
10026 | // (this lookup was already cached) | |||
10027 | ScAddress aPosPrev(aPos); | |||
10028 | aPosPrev.SetRow(nPrevRowWithEmptyValueLookup); | |||
10029 | ||||
10030 | eCacheResult = rCache.lookup( o_rResultPos, aCriteria, aPosPrev ); | |||
10031 | } | |||
10032 | } | |||
10033 | ||||
10034 | switch (eCacheResult) | |||
10035 | { | |||
10036 | case ScLookupCache::NOT_CACHED : | |||
10037 | case ScLookupCache::CRITERIA_DIFFERENT : | |||
10038 | bFound = lcl_LookupQuery( o_rResultPos, mrDoc, mrContext, rParam, rEntry); | |||
10039 | if (eCacheResult == ScLookupCache::NOT_CACHED) | |||
10040 | rCache.insert( o_rResultPos, aCriteria, aPos, bFound); | |||
10041 | break; | |||
10042 | case ScLookupCache::FOUND : | |||
10043 | bFound = true; | |||
10044 | break; | |||
10045 | case ScLookupCache::NOT_AVAILABLE : | |||
10046 | ; // nothing, bFound remains FALSE | |||
10047 | break; | |||
10048 | } | |||
10049 | } | |||
10050 | return bFound; | |||
10051 | } | |||
10052 | ||||
10053 | /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |