LibreOffice Module sc (master) 1
global2.cxx
Go to the documentation of this file.
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 <sfx2/docfile.hxx>
21#include <sfx2/objsh.hxx>
24#include <tools/urlobj.hxx>
25#include <svl/numformat.hxx>
26#include <svl/zforlist.hxx>
28#include <sal/log.hxx>
29#include <rtl/character.hxx>
30#include <rtl/math.hxx>
31#include <o3tl/string_view.hxx>
32
33#include <global.hxx>
34#include <rangeutl.hxx>
35#include <compiler.hxx>
36#include <paramisc.hxx>
37#include <calcconfig.hxx>
38
39// struct ScImportParam:
40
42 nCol1(0),
43 nRow1(0),
44 nCol2(0),
45 nRow2(0),
46 bImport(false),
47 bNative(false),
48 bSql(true),
50{
51}
52
54 nCol1 (r.nCol1),
55 nRow1 (r.nRow1),
56 nCol2 (r.nCol2),
57 nRow2 (r.nRow2),
58 bImport (r.bImport),
59 aDBName (r.aDBName),
60 aStatement (r.aStatement),
61 bNative (r.bNative),
62 bSql (r.bSql),
63 nType (r.nType)
64{
65}
66
68{
69}
70
72{
73 nCol1 = r.nCol1;
74 nRow1 = r.nRow1;
75 nCol2 = r.nCol2;
76 nRow2 = r.nRow2;
77 bImport = r.bImport;
78 aDBName = r.aDBName;
80 bNative = r.bNative;
81 bSql = r.bSql;
82 nType = r.nType;
83
84 return *this;
85}
86
87bool ScImportParam::operator==( const ScImportParam& rOther ) const
88{
89 return( nCol1 == rOther.nCol1 &&
90 nRow1 == rOther.nRow1 &&
91 nCol2 == rOther.nCol2 &&
92 nRow2 == rOther.nRow2 &&
93 bImport == rOther.bImport &&
94 aDBName == rOther.aDBName &&
95 aStatement == rOther.aStatement &&
96 bNative == rOther.bNative &&
97 bSql == rOther.bSql &&
98 nType == rOther.nType );
99
100 //TODO: are nQuerySh and pConnection equal ?
101}
102
103// struct ScConsolidateParam:
104
106{
107 Clear();
108}
109
111{
112 operator=(r);
113}
114
116{
117}
118
120{
121 pDataAreas.reset();
122 nDataAreaCount = 0;
123}
124
126{
128
129 nCol = 0;
130 nRow = 0;
131 nTab = 0;
132 bByCol = bByRow = bReferenceData = false;
134}
135
137{
138 if (this != &r)
139 {
140 nCol = r.nCol;
141 nRow = r.nRow;
142 nTab = r.nTab;
143 bByCol = r.bByCol;
144 bByRow = r.bByRow;
148 if ( r.nDataAreaCount > 0 )
149 {
151 pDataAreas.reset( new ScArea[nDataAreaCount] );
152 for ( sal_uInt16 i=0; i<nDataAreaCount; i++ )
153 pDataAreas[i] = r.pDataAreas[i];
154 }
155 else
156 pDataAreas.reset();
157 }
158 return *this;
159}
160
162{
163 bool bEqual = (nCol == r.nCol)
164 && (nRow == r.nRow)
165 && (nTab == r.nTab)
166 && (bByCol == r.bByCol)
167 && (bByRow == r.bByRow)
170 && (eFunction == r.eFunction);
171
172 if ( nDataAreaCount == 0 )
173 bEqual = bEqual && (pDataAreas == nullptr) && (r.pDataAreas == nullptr);
174 else
175 bEqual = bEqual && (pDataAreas != nullptr) && (r.pDataAreas != nullptr);
176
177 if ( bEqual && (nDataAreaCount > 0) )
178 for ( sal_uInt16 i=0; i<nDataAreaCount && bEqual; i++ )
179 bEqual = pDataAreas[i] == r.pDataAreas[i];
180
181 return bEqual;
182}
183
184void ScConsolidateParam::SetAreas( std::unique_ptr<ScArea[]> pAreas, sal_uInt16 nCount )
185{
186 pDataAreas = std::move(pAreas);
188}
189
190// struct ScSolveParam
191
193{
194}
195
197 : aRefFormulaCell ( r.aRefFormulaCell ),
198 aRefVariableCell( r.aRefVariableCell ),
199 pStrTargetVal ( r.pStrTargetVal )
200{
201}
202
204 const ScAddress& rVariableCell,
205 const OUString& rTargetValStr )
206 : aRefFormulaCell ( rFormulaCell ),
207 aRefVariableCell( rVariableCell ),
208 pStrTargetVal ( rTargetValStr )
209{
210}
211
213{
214}
215
217{
221 return *this;
222}
223
225{
226 bool bEqual = (aRefFormulaCell == r.aRefFormulaCell)
228
229 if ( bEqual )
230 {
231 if ( !pStrTargetVal && !r.pStrTargetVal )
232 bEqual = true;
233 else if ( !pStrTargetVal || !r.pStrTargetVal )
234 bEqual = false;
235 else
236 bEqual = ( *pStrTargetVal == *(r.pStrTargetVal) );
237 }
238
239 return bEqual;
240}
241
242// struct ScTabOpParam
243
245
247 : aRefFormulaCell ( r.aRefFormulaCell ),
248 aRefFormulaEnd ( r.aRefFormulaEnd ),
249 aRefRowCell ( r.aRefRowCell ),
250 aRefColCell ( r.aRefColCell ),
251 meMode(r.meMode)
252{
253}
254
256 const ScRefAddress& rFormulaEnd,
257 const ScRefAddress& rRowCell,
258 const ScRefAddress& rColCell,
259 Mode eMode )
260 : aRefFormulaCell ( rFormulaCell ),
261 aRefFormulaEnd ( rFormulaEnd ),
262 aRefRowCell ( rRowCell ),
263 aRefColCell ( rColCell ),
265{
266}
267
269{
274 meMode = r.meMode;
275 return *this;
276}
277
279{
280 return ( (aRefFormulaCell == r.aRefFormulaCell)
282 && (aRefRowCell == r.aRefRowCell)
283 && (aRefColCell == r.aRefColCell)
284 && (meMode == r.meMode) );
285}
286
287OUString ScGlobal::GetAbsDocName( const OUString& rFileName,
288 const SfxObjectShell* pShell )
289{
290 OUString aAbsName;
291 if (!pShell || !pShell->HasName())
292 { // maybe relative to document path working directory
293 INetURLObject aObj;
295 {
296 aObj.SetSmartURL(SvtPathOptions().GetWorkPath());
297 aObj.setFinalSlash(); // it IS a path
298 }
299 else
300 aObj.SetSmartURL(u"file:///tmp/document");
301 bool bWasAbs = true;
302 aAbsName = aObj.smartRel2Abs( rFileName, bWasAbs ).GetMainURL(INetURLObject::DecodeMechanism::NONE);
303 // returned string must be encoded because it's used directly to create SfxMedium
304 }
305 else
306 {
307 const SfxMedium* pMedium = pShell->GetMedium();
308 if ( pMedium )
309 {
310 bool bWasAbs = true;
311 aAbsName = pMedium->GetURLObject().smartRel2Abs( rFileName, bWasAbs ).GetMainURL(INetURLObject::DecodeMechanism::NONE);
312 }
313 else
314 { // This can't happen, but ...
315 // just to be sure to have the same encoding
316 INetURLObject aObj;
317 aObj.SetSmartURL( aAbsName );
319 }
320 }
321 return aAbsName;
322}
323
324OUString ScGlobal::GetDocTabName( std::u16string_view rFileName,
325 std::u16string_view rTabName )
326{
327 OUString aDocTab(rFileName);
328 // "'Doc'#Tab"
329 aDocTab = "'" + aDocTab.replaceAll(u"'", u"\\'") + "'" + OUStringChar(SC_COMPILER_FILE_TAB_SEP) + rTabName;
330 return aDocTab;
331}
332
333namespace
334{
335bool isEmptyString( const OUString& rStr )
336{
337 if (rStr.isEmpty())
338 return true;
339 else if (rStr[0] == ' ')
340 {
341 const sal_Unicode* p = rStr.getStr() + 1;
342 const sal_Unicode* const pStop = p - 1 + rStr.getLength();
343 while (p < pStop && *p == ' ')
344 ++p;
345 if (p == pStop)
346 return true;
347 }
348 return false;
349}
350}
351
352double ScGlobal::ConvertStringToValue( const OUString& rStr, const ScCalcConfig& rConfig,
353 FormulaError & rError, FormulaError nStringNoValueError,
354 SvNumberFormatter* pFormatter, SvNumFormatType & rCurFmtType )
355{
356 // We keep ScCalcConfig::StringConversion::LOCALE default until
357 // we provide a friendly way to convert string numbers into numbers in the UI.
358
359 double fValue = 0.0;
360 if (nStringNoValueError == FormulaError::CellNoValue)
361 {
362 // Requested that all strings result in 0, error handled by caller.
363 rError = nStringNoValueError;
364 return fValue;
365 }
366
367 switch (rConfig.meStringConversion)
368 {
370 rError = nStringNoValueError;
371 return fValue;
373 return fValue;
375 {
376 if (rConfig.mbEmptyStringAsZero)
377 {
378 // The number scanner does not accept empty strings or strings
379 // containing only spaces, be on par in these cases with what was
380 // accepted in OOo and is in AOO (see also the
381 // StringConversion::UNAMBIGUOUS branch) and convert to 0 to prevent
382 // interoperability nightmares.
383
384 if (isEmptyString( rStr))
385 return fValue;
386 }
387
388 if (!pFormatter)
389 goto Label_fallback_to_unambiguous;
390
391 sal_uInt32 nFIndex = 0;
392 if (!pFormatter->IsNumberFormat(rStr, nFIndex, fValue))
393 {
394 rError = nStringNoValueError;
395 fValue = 0.0;
396 }
397 return fValue;
398 }
399 break;
401Label_fallback_to_unambiguous:
402 {
403 if (!rConfig.mbEmptyStringAsZero)
404 {
405 if (isEmptyString( rStr))
406 {
407 rError = nStringNoValueError;
408 return fValue;
409 }
410 }
411 }
412 // continue below, pulled from switch case for better readability
413 break;
414 }
415
416 rtl_math_ConversionStatus eStatus;
417 sal_Int32 nParseEnd;
418 // Decimal and group separator 0 => only integer and possibly exponent,
419 // stops at first non-digit non-sign.
420 fValue = ::rtl::math::stringToDouble( rStr, 0, 0, &eStatus, &nParseEnd);
421 sal_Int32 nLen = rStr.getLength();
422 if (eStatus == rtl_math_ConversionStatus_Ok && nParseEnd < nLen)
423 {
424 // Not at string end, check for trailing blanks or switch to date or
425 // time parsing or bail out.
426 const sal_Unicode* const pStart = rStr.getStr();
427 const sal_Unicode* p = pStart + nParseEnd;
428 const sal_Unicode* const pStop = pStart + nLen;
429 switch (*p++)
430 {
431 case ' ':
432 while (p < pStop && *p == ' ')
433 ++p;
434 if (p < pStop)
435 rError = nStringNoValueError;
436 break;
437 case '-':
438 case ':':
439 {
440 bool bDate = (*(p-1) == '-');
441 enum State { year = 0, month, day, hour, minute, second, fraction, done, blank, stop };
442 sal_Int32 nUnit[done] = {0,0,0,0,0,0,0};
443 const sal_Int32 nLimit[done] = {0,12,31,0,59,59,0};
444 State eState = (bDate ? month : minute);
445 rCurFmtType = (bDate ? SvNumFormatType::DATE : SvNumFormatType::TIME);
446 nUnit[eState-1] = o3tl::toInt32(rStr.subView( 0, nParseEnd));
447 const sal_Unicode* pLastStart = p;
448 // Ensure there's no preceding sign. Negative dates
449 // currently aren't handled correctly. Also discard
450 // +CCYY-MM-DD
451 p = pStart;
452 while (p < pStop && *p == ' ')
453 ++p;
454 if (p < pStop && !rtl::isAsciiDigit(*p))
455 rError = nStringNoValueError;
456 p = pLastStart;
457 while (p < pStop && rError == FormulaError::NONE && eState < blank)
458 {
459 if (eState == minute)
460 rCurFmtType |= SvNumFormatType::TIME;
461 if (rtl::isAsciiDigit(*p))
462 {
463 // Maximum 2 digits per unit, except fractions.
464 if (p - pLastStart >= 2 && eState != fraction)
465 rError = nStringNoValueError;
466 }
467 else if (p > pLastStart)
468 {
469 // We had at least one digit.
470 if (eState < done)
471 {
472 nUnit[eState] = o3tl::toInt32(rStr.subView( pLastStart - pStart, p - pLastStart));
473 if (nLimit[eState] && nLimit[eState] < nUnit[eState])
474 rError = nStringNoValueError;
475 }
476 pLastStart = p + 1; // hypothetical next start
477 // Delimiters must match, a trailing delimiter
478 // yields an invalid date/time.
479 switch (eState)
480 {
481 case month:
482 // Month must be followed by separator and
483 // day, no trailing blanks.
484 if (*p != '-' || (p+1 == pStop))
485 rError = nStringNoValueError;
486 break;
487 case day:
488 if ((*p != 'T' || (p+1 == pStop)) && *p != ' ')
489 rError = nStringNoValueError;
490 // Take one blank as a valid delimiter
491 // between date and time.
492 break;
493 case hour:
494 // Hour must be followed by separator and
495 // minute, no trailing blanks.
496 if (*p != ':' || (p+1 == pStop))
497 rError = nStringNoValueError;
498 break;
499 case minute:
500 if ((*p != ':' || (p+1 == pStop)) && *p != ' ')
501 rError = nStringNoValueError;
502 if (*p == ' ')
503 eState = done;
504 break;
505 case second:
506 if (((*p != ',' && *p != '.') || (p+1 == pStop)) && *p != ' ')
507 rError = nStringNoValueError;
508 if (*p == ' ')
509 eState = done;
510 break;
511 case fraction:
512 eState = done;
513 break;
514 default:
515 rError = nStringNoValueError;
516 break;
517 }
518 eState = static_cast<State>(eState + 1);
519 }
520 else
521 rError = nStringNoValueError;
522 ++p;
523 }
524 if (eState == blank)
525 {
526 while (p < pStop && *p == ' ')
527 ++p;
528 if (p < pStop)
529 rError = nStringNoValueError;
530 eState = stop;
531 }
532
533 // Month without day, or hour without minute.
534 if (eState == month || (eState == day && p <= pLastStart) ||
535 eState == hour || (eState == minute && p <= pLastStart))
536 rError = nStringNoValueError;
537
538 if (rError == FormulaError::NONE)
539 {
540 // Catch the very last unit at end of string.
541 if (p > pLastStart && eState < done)
542 {
543 nUnit[eState] = o3tl::toInt32(rStr.subView( pLastStart - pStart, p - pLastStart));
544 if (nLimit[eState] && nLimit[eState] < nUnit[eState])
545 rError = nStringNoValueError;
546 }
547 if (bDate && nUnit[hour] > 23)
548 rError = nStringNoValueError;
549 if (rError == FormulaError::NONE)
550 {
551 if (bDate && nUnit[day] == 0)
552 nUnit[day] = 1;
553 double fFraction = (nUnit[fraction] <= 0 ? 0.0 :
554 ::rtl::math::pow10Exp( nUnit[fraction],
555 static_cast<int>( -ceil( log10( static_cast<double>( nUnit[fraction]))))));
556 if (!bDate)
557 fValue = 0.0;
558 else
559 {
560 Date aDate(
561 sal::static_int_cast<sal_Int16>(nUnit[day]),
562 sal::static_int_cast<sal_Int16>(nUnit[month]),
563 sal::static_int_cast<sal_Int16>(nUnit[year]));
564 if (!aDate.IsValidDate())
565 rError = nStringNoValueError;
566 else
567 {
568 if (pFormatter)
569 fValue = aDate - pFormatter->GetNullDate();
570 else
571 {
572 SAL_WARN("sc.core","ScGlobal::ConvertStringToValue - fixed null date");
573 static Date aDefaultNullDate( 30, 12, 1899);
574 fValue = aDate - aDefaultNullDate;
575 }
576 }
577 }
578 fValue += ((nUnit[hour] * 3600) + (nUnit[minute] * 60) + nUnit[second] + fFraction) / 86400.0;
579 }
580 }
581 }
582 break;
583 default:
584 rError = nStringNoValueError;
585 }
586 if (rError != FormulaError::NONE)
587 fValue = 0.0;
588 }
589 return fValue;
590}
591
592/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
struct _ADOColumn Column
sal_Int32 day
sal_Int32 year
sal_Int32 month
bool IsValidDate() const
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
INetURLObject smartRel2Abs(OUString const &rTheRelURIRef, bool &rWasAbsolute, bool bIgnoreFragment=false, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, bool bRelativeNonURIs=false, FSysStyle eStyle=FSysStyle::Detect) const
bool setFinalSlash()
bool SetSmartURL(std::u16string_view rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, FSysStyle eStyle=FSysStyle::Detect)
static SC_DLLPUBLIC OUString GetDocTabName(std::u16string_view rFileName, std::u16string_view rTabName)
Definition: global2.cxx:324
static double ConvertStringToValue(const OUString &rStr, const ScCalcConfig &rConfig, FormulaError &rError, FormulaError nStringNoValueError, SvNumberFormatter *pFormatter, SvNumFormatType &rCurFmtType)
Convert string content to numeric value.
Definition: global2.cxx:352
static SC_DLLPUBLIC OUString GetAbsDocName(const OUString &rFileName, const SfxObjectShell *pShell)
Definition: global2.cxx:287
const INetURLObject & GetURLObject() const
bool HasName() const
SfxMedium * GetMedium() const
const Date & GetNullDate() const
bool IsNumberFormat(const OUString &sString, sal_uInt32 &F_Index, double &fOutNumber, SvNumInputOptions eInputOptions=SvNumInputOptions::NONE)
static bool IsFuzzing()
ColorMode meMode
#define SC_COMPILER_FILE_TAB_SEP
Definition: compiler.hxx:83
int nCount
float u
FormulaError
@ SUBTOTAL_FUNC_SUM
Definition: global.hxx:869
@ ScDbTable
Definition: global.hxx:419
Mode eMode
void * p
#define SAL_WARN(area, stream)
def stop(arg=None)
int i
sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix=10)
State
QPRO_FUNC_TYPE nType
Definition: qproform.cxx:398
Configuration options for formula interpreter.
Definition: calcconfig.hxx:44
StringConversion meStringConversion
Definition: calcconfig.hxx:54
@ UNAMBIGUOUS
=1+"1" gives 2, but =1+"1.000" or =1+"x" give VALUE!
@ LOCALE
=1+"1.000" may be 2 or 1001 ... =1+"x" gives VALUE!
@ ILLEGAL
=1+"1" or =1+"x" give VALUE!
@ ZERO
=1+"1" or =1+"x" give 1
bool mbEmptyStringAsZero
Definition: calcconfig.hxx:55
ScConsolidateParam & operator=(const ScConsolidateParam &r)
Definition: global2.cxx:136
sal_uInt16 nDataAreaCount
Definition: global.hxx:907
bool operator==(const ScConsolidateParam &r) const
Definition: global2.cxx:161
void ClearDataAreas()
Definition: global2.cxx:119
std::unique_ptr< ScArea[]> pDataAreas
Definition: global.hxx:908
ScSubTotalFunc eFunction
Definition: global.hxx:906
void SetAreas(std::unique_ptr< ScArea[]> pAreas, sal_uInt16 nCount)
Definition: global2.cxx:184
bool operator==(const ScImportParam &r) const
Definition: global2.cxx:87
SCROW nRow1
Definition: global.hxx:441
OUString aStatement
Definition: global.hxx:446
SCCOL nCol1
Definition: global.hxx:440
SCCOL nCol2
Definition: global.hxx:442
ScImportParam & operator=(const ScImportParam &r)
Definition: global2.cxx:71
SCROW nRow2
Definition: global.hxx:443
sal_uInt8 nType
Definition: global.hxx:449
OUString aDBName
Definition: global.hxx:445
bool bNative
Definition: global.hxx:447
bool bImport
Definition: global.hxx:444
ScAddress aRefVariableCell
Definition: paramisc.hxx:28
std::optional< OUString > pStrTargetVal
Definition: paramisc.hxx:29
ScSolveParam & operator=(const ScSolveParam &r)
Definition: global2.cxx:216
bool operator==(const ScSolveParam &r) const
Definition: global2.cxx:224
ScAddress aRefFormulaCell
Definition: paramisc.hxx:27
Parameter for data table aka multiple operations.
Definition: paramisc.hxx:46
ScRefAddress aRefFormulaEnd
Definition: paramisc.hxx:50
bool operator==(const ScTabOpParam &r) const
Definition: global2.cxx:278
ScRefAddress aRefColCell
Definition: paramisc.hxx:52
ScTabOpParam & operator=(const ScTabOpParam &r)
Definition: global2.cxx:268
ScRefAddress aRefFormulaCell
Definition: paramisc.hxx:49
ScRefAddress aRefRowCell
Definition: paramisc.hxx:51
sal_uInt16 sal_Unicode
SvNumFormatType