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/pathoptions.hxx>
23 #include <unotools/useroptions.hxx>
24 #include <tools/urlobj.hxx>
25 #include <unotools/charclass.hxx>
28 #include <unotools/syslocale.hxx>
29 #include <svl/zforlist.hxx>
30 #include <formula/errorcodes.hxx>
33 #include "rangeutl.hxx"
34 #include "rechead.hxx"
35 #include "compiler.hxx"
36 #include "paramisc.hxx"
37 #include "calcconfig.hxx"
40 #include "globstr.hrc"
44 // struct ScImportParam:
46 ScImportParam::ScImportParam() :
58 ScImportParam::ScImportParam( const ScImportParam
& r
) :
65 aStatement (r
.aStatement
),
72 ScImportParam::~ScImportParam()
76 ScImportParam
& ScImportParam::operator=( const ScImportParam
& r
)
84 aStatement
= r
.aStatement
;
92 bool ScImportParam::operator==( const ScImportParam
& rOther
) const
94 return( nCol1
== rOther
.nCol1
&&
95 nRow1
== rOther
.nRow1
&&
96 nCol2
== rOther
.nCol2
&&
97 nRow2
== rOther
.nRow2
&&
98 bImport
== rOther
.bImport
&&
99 aDBName
== rOther
.aDBName
&&
100 aStatement
== rOther
.aStatement
&&
101 bNative
== rOther
.bNative
&&
102 bSql
== rOther
.bSql
&&
103 nType
== rOther
.nType
);
105 //TODO: are nQuerySh and pConnection equal ?
108 // struct ScConsolidateParam:
110 ScConsolidateParam::ScConsolidateParam() :
116 ScConsolidateParam::ScConsolidateParam( const ScConsolidateParam
& r
) :
117 nCol(r
.nCol
),nRow(r
.nRow
),nTab(r
.nTab
),
118 eFunction(r
.eFunction
),nDataAreaCount(0),
120 bByCol(r
.bByCol
),bByRow(r
.bByRow
),bReferenceData(r
.bReferenceData
)
122 if ( r
.nDataAreaCount
> 0 )
124 nDataAreaCount
= r
.nDataAreaCount
;
125 ppDataAreas
= new ScArea
*[nDataAreaCount
];
126 for ( sal_uInt16 i
=0; i
<nDataAreaCount
; i
++ )
127 ppDataAreas
[i
] = new ScArea( *(r
.ppDataAreas
[i
]) );
131 ScConsolidateParam::~ScConsolidateParam()
136 void ScConsolidateParam::ClearDataAreas()
140 for ( sal_uInt16 i
=0; i
<nDataAreaCount
; i
++ )
141 delete ppDataAreas
[i
];
142 delete [] ppDataAreas
;
148 void ScConsolidateParam::Clear()
155 bByCol
= bByRow
= bReferenceData
= false;
156 eFunction
= SUBTOTAL_FUNC_SUM
;
159 ScConsolidateParam
& ScConsolidateParam::operator=( const ScConsolidateParam
& r
)
166 bReferenceData
= r
.bReferenceData
;
167 eFunction
= r
.eFunction
;
168 SetAreas( r
.ppDataAreas
, r
.nDataAreaCount
);
173 bool ScConsolidateParam::operator==( const ScConsolidateParam
& r
) const
175 bool bEqual
= (nCol
== r
.nCol
)
178 && (bByCol
== r
.bByCol
)
179 && (bByRow
== r
.bByRow
)
180 && (bReferenceData
== r
.bReferenceData
)
181 && (nDataAreaCount
== r
.nDataAreaCount
)
182 && (eFunction
== r
.eFunction
);
184 if ( nDataAreaCount
== 0 )
185 bEqual
= bEqual
&& (ppDataAreas
== NULL
) && (r
.ppDataAreas
== NULL
);
187 bEqual
= bEqual
&& (ppDataAreas
!= NULL
) && (r
.ppDataAreas
!= NULL
);
189 if ( bEqual
&& (nDataAreaCount
> 0) )
190 for ( sal_uInt16 i
=0; i
<nDataAreaCount
&& bEqual
; i
++ )
191 bEqual
= *(ppDataAreas
[i
]) == *(r
.ppDataAreas
[i
]);
196 void ScConsolidateParam::SetAreas( ScArea
* const* ppAreas
, sal_uInt16 nCount
)
199 if ( ppAreas
&& nCount
> 0 )
201 ppDataAreas
= new ScArea
*[nCount
];
202 for ( sal_uInt16 i
=0; i
<nCount
; i
++ )
203 ppDataAreas
[i
] = new ScArea( *(ppAreas
[i
]) );
204 nDataAreaCount
= nCount
;
208 // struct ScSolveParam
210 ScSolveParam::ScSolveParam()
211 : pStrTargetVal( NULL
)
215 ScSolveParam::ScSolveParam( const ScSolveParam
& r
)
216 : aRefFormulaCell ( r
.aRefFormulaCell
),
217 aRefVariableCell( r
.aRefVariableCell
),
218 pStrTargetVal ( r
.pStrTargetVal
219 ? new OUString(*r
.pStrTargetVal
)
224 ScSolveParam::ScSolveParam( const ScAddress
& rFormulaCell
,
225 const ScAddress
& rVariableCell
,
226 const OUString
& rTargetValStr
)
227 : aRefFormulaCell ( rFormulaCell
),
228 aRefVariableCell( rVariableCell
),
229 pStrTargetVal ( new OUString(rTargetValStr
) )
233 ScSolveParam::~ScSolveParam()
235 delete pStrTargetVal
;
238 ScSolveParam
& ScSolveParam::operator=( const ScSolveParam
& r
)
240 delete pStrTargetVal
;
242 aRefFormulaCell
= r
.aRefFormulaCell
;
243 aRefVariableCell
= r
.aRefVariableCell
;
244 pStrTargetVal
= r
.pStrTargetVal
245 ? new OUString(*r
.pStrTargetVal
)
250 bool ScSolveParam::operator==( const ScSolveParam
& r
) const
252 bool bEqual
= (aRefFormulaCell
== r
.aRefFormulaCell
)
253 && (aRefVariableCell
== r
.aRefVariableCell
);
257 if ( !pStrTargetVal
&& !r
.pStrTargetVal
)
259 else if ( !pStrTargetVal
|| !r
.pStrTargetVal
)
261 else if ( pStrTargetVal
&& r
.pStrTargetVal
)
262 bEqual
= ( *pStrTargetVal
== *(r
.pStrTargetVal
) );
268 // struct ScTabOpParam
270 ScTabOpParam::ScTabOpParam() : meMode(Column
) {}
272 ScTabOpParam::ScTabOpParam( const ScTabOpParam
& r
)
273 : aRefFormulaCell ( r
.aRefFormulaCell
),
274 aRefFormulaEnd ( r
.aRefFormulaEnd
),
275 aRefRowCell ( r
.aRefRowCell
),
276 aRefColCell ( r
.aRefColCell
),
281 ScTabOpParam::ScTabOpParam( const ScRefAddress
& rFormulaCell
,
282 const ScRefAddress
& rFormulaEnd
,
283 const ScRefAddress
& rRowCell
,
284 const ScRefAddress
& rColCell
,
286 : aRefFormulaCell ( rFormulaCell
),
287 aRefFormulaEnd ( rFormulaEnd
),
288 aRefRowCell ( rRowCell
),
289 aRefColCell ( rColCell
),
294 ScTabOpParam
& ScTabOpParam::operator=( const ScTabOpParam
& r
)
296 aRefFormulaCell
= r
.aRefFormulaCell
;
297 aRefFormulaEnd
= r
.aRefFormulaEnd
;
298 aRefRowCell
= r
.aRefRowCell
;
299 aRefColCell
= r
.aRefColCell
;
304 bool ScTabOpParam::operator==( const ScTabOpParam
& r
) const
306 return ( (aRefFormulaCell
== r
.aRefFormulaCell
)
307 && (aRefFormulaEnd
== r
.aRefFormulaEnd
)
308 && (aRefRowCell
== r
.aRefRowCell
)
309 && (aRefColCell
== r
.aRefColCell
)
310 && (meMode
== r
.meMode
) );
313 OUString
ScGlobal::GetAbsDocName( const OUString
& rFileName
,
314 SfxObjectShell
* pShell
)
317 if ( !pShell
->HasName() )
318 { // maybe relative to document path working directory
320 SvtPathOptions aPathOpt
;
321 aObj
.SetSmartURL( aPathOpt
.GetWorkPath() );
322 aObj
.setFinalSlash(); // it IS a path
324 aAbsName
= aObj
.smartRel2Abs( rFileName
, bWasAbs
).GetMainURL(INetURLObject::NO_DECODE
);
325 // returned string must be encoded because it's used directly to create SfxMedium
329 const SfxMedium
* pMedium
= pShell
->GetMedium();
333 aAbsName
= pMedium
->GetURLObject().smartRel2Abs( rFileName
, bWasAbs
).GetMainURL(INetURLObject::NO_DECODE
);
336 { // This can't happen, but ...
337 // just to be sure to have the same encoding
339 aObj
.SetSmartURL( aAbsName
);
340 aAbsName
= aObj
.GetMainURL(INetURLObject::NO_DECODE
);
346 OUString
ScGlobal::GetDocTabName( const OUString
& rFileName
,
347 const OUString
& rTabName
)
349 OUString
aDocTab('\'');
350 aDocTab
+= rFileName
;
352 while( (nPos
= aDocTab
.indexOf( '\'', nPos
)) != -1 )
354 aDocTab
= aDocTab
.replaceAt( nPos
, 0, "\\" );
358 aDocTab
+= OUString(SC_COMPILER_FILE_TAB_SEP
);
359 aDocTab
+= rTabName
; // "'Doc'#Tab"
365 bool isEmptyString( const OUString
& rStr
)
369 else if (rStr
[0] == ' ')
371 const sal_Unicode
* p
= rStr
.getStr() + 1;
372 const sal_Unicode
* const pStop
= p
- 1 + rStr
.getLength();
373 while (p
< pStop
&& *p
== ' ')
382 double ScGlobal::ConvertStringToValue( const OUString
& rStr
, const ScCalcConfig
& rConfig
,
383 sal_uInt16
& rError
, sal_uInt16 nStringNoValueError
,
384 SvNumberFormatter
* pFormatter
, short & rCurFmtType
)
386 // We keep ScCalcConfig::StringConversion::LOCALE default until
387 // we provide a friendly way to convert string numbers into numbers in the UI.
390 if (nStringNoValueError
== errCellNoValue
)
392 // Requested that all strings result in 0, error handled by caller.
393 rError
= nStringNoValueError
;
397 switch (rConfig
.meStringConversion
)
399 case ScCalcConfig::StringConversion::ILLEGAL
:
400 rError
= nStringNoValueError
;
402 case ScCalcConfig::StringConversion::ZERO
:
404 case ScCalcConfig::StringConversion::LOCALE
:
406 if (rConfig
.mbEmptyStringAsZero
)
408 // The number scanner does not accept empty strings or strings
409 // containing only spaces, be on par in these cases with what was
410 // accepted in OOo and is in AOO (see also the
411 // StringConversion::UNAMBIGUOUS branch) and convert to 0 to prevent
412 // interoperability nightmares.
414 if (isEmptyString( rStr
))
419 goto Label_fallback_to_unambiguous
;
421 sal_uInt32 nFIndex
= 0;
422 if (!pFormatter
->IsNumberFormat(rStr
, nFIndex
, fValue
))
424 rError
= nStringNoValueError
;
430 case ScCalcConfig::StringConversion::UNAMBIGUOUS
:
431 Label_fallback_to_unambiguous
:
433 if (!rConfig
.mbEmptyStringAsZero
)
435 if (isEmptyString( rStr
))
437 rError
= nStringNoValueError
;
442 // continue below, pulled from switch case for better readability
446 OUString
aStr( rStr
);
447 rtl_math_ConversionStatus eStatus
;
449 // Decimal and group separator 0 => only integer and possibly exponent,
450 // stops at first non-digit non-sign.
451 fValue
= ::rtl::math::stringToDouble( aStr
, 0, 0, &eStatus
, &nParseEnd
);
453 if (eStatus
== rtl_math_ConversionStatus_Ok
&& nParseEnd
< (nLen
= aStr
.getLength()))
455 // Not at string end, check for trailing blanks or switch to date or
456 // time parsing or bail out.
457 const sal_Unicode
* const pStart
= aStr
.getStr();
458 const sal_Unicode
* p
= pStart
+ nParseEnd
;
459 const sal_Unicode
* const pStop
= pStart
+ nLen
;
463 while (p
< pStop
&& *p
== ' ')
466 rError
= nStringNoValueError
;
471 bool bDate
= (*(p
-1) == '-');
472 enum State
{ year
= 0, month
, day
, hour
, minute
, second
, fraction
, done
, blank
, stop
};
473 sal_Int32 nUnit
[done
] = {0,0,0,0,0,0,0};
474 const sal_Int32 nLimit
[done
] = {0,12,31,0,59,59,0};
475 State eState
= (bDate
? month
: minute
);
476 rCurFmtType
= (bDate
? css::util::NumberFormat::DATE
: css::util::NumberFormat::TIME
);
477 nUnit
[eState
-1] = aStr
.copy( 0, nParseEnd
).toInt32();
478 const sal_Unicode
* pLastStart
= p
;
479 // Ensure there's no preceding sign. Negative dates
480 // currently aren't handled correctly. Also discard
483 while (p
< pStop
&& *p
== ' ')
485 if (p
< pStop
&& !rtl::isAsciiDigit(*p
))
486 rError
= nStringNoValueError
;
488 while (p
< pStop
&& !rError
&& eState
< blank
)
490 if (eState
== minute
)
491 rCurFmtType
|= css::util::NumberFormat::TIME
;
492 if (rtl::isAsciiDigit(*p
))
494 // Maximum 2 digits per unit, except fractions.
495 if (p
- pLastStart
>= 2 && eState
!= fraction
)
496 rError
= nStringNoValueError
;
498 else if (p
> pLastStart
)
500 // We had at least one digit.
503 nUnit
[eState
] = aStr
.copy( pLastStart
- pStart
, p
- pLastStart
).toInt32();
504 if (nLimit
[eState
] && nLimit
[eState
] < nUnit
[eState
])
505 rError
= nStringNoValueError
;
507 pLastStart
= p
+ 1; // hypothetical next start
508 // Delimiters must match, a trailing delimiter
509 // yields an invalid date/time.
513 // Month must be followed by separator and
514 // day, no trailing blanks.
515 if (*p
!= '-' || (p
+1 == pStop
))
516 rError
= nStringNoValueError
;
519 if ((*p
!= 'T' || (p
+1 == pStop
)) && *p
!= ' ')
520 rError
= nStringNoValueError
;
521 // Take one blank as a valid delimiter
522 // between date and time.
525 // Hour must be followed by separator and
526 // minute, no trailing blanks.
527 if (*p
!= ':' || (p
+1 == pStop
))
528 rError
= nStringNoValueError
;
531 if ((*p
!= ':' || (p
+1 == pStop
)) && *p
!= ' ')
532 rError
= nStringNoValueError
;
537 if (((*p
!= ',' && *p
!= '.') || (p
+1 == pStop
)) && *p
!= ' ')
538 rError
= nStringNoValueError
;
549 rError
= nStringNoValueError
;
552 eState
= static_cast<State
>(eState
+ 1);
555 rError
= nStringNoValueError
;
560 while (p
< pStop
&& *p
== ' ')
563 rError
= nStringNoValueError
;
567 // Month without day, or hour without minute.
568 if (eState
== month
|| (eState
== day
&& p
<= pLastStart
) ||
569 eState
== hour
|| (eState
== minute
&& p
<= pLastStart
))
570 rError
= nStringNoValueError
;
574 // Catch the very last unit at end of string.
575 if (p
> pLastStart
&& eState
< done
)
577 nUnit
[eState
] = aStr
.copy( pLastStart
- pStart
, p
- pLastStart
).toInt32();
578 if (nLimit
[eState
] && nLimit
[eState
] < nUnit
[eState
])
579 rError
= nStringNoValueError
;
581 if (bDate
&& nUnit
[hour
] > 23)
582 rError
= nStringNoValueError
;
585 if (bDate
&& nUnit
[day
] == 0)
587 double fFraction
= (nUnit
[fraction
] <= 0 ? 0.0 :
588 ::rtl::math::pow10Exp( nUnit
[fraction
],
589 static_cast<int>( -ceil( log10( static_cast<double>( nUnit
[fraction
]))))));
595 sal::static_int_cast
<sal_Int16
>(nUnit
[day
]),
596 sal::static_int_cast
<sal_Int16
>(nUnit
[month
]),
597 sal::static_int_cast
<sal_Int16
>(nUnit
[year
]));
598 if (!aDate
.IsValidDate())
599 rError
= nStringNoValueError
;
603 fValue
= aDate
- *(pFormatter
->GetNullDate());
606 SAL_WARN("sc.core","ScGlobal::ConvertStringToValue - fixed null date");
607 static Date
aDefaultNullDate( 30, 12, 1899);
608 fValue
= aDate
- aDefaultNullDate
;
612 fValue
+= ((nUnit
[hour
] * 3600) + (nUnit
[minute
] * 60) + nUnit
[second
] + fFraction
) / 86400.0;
618 rError
= nStringNoValueError
;
626 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */