1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
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>
34 #include <rangeutl.hxx>
35 #include <compiler.hxx>
36 #include <paramisc.hxx>
37 #include <calcconfig.hxx>
39 // struct ScImportParam:
41 ScImportParam::ScImportParam() :
53 ScImportParam::ScImportParam( const ScImportParam
& r
) :
60 aStatement (r
.aStatement
),
67 ScImportParam::~ScImportParam()
71 ScImportParam
& ScImportParam::operator=( const ScImportParam
& r
)
79 aStatement
= r
.aStatement
;
87 bool ScImportParam::operator==( const ScImportParam
& rOther
) const
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
);
100 //TODO: are nQuerySh and pConnection equal ?
103 // struct ScConsolidateParam:
105 ScConsolidateParam::ScConsolidateParam()
110 ScConsolidateParam::ScConsolidateParam( const ScConsolidateParam
& r
)
115 ScConsolidateParam::~ScConsolidateParam()
119 void ScConsolidateParam::ClearDataAreas()
125 void ScConsolidateParam::Clear()
132 bByCol
= bByRow
= bReferenceData
= false;
133 eFunction
= SUBTOTAL_FUNC_SUM
;
136 ScConsolidateParam
& ScConsolidateParam::operator=( const ScConsolidateParam
& r
)
145 bReferenceData
= r
.bReferenceData
;
146 eFunction
= r
.eFunction
;
147 nDataAreaCount
= r
.nDataAreaCount
;
148 if ( r
.nDataAreaCount
> 0 )
150 nDataAreaCount
= r
.nDataAreaCount
;
151 pDataAreas
.reset( new ScArea
[nDataAreaCount
] );
152 for ( sal_uInt16 i
=0; i
<nDataAreaCount
; i
++ )
153 pDataAreas
[i
] = r
.pDataAreas
[i
];
161 bool ScConsolidateParam::operator==( const ScConsolidateParam
& r
) const
163 bool bEqual
= (nCol
== r
.nCol
)
166 && (bByCol
== r
.bByCol
)
167 && (bByRow
== r
.bByRow
)
168 && (bReferenceData
== r
.bReferenceData
)
169 && (nDataAreaCount
== r
.nDataAreaCount
)
170 && (eFunction
== r
.eFunction
);
172 if ( nDataAreaCount
== 0 )
173 bEqual
= bEqual
&& (pDataAreas
== nullptr) && (r
.pDataAreas
== nullptr);
175 bEqual
= bEqual
&& (pDataAreas
!= nullptr) && (r
.pDataAreas
!= nullptr);
177 if ( bEqual
&& (nDataAreaCount
> 0) )
178 for ( sal_uInt16 i
=0; i
<nDataAreaCount
&& bEqual
; i
++ )
179 bEqual
= pDataAreas
[i
] == r
.pDataAreas
[i
];
184 void ScConsolidateParam::SetAreas( std::unique_ptr
<ScArea
[]> pAreas
, sal_uInt16 nCount
)
186 pDataAreas
= std::move(pAreas
);
187 nDataAreaCount
= nCount
;
190 // struct ScSolveParam
192 ScSolveParam::ScSolveParam()
196 ScSolveParam::ScSolveParam( const ScSolveParam
& r
)
197 : aRefFormulaCell ( r
.aRefFormulaCell
),
198 aRefVariableCell( r
.aRefVariableCell
),
199 pStrTargetVal ( r
.pStrTargetVal
)
203 ScSolveParam::ScSolveParam( const ScAddress
& rFormulaCell
,
204 const ScAddress
& rVariableCell
,
205 const OUString
& rTargetValStr
)
206 : aRefFormulaCell ( rFormulaCell
),
207 aRefVariableCell( rVariableCell
),
208 pStrTargetVal ( rTargetValStr
)
212 ScSolveParam::~ScSolveParam()
216 ScSolveParam
& ScSolveParam::operator=( const ScSolveParam
& r
)
218 aRefFormulaCell
= r
.aRefFormulaCell
;
219 aRefVariableCell
= r
.aRefVariableCell
;
220 pStrTargetVal
= r
.pStrTargetVal
;
224 bool ScSolveParam::operator==( const ScSolveParam
& r
) const
226 bool bEqual
= (aRefFormulaCell
== r
.aRefFormulaCell
)
227 && (aRefVariableCell
== r
.aRefVariableCell
);
231 if ( !pStrTargetVal
&& !r
.pStrTargetVal
)
233 else if ( !pStrTargetVal
|| !r
.pStrTargetVal
)
236 bEqual
= ( *pStrTargetVal
== *(r
.pStrTargetVal
) );
242 // struct ScTabOpParam
244 ScTabOpParam::ScTabOpParam() : meMode(Column
) {}
246 ScTabOpParam::ScTabOpParam( const ScTabOpParam
& r
)
247 : aRefFormulaCell ( r
.aRefFormulaCell
),
248 aRefFormulaEnd ( r
.aRefFormulaEnd
),
249 aRefRowCell ( r
.aRefRowCell
),
250 aRefColCell ( r
.aRefColCell
),
255 ScTabOpParam::ScTabOpParam( const ScRefAddress
& rFormulaCell
,
256 const ScRefAddress
& rFormulaEnd
,
257 const ScRefAddress
& rRowCell
,
258 const ScRefAddress
& rColCell
,
260 : aRefFormulaCell ( rFormulaCell
),
261 aRefFormulaEnd ( rFormulaEnd
),
262 aRefRowCell ( rRowCell
),
263 aRefColCell ( rColCell
),
268 ScTabOpParam
& ScTabOpParam::operator=( const ScTabOpParam
& r
)
270 aRefFormulaCell
= r
.aRefFormulaCell
;
271 aRefFormulaEnd
= r
.aRefFormulaEnd
;
272 aRefRowCell
= r
.aRefRowCell
;
273 aRefColCell
= r
.aRefColCell
;
278 bool ScTabOpParam::operator==( const ScTabOpParam
& r
) const
280 return ( (aRefFormulaCell
== r
.aRefFormulaCell
)
281 && (aRefFormulaEnd
== r
.aRefFormulaEnd
)
282 && (aRefRowCell
== r
.aRefRowCell
)
283 && (aRefColCell
== r
.aRefColCell
)
284 && (meMode
== r
.meMode
) );
287 OUString
ScGlobal::GetAbsDocName( const OUString
& rFileName
,
288 const SfxObjectShell
* pShell
)
291 if (!pShell
|| !pShell
->HasName())
292 { // maybe relative to document path working directory
294 if (!utl::ConfigManager::IsFuzzing())
296 aObj
.SetSmartURL(SvtPathOptions().GetWorkPath());
297 aObj
.setFinalSlash(); // it IS a path
300 aObj
.SetSmartURL(u
"file:///tmp/document");
302 aAbsName
= aObj
.smartRel2Abs( rFileName
, bWasAbs
).GetMainURL(INetURLObject::DecodeMechanism::NONE
);
303 // returned string must be encoded because it's used directly to create SfxMedium
307 const SfxMedium
* pMedium
= pShell
->GetMedium();
311 aAbsName
= pMedium
->GetURLObject().smartRel2Abs( rFileName
, bWasAbs
).GetMainURL(INetURLObject::DecodeMechanism::NONE
);
314 { // This can't happen, but ...
315 // just to be sure to have the same encoding
317 aObj
.SetSmartURL( aAbsName
);
318 aAbsName
= aObj
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
324 OUString
ScGlobal::GetDocTabName( std::u16string_view rFileName
,
325 std::u16string_view rTabName
)
327 OUString
aDocTab(rFileName
);
329 aDocTab
= "'" + aDocTab
.replaceAll(u
"'", u
"\\'") + "'" + OUStringChar(SC_COMPILER_FILE_TAB_SEP
) + rTabName
;
335 bool isEmptyString( const OUString
& rStr
)
339 else if (rStr
[0] == ' ')
341 const sal_Unicode
* p
= rStr
.getStr() + 1;
342 const sal_Unicode
* const pStop
= p
- 1 + rStr
.getLength();
343 while (p
< pStop
&& *p
== ' ')
352 double ScGlobal::ConvertStringToValue( const OUString
& rStr
, const ScCalcConfig
& rConfig
,
353 FormulaError
& rError
, FormulaError nStringNoValueError
,
354 SvNumberFormatter
* pFormatter
, SvNumFormatType
& rCurFmtType
)
356 // We keep ScCalcConfig::StringConversion::LOCALE default until
357 // we provide a friendly way to convert string numbers into numbers in the UI.
360 if (nStringNoValueError
== FormulaError::CellNoValue
)
362 // Requested that all strings result in 0, error handled by caller.
363 rError
= nStringNoValueError
;
367 switch (rConfig
.meStringConversion
)
369 case ScCalcConfig::StringConversion::ILLEGAL
:
370 rError
= nStringNoValueError
;
372 case ScCalcConfig::StringConversion::ZERO
:
374 case ScCalcConfig::StringConversion::LOCALE
:
376 if (rConfig
.mbEmptyStringAsZero
)
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.
384 if (isEmptyString( rStr
))
389 goto Label_fallback_to_unambiguous
;
391 sal_uInt32 nFIndex
= 0;
392 if (!pFormatter
->IsNumberFormat(rStr
, nFIndex
, fValue
))
394 rError
= nStringNoValueError
;
400 case ScCalcConfig::StringConversion::UNAMBIGUOUS
:
401 Label_fallback_to_unambiguous
:
403 if (!rConfig
.mbEmptyStringAsZero
)
405 if (isEmptyString( rStr
))
407 rError
= nStringNoValueError
;
412 // continue below, pulled from switch case for better readability
416 rtl_math_ConversionStatus eStatus
;
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
)
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
;
432 while (p
< pStop
&& *p
== ' ')
435 rError
= nStringNoValueError
;
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
452 while (p
< pStop
&& *p
== ' ')
454 if (p
< pStop
&& !rtl::isAsciiDigit(*p
))
455 rError
= nStringNoValueError
;
457 while (p
< pStop
&& rError
== FormulaError::NONE
&& eState
< blank
)
459 if (eState
== minute
)
460 rCurFmtType
|= SvNumFormatType::TIME
;
461 if (rtl::isAsciiDigit(*p
))
463 // Maximum 2 digits per unit, except fractions.
464 if (p
- pLastStart
>= 2 && eState
!= fraction
)
465 rError
= nStringNoValueError
;
467 else if (p
> pLastStart
)
469 // We had at least one digit.
472 nUnit
[eState
] = o3tl::toInt32(rStr
.subView( pLastStart
- pStart
, p
- pLastStart
));
473 if (nLimit
[eState
] && nLimit
[eState
] < nUnit
[eState
])
474 rError
= nStringNoValueError
;
476 pLastStart
= p
+ 1; // hypothetical next start
477 // Delimiters must match, a trailing delimiter
478 // yields an invalid date/time.
482 // Month must be followed by separator and
483 // day, no trailing blanks.
484 if (*p
!= '-' || (p
+1 == pStop
))
485 rError
= nStringNoValueError
;
488 if ((*p
!= 'T' || (p
+1 == pStop
)) && *p
!= ' ')
489 rError
= nStringNoValueError
;
490 // Take one blank as a valid delimiter
491 // between date and time.
494 // Hour must be followed by separator and
495 // minute, no trailing blanks.
496 if (*p
!= ':' || (p
+1 == pStop
))
497 rError
= nStringNoValueError
;
500 if ((*p
!= ':' || (p
+1 == pStop
)) && *p
!= ' ')
501 rError
= nStringNoValueError
;
506 if (((*p
!= ',' && *p
!= '.') || (p
+1 == pStop
)) && *p
!= ' ')
507 rError
= nStringNoValueError
;
515 rError
= nStringNoValueError
;
518 eState
= static_cast<State
>(eState
+ 1);
521 rError
= nStringNoValueError
;
526 while (p
< pStop
&& *p
== ' ')
529 rError
= nStringNoValueError
;
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
;
538 if (rError
== FormulaError::NONE
)
540 // Catch the very last unit at end of string.
541 if (p
> pLastStart
&& eState
< done
)
543 nUnit
[eState
] = o3tl::toInt32(rStr
.subView( pLastStart
- pStart
, p
- pLastStart
));
544 if (nLimit
[eState
] && nLimit
[eState
] < nUnit
[eState
])
545 rError
= nStringNoValueError
;
547 if (bDate
&& nUnit
[hour
] > 23)
548 rError
= nStringNoValueError
;
549 if (rError
== FormulaError::NONE
)
551 if (bDate
&& nUnit
[day
] == 0)
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
]))))));
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
;
569 fValue
= aDate
- pFormatter
->GetNullDate();
572 SAL_WARN("sc.core","ScGlobal::ConvertStringToValue - fixed null date");
573 static Date
aDefaultNullDate( 30, 12, 1899);
574 fValue
= aDate
- aDefaultNullDate
;
578 fValue
+= ((nUnit
[hour
] * 3600) + (nUnit
[minute
] * 60) + nUnit
[second
] + fFraction
) / 86400.0;
584 rError
= nStringNoValueError
;
586 if (rError
!= FormulaError::NONE
)
592 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */