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 <comphelper/configuration.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>
38 #include <interpretercontext.hxx>
40 // struct ScImportParam:
42 ScImportParam::ScImportParam() :
54 ScImportParam::ScImportParam( const ScImportParam
& r
) :
61 aStatement (r
.aStatement
),
68 ScImportParam::~ScImportParam()
72 ScImportParam
& ScImportParam::operator=( const ScImportParam
& r
)
80 aStatement
= r
.aStatement
;
88 bool ScImportParam::operator==( const ScImportParam
& rOther
) const
90 return( nCol1
== rOther
.nCol1
&&
91 nRow1
== rOther
.nRow1
&&
92 nCol2
== rOther
.nCol2
&&
93 nRow2
== rOther
.nRow2
&&
94 bImport
== rOther
.bImport
&&
95 aDBName
== rOther
.aDBName
&&
96 aStatement
== rOther
.aStatement
&&
97 bNative
== rOther
.bNative
&&
98 bSql
== rOther
.bSql
&&
99 nType
== rOther
.nType
);
101 //TODO: are nQuerySh and pConnection equal ?
104 // struct ScConsolidateParam:
106 ScConsolidateParam::ScConsolidateParam()
111 ScConsolidateParam::ScConsolidateParam( const ScConsolidateParam
& r
)
116 ScConsolidateParam::~ScConsolidateParam()
120 void ScConsolidateParam::ClearDataAreas()
126 void ScConsolidateParam::Clear()
133 bByCol
= bByRow
= bReferenceData
= false;
134 eFunction
= SUBTOTAL_FUNC_SUM
;
137 ScConsolidateParam
& ScConsolidateParam::operator=( const ScConsolidateParam
& r
)
146 bReferenceData
= r
.bReferenceData
;
147 eFunction
= r
.eFunction
;
148 nDataAreaCount
= r
.nDataAreaCount
;
149 if ( r
.nDataAreaCount
> 0 )
151 nDataAreaCount
= r
.nDataAreaCount
;
152 pDataAreas
.reset( new ScArea
[nDataAreaCount
] );
153 for ( sal_uInt16 i
=0; i
<nDataAreaCount
; i
++ )
154 pDataAreas
[i
] = r
.pDataAreas
[i
];
162 bool ScConsolidateParam::operator==( const ScConsolidateParam
& r
) const
164 bool bEqual
= (nCol
== r
.nCol
)
167 && (bByCol
== r
.bByCol
)
168 && (bByRow
== r
.bByRow
)
169 && (bReferenceData
== r
.bReferenceData
)
170 && (nDataAreaCount
== r
.nDataAreaCount
)
171 && (eFunction
== r
.eFunction
);
173 if ( nDataAreaCount
== 0 )
174 bEqual
= bEqual
&& (pDataAreas
== nullptr) && (r
.pDataAreas
== nullptr);
176 bEqual
= bEqual
&& (pDataAreas
!= nullptr) && (r
.pDataAreas
!= nullptr);
178 if ( bEqual
&& (nDataAreaCount
> 0) )
179 for ( sal_uInt16 i
=0; i
<nDataAreaCount
&& bEqual
; i
++ )
180 bEqual
= pDataAreas
[i
] == r
.pDataAreas
[i
];
185 void ScConsolidateParam::SetAreas( std::unique_ptr
<ScArea
[]> pAreas
, sal_uInt16 nCount
)
187 pDataAreas
= std::move(pAreas
);
188 nDataAreaCount
= nCount
;
191 // struct ScSolveParam
193 ScSolveParam::ScSolveParam()
197 ScSolveParam::ScSolveParam( const ScSolveParam
& r
)
198 : aRefFormulaCell ( r
.aRefFormulaCell
),
199 aRefVariableCell( r
.aRefVariableCell
),
200 pStrTargetVal ( r
.pStrTargetVal
)
204 ScSolveParam::ScSolveParam( const ScAddress
& rFormulaCell
,
205 const ScAddress
& rVariableCell
,
206 const OUString
& rTargetValStr
)
207 : aRefFormulaCell ( rFormulaCell
),
208 aRefVariableCell( rVariableCell
),
209 pStrTargetVal ( rTargetValStr
)
213 ScSolveParam::~ScSolveParam()
217 ScSolveParam
& ScSolveParam::operator=( const ScSolveParam
& r
)
219 aRefFormulaCell
= r
.aRefFormulaCell
;
220 aRefVariableCell
= r
.aRefVariableCell
;
221 pStrTargetVal
= r
.pStrTargetVal
;
225 bool ScSolveParam::operator==( const ScSolveParam
& r
) const
227 bool bEqual
= (aRefFormulaCell
== r
.aRefFormulaCell
)
228 && (aRefVariableCell
== r
.aRefVariableCell
);
232 if ( !pStrTargetVal
&& !r
.pStrTargetVal
)
234 else if ( !pStrTargetVal
|| !r
.pStrTargetVal
)
237 bEqual
= ( *pStrTargetVal
== *(r
.pStrTargetVal
) );
243 // struct ScTabOpParam
245 ScTabOpParam::ScTabOpParam() : meMode(Column
) {}
247 ScTabOpParam::ScTabOpParam( const ScTabOpParam
& r
)
248 : aRefFormulaCell ( r
.aRefFormulaCell
),
249 aRefFormulaEnd ( r
.aRefFormulaEnd
),
250 aRefRowCell ( r
.aRefRowCell
),
251 aRefColCell ( r
.aRefColCell
),
256 ScTabOpParam::ScTabOpParam( const ScRefAddress
& rFormulaCell
,
257 const ScRefAddress
& rFormulaEnd
,
258 const ScRefAddress
& rRowCell
,
259 const ScRefAddress
& rColCell
,
261 : aRefFormulaCell ( rFormulaCell
),
262 aRefFormulaEnd ( rFormulaEnd
),
263 aRefRowCell ( rRowCell
),
264 aRefColCell ( rColCell
),
269 ScTabOpParam
& ScTabOpParam::operator=( const ScTabOpParam
& r
)
271 aRefFormulaCell
= r
.aRefFormulaCell
;
272 aRefFormulaEnd
= r
.aRefFormulaEnd
;
273 aRefRowCell
= r
.aRefRowCell
;
274 aRefColCell
= r
.aRefColCell
;
279 bool ScTabOpParam::operator==( const ScTabOpParam
& r
) const
281 return ( (aRefFormulaCell
== r
.aRefFormulaCell
)
282 && (aRefFormulaEnd
== r
.aRefFormulaEnd
)
283 && (aRefRowCell
== r
.aRefRowCell
)
284 && (aRefColCell
== r
.aRefColCell
)
285 && (meMode
== r
.meMode
) );
288 OUString
ScGlobal::GetAbsDocName( const OUString
& rFileName
,
289 const SfxObjectShell
* pShell
)
292 if (!pShell
|| !pShell
->HasName())
293 { // maybe relative to document path working directory
295 if (!comphelper::IsFuzzing())
297 aObj
.SetSmartURL(SvtPathOptions().GetWorkPath());
298 aObj
.setFinalSlash(); // it IS a path
301 aObj
.SetSmartURL(u
"file:///tmp/document");
303 aAbsName
= aObj
.smartRel2Abs( rFileName
, bWasAbs
).GetMainURL(INetURLObject::DecodeMechanism::NONE
);
304 // returned string must be encoded because it's used directly to create SfxMedium
308 const SfxMedium
* pMedium
= pShell
->GetMedium();
312 aAbsName
= pMedium
->GetURLObject().smartRel2Abs( rFileName
, bWasAbs
).GetMainURL(INetURLObject::DecodeMechanism::NONE
);
315 { // This can't happen, but ...
316 // just to be sure to have the same encoding
318 aObj
.SetSmartURL( aAbsName
);
319 aAbsName
= aObj
.GetMainURL(INetURLObject::DecodeMechanism::NONE
);
325 OUString
ScGlobal::GetDocTabName( std::u16string_view rFileName
,
326 std::u16string_view rTabName
)
328 OUString
aDocTab(rFileName
);
330 aDocTab
= "'" + aDocTab
.replaceAll(u
"'", u
"\\'") + "'" + OUStringChar(SC_COMPILER_FILE_TAB_SEP
) + rTabName
;
336 bool isEmptyString( const OUString
& rStr
)
340 else if (rStr
[0] == ' ')
342 const sal_Unicode
* p
= rStr
.getStr() + 1;
343 const sal_Unicode
* const pStop
= p
- 1 + rStr
.getLength();
344 while (p
< pStop
&& *p
== ' ')
353 double ScGlobal::ConvertStringToValue( const OUString
& rStr
, const ScCalcConfig
& rConfig
,
354 FormulaError
& rError
, FormulaError nStringNoValueError
,
355 ScInterpreterContext
& rContext
, SvNumFormatType
& rCurFmtType
)
357 // We keep ScCalcConfig::StringConversion::LOCALE default until
358 // we provide a friendly way to convert string numbers into numbers in the UI.
361 if (nStringNoValueError
== FormulaError::CellNoValue
)
363 // Requested that all strings result in 0, error handled by caller.
364 rError
= nStringNoValueError
;
368 switch (rConfig
.meStringConversion
)
370 case ScCalcConfig::StringConversion::ILLEGAL
:
371 rError
= nStringNoValueError
;
373 case ScCalcConfig::StringConversion::ZERO
:
375 case ScCalcConfig::StringConversion::LOCALE
:
377 if (rConfig
.mbEmptyStringAsZero
)
379 // The number scanner does not accept empty strings or strings
380 // containing only spaces, be on par in these cases with what was
381 // accepted in OOo and is in AOO (see also the
382 // StringConversion::UNAMBIGUOUS branch) and convert to 0 to prevent
383 // interoperability nightmares.
385 if (isEmptyString( rStr
))
389 sal_uInt32 nFIndex
= 0;
390 if (!rContext
.NFIsNumberFormat(rStr
, nFIndex
, fValue
))
392 rError
= nStringNoValueError
;
398 case ScCalcConfig::StringConversion::UNAMBIGUOUS
:
400 if (!rConfig
.mbEmptyStringAsZero
)
402 if (isEmptyString( rStr
))
404 rError
= nStringNoValueError
;
409 // continue below, pulled from switch case for better readability
413 rtl_math_ConversionStatus eStatus
;
415 // Decimal and group separator 0 => only integer and possibly exponent,
416 // stops at first non-digit non-sign.
417 fValue
= ::rtl::math::stringToDouble( rStr
, 0, 0, &eStatus
, &nParseEnd
);
418 sal_Int32 nLen
= rStr
.getLength();
419 if (eStatus
== rtl_math_ConversionStatus_Ok
&& nParseEnd
< nLen
)
421 // Not at string end, check for trailing blanks or switch to date or
422 // time parsing or bail out.
423 const sal_Unicode
* const pStart
= rStr
.getStr();
424 const sal_Unicode
* p
= pStart
+ nParseEnd
;
425 const sal_Unicode
* const pStop
= pStart
+ nLen
;
429 while (p
< pStop
&& *p
== ' ')
432 rError
= nStringNoValueError
;
437 bool bDate
= (*(p
-1) == '-');
438 enum State
{ year
= 0, month
, day
, hour
, minute
, second
, fraction
, done
, blank
, stop
};
439 sal_Int32 nUnit
[done
] = {0,0,0,0,0,0,0};
440 const sal_Int32 nLimit
[done
] = {0,12,31,0,59,59,0};
441 State eState
= (bDate
? month
: minute
);
442 rCurFmtType
= (bDate
? SvNumFormatType::DATE
: SvNumFormatType::TIME
);
443 nUnit
[eState
-1] = o3tl::toInt32(rStr
.subView( 0, nParseEnd
));
444 const sal_Unicode
* pLastStart
= p
;
445 // Ensure there's no preceding sign. Negative dates
446 // currently aren't handled correctly. Also discard
449 while (p
< pStop
&& *p
== ' ')
451 if (p
< pStop
&& !rtl::isAsciiDigit(*p
))
452 rError
= nStringNoValueError
;
454 while (p
< pStop
&& rError
== FormulaError::NONE
&& eState
< blank
)
456 if (eState
== minute
)
457 rCurFmtType
|= SvNumFormatType::TIME
;
458 if (rtl::isAsciiDigit(*p
))
460 // Maximum 2 digits per unit, except fractions.
461 if (p
- pLastStart
>= 2 && eState
!= fraction
)
462 rError
= nStringNoValueError
;
464 else if (p
> pLastStart
)
466 // We had at least one digit.
469 nUnit
[eState
] = o3tl::toInt32(rStr
.subView( pLastStart
- pStart
, p
- pLastStart
));
470 if (nLimit
[eState
] && nLimit
[eState
] < nUnit
[eState
])
471 rError
= nStringNoValueError
;
473 pLastStart
= p
+ 1; // hypothetical next start
474 // Delimiters must match, a trailing delimiter
475 // yields an invalid date/time.
479 // Month must be followed by separator and
480 // day, no trailing blanks.
481 if (*p
!= '-' || (p
+1 == pStop
))
482 rError
= nStringNoValueError
;
485 if ((*p
!= 'T' || (p
+1 == pStop
)) && *p
!= ' ')
486 rError
= nStringNoValueError
;
487 // Take one blank as a valid delimiter
488 // between date and time.
491 // Hour must be followed by separator and
492 // minute, no trailing blanks.
493 if (*p
!= ':' || (p
+1 == pStop
))
494 rError
= nStringNoValueError
;
497 if ((*p
!= ':' || (p
+1 == pStop
)) && *p
!= ' ')
498 rError
= nStringNoValueError
;
503 if (((*p
!= ',' && *p
!= '.') || (p
+1 == pStop
)) && *p
!= ' ')
504 rError
= nStringNoValueError
;
512 rError
= nStringNoValueError
;
515 eState
= static_cast<State
>(eState
+ 1);
518 rError
= nStringNoValueError
;
523 while (p
< pStop
&& *p
== ' ')
526 rError
= nStringNoValueError
;
530 // Month without day, or hour without minute.
531 if (eState
== month
|| (eState
== day
&& p
<= pLastStart
) ||
532 eState
== hour
|| (eState
== minute
&& p
<= pLastStart
))
533 rError
= nStringNoValueError
;
535 if (rError
== FormulaError::NONE
)
537 // Catch the very last unit at end of string.
538 if (p
> pLastStart
&& eState
< done
)
540 nUnit
[eState
] = o3tl::toInt32(rStr
.subView( pLastStart
- pStart
, p
- pLastStart
));
541 if (nLimit
[eState
] && nLimit
[eState
] < nUnit
[eState
])
542 rError
= nStringNoValueError
;
544 if (bDate
&& nUnit
[hour
] > 23)
545 rError
= nStringNoValueError
;
546 if (rError
== FormulaError::NONE
)
548 if (bDate
&& nUnit
[day
] == 0)
550 double fFraction
= (nUnit
[fraction
] <= 0 ? 0.0 :
551 ::rtl::math::pow10Exp( nUnit
[fraction
],
552 static_cast<int>( -ceil( log10( static_cast<double>( nUnit
[fraction
]))))));
558 sal::static_int_cast
<sal_Int16
>(nUnit
[day
]),
559 sal::static_int_cast
<sal_Int16
>(nUnit
[month
]),
560 sal::static_int_cast
<sal_Int16
>(nUnit
[year
]));
561 if (!aDate
.IsValidDate())
562 rError
= nStringNoValueError
;
565 fValue
= aDate
- rContext
.NFGetNullDate();
568 fValue
+= ((nUnit
[hour
] * 3600) + (nUnit
[minute
] * 60) + nUnit
[second
] + fFraction
) / 86400.0;
574 rError
= nStringNoValueError
;
576 if (rError
!= FormulaError::NONE
)
582 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */