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