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>
22 #include <unotools/configmgr.hxx>
23 #include <unotools/pathoptions.hxx>
24 #include <tools/urlobj.hxx>
25 #include <svl/numformat.hxx>
26 #include <svl/zforlist.hxx>
27 #include <formula/errorcodes.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 
87 bool 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 {
127  ClearDataAreas();
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;
146  eFunction = r.eFunction;
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 
184 void ScConsolidateParam::SetAreas( std::unique_ptr<ScArea[]> pAreas, sal_uInt16 nCount )
185 {
186  pDataAreas = std::move(pAreas);
187  nDataAreaCount = nCount;
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 ),
264  meMode(eMode)
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 
287 OUString 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 
324 OUString ScGlobal::GetDocTabName( std::u16string_view rFileName,
325  std::u16string_view rTabName )
326 {
327  OUString aDocTab = OUString::Concat("'") + rFileName;
328  sal_Int32 nPos = 1;
329  while( (nPos = aDocTab.indexOf( '\'', nPos )) != -1 )
330  { // escape Quotes
331  aDocTab = aDocTab.replaceAt( nPos, 0, u"\\" );
332  nPos += 2;
333  }
334  aDocTab += "'" + OUStringChar(SC_COMPILER_FILE_TAB_SEP) + rTabName;
335  // "'Doc'#Tab"
336  return aDocTab;
337 }
338 
339 namespace
340 {
341 bool isEmptyString( const OUString& rStr )
342 {
343  if (rStr.isEmpty())
344  return true;
345  else if (rStr[0] == ' ')
346  {
347  const sal_Unicode* p = rStr.getStr() + 1;
348  const sal_Unicode* const pStop = p - 1 + rStr.getLength();
349  while (p < pStop && *p == ' ')
350  ++p;
351  if (p == pStop)
352  return true;
353  }
354  return false;
355 }
356 }
357 
358 double ScGlobal::ConvertStringToValue( const OUString& rStr, const ScCalcConfig& rConfig,
359  FormulaError & rError, FormulaError nStringNoValueError,
360  SvNumberFormatter* pFormatter, SvNumFormatType & rCurFmtType )
361 {
362  // We keep ScCalcConfig::StringConversion::LOCALE default until
363  // we provide a friendly way to convert string numbers into numbers in the UI.
364 
365  double fValue = 0.0;
366  if (nStringNoValueError == FormulaError::CellNoValue)
367  {
368  // Requested that all strings result in 0, error handled by caller.
369  rError = nStringNoValueError;
370  return fValue;
371  }
372 
373  switch (rConfig.meStringConversion)
374  {
376  rError = nStringNoValueError;
377  return fValue;
379  return fValue;
381  {
382  if (rConfig.mbEmptyStringAsZero)
383  {
384  // The number scanner does not accept empty strings or strings
385  // containing only spaces, be on par in these cases with what was
386  // accepted in OOo and is in AOO (see also the
387  // StringConversion::UNAMBIGUOUS branch) and convert to 0 to prevent
388  // interoperability nightmares.
389 
390  if (isEmptyString( rStr))
391  return fValue;
392  }
393 
394  if (!pFormatter)
395  goto Label_fallback_to_unambiguous;
396 
397  sal_uInt32 nFIndex = 0;
398  if (!pFormatter->IsNumberFormat(rStr, nFIndex, fValue))
399  {
400  rError = nStringNoValueError;
401  fValue = 0.0;
402  }
403  return fValue;
404  }
405  break;
407 Label_fallback_to_unambiguous:
408  {
409  if (!rConfig.mbEmptyStringAsZero)
410  {
411  if (isEmptyString( rStr))
412  {
413  rError = nStringNoValueError;
414  return fValue;
415  }
416  }
417  }
418  // continue below, pulled from switch case for better readability
419  break;
420  }
421 
422  rtl_math_ConversionStatus eStatus;
423  sal_Int32 nParseEnd;
424  // Decimal and group separator 0 => only integer and possibly exponent,
425  // stops at first non-digit non-sign.
426  fValue = ::rtl::math::stringToDouble( rStr, 0, 0, &eStatus, &nParseEnd);
427  sal_Int32 nLen = rStr.getLength();
428  if (eStatus == rtl_math_ConversionStatus_Ok && nParseEnd < nLen)
429  {
430  // Not at string end, check for trailing blanks or switch to date or
431  // time parsing or bail out.
432  const sal_Unicode* const pStart = rStr.getStr();
433  const sal_Unicode* p = pStart + nParseEnd;
434  const sal_Unicode* const pStop = pStart + nLen;
435  switch (*p++)
436  {
437  case ' ':
438  while (p < pStop && *p == ' ')
439  ++p;
440  if (p < pStop)
441  rError = nStringNoValueError;
442  break;
443  case '-':
444  case ':':
445  {
446  bool bDate = (*(p-1) == '-');
447  enum State { year = 0, month, day, hour, minute, second, fraction, done, blank, stop };
448  sal_Int32 nUnit[done] = {0,0,0,0,0,0,0};
449  const sal_Int32 nLimit[done] = {0,12,31,0,59,59,0};
450  State eState = (bDate ? month : minute);
451  rCurFmtType = (bDate ? SvNumFormatType::DATE : SvNumFormatType::TIME);
452  nUnit[eState-1] = o3tl::toInt32(rStr.subView( 0, nParseEnd));
453  const sal_Unicode* pLastStart = p;
454  // Ensure there's no preceding sign. Negative dates
455  // currently aren't handled correctly. Also discard
456  // +CCYY-MM-DD
457  p = pStart;
458  while (p < pStop && *p == ' ')
459  ++p;
460  if (p < pStop && !rtl::isAsciiDigit(*p))
461  rError = nStringNoValueError;
462  p = pLastStart;
463  while (p < pStop && rError == FormulaError::NONE && eState < blank)
464  {
465  if (eState == minute)
466  rCurFmtType |= SvNumFormatType::TIME;
467  if (rtl::isAsciiDigit(*p))
468  {
469  // Maximum 2 digits per unit, except fractions.
470  if (p - pLastStart >= 2 && eState != fraction)
471  rError = nStringNoValueError;
472  }
473  else if (p > pLastStart)
474  {
475  // We had at least one digit.
476  if (eState < done)
477  {
478  nUnit[eState] = o3tl::toInt32(rStr.subView( pLastStart - pStart, p - pLastStart));
479  if (nLimit[eState] && nLimit[eState] < nUnit[eState])
480  rError = nStringNoValueError;
481  }
482  pLastStart = p + 1; // hypothetical next start
483  // Delimiters must match, a trailing delimiter
484  // yields an invalid date/time.
485  switch (eState)
486  {
487  case month:
488  // Month must be followed by separator and
489  // day, no trailing blanks.
490  if (*p != '-' || (p+1 == pStop))
491  rError = nStringNoValueError;
492  break;
493  case day:
494  if ((*p != 'T' || (p+1 == pStop)) && *p != ' ')
495  rError = nStringNoValueError;
496  // Take one blank as a valid delimiter
497  // between date and time.
498  break;
499  case hour:
500  // Hour must be followed by separator and
501  // minute, no trailing blanks.
502  if (*p != ':' || (p+1 == pStop))
503  rError = nStringNoValueError;
504  break;
505  case minute:
506  if ((*p != ':' || (p+1 == pStop)) && *p != ' ')
507  rError = nStringNoValueError;
508  if (*p == ' ')
509  eState = done;
510  break;
511  case second:
512  if (((*p != ',' && *p != '.') || (p+1 == pStop)) && *p != ' ')
513  rError = nStringNoValueError;
514  if (*p == ' ')
515  eState = done;
516  break;
517  case fraction:
518  eState = done;
519  break;
520  default:
521  rError = nStringNoValueError;
522  break;
523  }
524  eState = static_cast<State>(eState + 1);
525  }
526  else
527  rError = nStringNoValueError;
528  ++p;
529  }
530  if (eState == blank)
531  {
532  while (p < pStop && *p == ' ')
533  ++p;
534  if (p < pStop)
535  rError = nStringNoValueError;
536  eState = stop;
537  }
538 
539  // Month without day, or hour without minute.
540  if (eState == month || (eState == day && p <= pLastStart) ||
541  eState == hour || (eState == minute && p <= pLastStart))
542  rError = nStringNoValueError;
543 
544  if (rError == FormulaError::NONE)
545  {
546  // Catch the very last unit at end of string.
547  if (p > pLastStart && eState < done)
548  {
549  nUnit[eState] = o3tl::toInt32(rStr.subView( pLastStart - pStart, p - pLastStart));
550  if (nLimit[eState] && nLimit[eState] < nUnit[eState])
551  rError = nStringNoValueError;
552  }
553  if (bDate && nUnit[hour] > 23)
554  rError = nStringNoValueError;
555  if (rError == FormulaError::NONE)
556  {
557  if (bDate && nUnit[day] == 0)
558  nUnit[day] = 1;
559  double fFraction = (nUnit[fraction] <= 0 ? 0.0 :
560  ::rtl::math::pow10Exp( nUnit[fraction],
561  static_cast<int>( -ceil( log10( static_cast<double>( nUnit[fraction]))))));
562  if (!bDate)
563  fValue = 0.0;
564  else
565  {
566  Date aDate(
567  sal::static_int_cast<sal_Int16>(nUnit[day]),
568  sal::static_int_cast<sal_Int16>(nUnit[month]),
569  sal::static_int_cast<sal_Int16>(nUnit[year]));
570  if (!aDate.IsValidDate())
571  rError = nStringNoValueError;
572  else
573  {
574  if (pFormatter)
575  fValue = aDate - pFormatter->GetNullDate();
576  else
577  {
578  SAL_WARN("sc.core","ScGlobal::ConvertStringToValue - fixed null date");
579  static Date aDefaultNullDate( 30, 12, 1899);
580  fValue = aDate - aDefaultNullDate;
581  }
582  }
583  }
584  fValue += ((nUnit[hour] * 3600) + (nUnit[minute] * 60) + nUnit[second] + fFraction) / 86400.0;
585  }
586  }
587  }
588  break;
589  default:
590  rError = nStringNoValueError;
591  }
592  if (rError != FormulaError::NONE)
593  fValue = 0.0;
594  }
595  return fValue;
596 }
597 
598 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
bool operator==(const ScImportParam &r) const
Definition: global2.cxx:87
State
bool IsValidDate() const
OUString aDBName
Definition: global.hxx:444
sal_Int32 month
=1+"1" gives 2, but =1+"1.000" or =1+"x" give VALUE!
bool SetSmartURL(std::u16string_view rTheAbsURIRef, EncodeMechanism eMechanism=EncodeMechanism::WasEncoded, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8, FSysStyle eStyle=FSysStyle::Detect)
struct _ADOColumn Column
void SetAreas(std::unique_ptr< ScArea[]> pAreas, sal_uInt16 nCount)
Definition: global2.cxx:184
sal_uInt16 nDataAreaCount
Definition: global.hxx:892
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:358
std::unique_ptr< ScArea[]> pDataAreas
Definition: global.hxx:893
SCROW nRow2
Definition: global.hxx:442
bool operator==(const ScTabOpParam &r) const
Definition: global2.cxx:278
sal_uInt16 sal_Unicode
bool setFinalSlash()
=1+"1" or =1+"x" give 1
static bool IsFuzzing()
ScImportParam & operator=(const ScImportParam &r)
Definition: global2.cxx:71
SCCOL nCol2
Definition: global.hxx:441
ScRefAddress aRefRowCell
Definition: paramisc.hxx:51
StringConversion meStringConversion
Definition: calcconfig.hxx:54
bool operator==(const ScSolveParam &r) const
Definition: global2.cxx:224
ScAddress aRefVariableCell
Definition: paramisc.hxx:28
SCROW nRow1
Definition: global.hxx:440
ColorMode meMode
void ClearDataAreas()
Definition: global2.cxx:119
std::optional< OUString > pStrTargetVal
Definition: paramisc.hxx:29
bool operator==(const ScConsolidateParam &r) const
Definition: global2.cxx:161
static SC_DLLPUBLIC OUString GetAbsDocName(const OUString &rFileName, const SfxObjectShell *pShell)
Definition: global2.cxx:287
OUString aStatement
Definition: global.hxx:445
Parameter for data table aka multiple operations.
Definition: paramisc.hxx:45
int i
=1+"1.000" may be 2 or 1001 ... =1+"x" gives VALUE!
ScRefAddress aRefFormulaCell
Definition: paramisc.hxx:49
bool bNative
Definition: global.hxx:446
float u
bool IsNumberFormat(const OUString &sString, sal_uInt32 &F_Index, double &fOutNumber, SvNumInputOptions eInputOptions=SvNumInputOptions::NONE)
ScSolveParam & operator=(const ScSolveParam &r)
Definition: global2.cxx:216
SvNumFormatType
def stop
bool HasName() const
sal_Int32 toInt32(std::u16string_view str, sal_Int16 radix=10)
ScTabOpParam & operator=(const ScTabOpParam &r)
Definition: global2.cxx:268
FormulaError
Configuration options for formula interpreter.
Definition: calcconfig.hxx:43
OUString GetMainURL(DecodeMechanism eMechanism, rtl_TextEncoding eCharset=RTL_TEXTENCODING_UTF8) const
sal_Int32 day
sal_uInt8 nType
Definition: global.hxx:448
static SC_DLLPUBLIC OUString GetDocTabName(std::u16string_view rFileName, std::u16string_view rTabName)
Definition: global2.cxx:324
ScRefAddress aRefColCell
Definition: paramisc.hxx:52
ScSubTotalFunc eFunction
Definition: global.hxx:891
const INetURLObject & GetURLObject() const
void * p
QPRO_FUNC_TYPE nType
Definition: qproform.cxx:398
bool bImport
Definition: global.hxx:443
SCCOL nCol1
Definition: global.hxx:439
#define SC_COMPILER_FILE_TAB_SEP
Definition: compiler.hxx:82
ScAddress aRefFormulaCell
Definition: paramisc.hxx:27
=1+"1" or =1+"x" give VALUE!
#define SAL_WARN(area, stream)
ScRefAddress aRefFormulaEnd
Definition: paramisc.hxx:50
sal_Int32 year
bool mbEmptyStringAsZero
Definition: calcconfig.hxx:55
const Date & GetNullDate() 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
ScConsolidateParam & operator=(const ScConsolidateParam &r)
Definition: global2.cxx:136
sal_uInt16 nPos
bool m_bDetectedRangeSegmentation false
SfxMedium * GetMedium() const