cool#10610 Ensure the parent-child relations of comments.
[LibreOffice.git] / basic / source / sbx / sbxscan.cxx
blobf843d6b44e139d38ca76970950e1c5248e07ee49
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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 <sal/config.h>
22 #include <cstdlib>
23 #include <string_view>
25 #include <config_features.h>
27 #include <comphelper/errcode.hxx>
28 #include <unotools/resmgr.hxx>
29 #include "sbxconv.hxx"
31 #include <unotools/syslocale.hxx>
32 #include <unotools/charclass.hxx>
34 #include <vcl/svapp.hxx>
35 #include <vcl/settings.hxx>
37 #include <math.h>
39 #include <sbxbase.hxx>
40 #include <sbintern.hxx>
41 #include <sbxform.hxx>
43 #include <date.hxx>
44 #include <runtime.hxx>
45 #include <strings.hrc>
47 #include <rtl/character.hxx>
48 #include <rtl/math.hxx>
49 #include <svl/numformat.hxx>
50 #include <svl/zforlist.hxx>
51 #include <o3tl/temporary.hxx>
52 #include <o3tl/string_view.hxx>
53 #include <officecfg/Office/Scripting.hxx>
56 void ImpGetIntntlSep( sal_Unicode& rcDecimalSep, sal_Unicode& rcThousandSep, sal_Unicode& rcDecimalSepAlt )
58 SvtSysLocale aSysLocale;
59 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
60 rcDecimalSep = rData.getNumDecimalSep()[0];
61 rcThousandSep = rData.getNumThousandSep()[0];
62 rcDecimalSepAlt = rData.getNumDecimalSepAlt().toChar();
66 static bool ImpStrChr( std::u16string_view str, sal_Unicode c ) { return str.find(c) != std::u16string_view::npos; }
69 // scanning a string according to BASIC-conventions
70 // but exponent may also be a D, so data type is SbxDOUBLE
71 // conversion error if data type is fixed and it doesn't fit
73 ErrCode ImpScan( std::u16string_view rWSrc, double& nVal, SbxDataType& rType,
74 sal_Int32* pLen, bool* pHasNumber, bool bOnlyIntntl )
76 sal_Unicode cDecSep, cGrpSep, cDecSepAlt;
77 if( bOnlyIntntl )
79 ImpGetIntntlSep(cDecSep, cGrpSep, cDecSepAlt);
80 // Ensure that the decimal separator alternative is really one.
81 if (cDecSepAlt == cDecSep)
82 cDecSepAlt = 0;
84 else
86 cDecSep = '.';
87 cGrpSep = 0; // no group separator accepted in non-i18n
88 cDecSepAlt = 0;
91 auto const pStart = rWSrc.begin();
92 auto p = pStart;
93 bool bMinus = false;
94 nVal = 0;
95 SbxDataType eScanType = SbxSINGLE;
96 while (p != rWSrc.end() && (*p == ' ' || *p == '\t'))
97 p++;
98 if (p != rWSrc.end() && *p == '+')
99 p++;
100 else if (p != rWSrc.end() && *p == '-')
102 p++;
103 bMinus = true;
105 #if HAVE_FEATURE_SCRIPTING
106 if (SbiRuntime::isVBAEnabled())
108 while (p != rWSrc.end() && (*p == ' ' || *p == '\t'))
109 p++;
111 #endif
112 const auto pNumberStart = p;
113 if (p != rWSrc.end()
114 && (rtl::isAsciiDigit(*p)
115 || ((*p == cDecSep || (cGrpSep && *p == cGrpSep) || (cDecSepAlt && *p == cDecSepAlt))
116 && p + 1 != rWSrc.end() && rtl::isAsciiDigit(*(p + 1)))))
118 bool exp = false;
119 bool decsep = false;
120 short ndig = 0;
121 short ncdig = 0; // number of digits after decimal point
122 OUStringBuffer aSearchStr("0123456789DEde" + OUStringChar(cDecSep));
123 if (cDecSepAlt)
124 aSearchStr.append(cDecSepAlt);
125 if (cGrpSep)
126 aSearchStr.append(cGrpSep);
127 OUStringBuffer aBuf(rWSrc.end() - p);
128 for (; p != rWSrc.end() && ImpStrChr(aSearchStr, *p); ++p)
130 if (rtl::isAsciiDigit(*p))
132 aBuf.append(*p);
133 if (!exp)
135 ndig++;
136 if (decsep)
137 ncdig++;
140 else if (cGrpSep && *p == cGrpSep)
142 aBuf.append(*p);
144 else if (*p == cDecSep || (cDecSepAlt && *p == cDecSepAlt))
146 if (decsep)
147 return ERRCODE_BASIC_CONVERSION;
148 decsep = true;
150 // Use the separator that is passed to stringToDouble()
151 aBuf.append(cDecSep);
153 else // DdEe
155 if (exp)
156 return ERRCODE_BASIC_CONVERSION;
157 exp = true;
159 if( *p == 'D' || *p == 'd' )
160 eScanType = SbxDOUBLE;
161 aBuf.append('E');
162 if (auto pNext = p + 1; pNext != rWSrc.end())
164 if (*pNext == '+')
165 ++p;
166 else if (*pNext == '-')
168 aBuf.append('-');
169 ++p;
175 rtl_math_ConversionStatus eStatus = rtl_math_ConversionStatus_Ok;
176 sal_Int32 nParseEnd = 0;
177 nVal = rtl::math::stringToDouble(aBuf, cDecSep, cGrpSep, &eStatus, &nParseEnd);
178 if( eStatus != rtl_math_ConversionStatus_Ok || nParseEnd != aBuf.getLength() )
179 return ERRCODE_BASIC_CONVERSION;
181 if( !decsep && !exp )
183 if( nVal >= SbxMININT && nVal <= SbxMAXINT )
184 eScanType = SbxINTEGER;
185 else if( nVal >= SbxMINLNG && nVal <= SbxMAXLNG )
186 eScanType = SbxLONG;
189 // too many numbers for SINGLE?
190 if( ndig > 15 || ncdig > 6 )
191 eScanType = SbxDOUBLE;
193 // type detection?
194 static constexpr std::u16string_view pTypes = u"%!&#";
195 if (p != rWSrc.end() && ImpStrChr(pTypes, *p))
196 p++;
198 // hex/octal number? read in and convert:
199 else if (p != rWSrc.end() && *p == '&')
201 if (++p == rWSrc.end())
202 return ERRCODE_BASIC_CONVERSION;
203 eScanType = SbxLONG;
204 auto isValidDigit = rtl::isAsciiHexDigit<sal_Unicode>;
205 char base = 16;
206 char ndig = 8;
207 switch( *p++ )
209 case 'O':
210 case 'o':
211 isValidDigit = rtl::isAsciiOctalDigit<sal_Unicode>;
212 base = 8;
213 ndig = 11;
214 break;
215 case 'H':
216 case 'h':
217 break;
218 default :
219 return ERRCODE_BASIC_CONVERSION;
221 const auto pDigitsStart = p;
222 for (; p != rWSrc.end() && rtl::isAsciiAlphanumeric(*p); ++p)
224 if (!isValidDigit(*p))
225 return ERRCODE_BASIC_CONVERSION;
227 if (p - pDigitsStart > ndig)
228 return ERRCODE_BASIC_CONVERSION;
229 sal_Int32 l = o3tl::toInt32(rWSrc.substr(pDigitsStart - pStart, p - pDigitsStart), base);
230 if (p != rWSrc.end() && *p == '&')
231 p++;
232 nVal = static_cast<double>(l);
233 if( l >= SbxMININT && l <= SbxMAXINT )
234 eScanType = SbxINTEGER;
236 #if HAVE_FEATURE_SCRIPTING
237 else if ( SbiRuntime::isVBAEnabled() )
239 return ERRCODE_BASIC_CONVERSION;
241 #endif
242 const auto pNumberEnd = p;
243 // tdf#146672 - skip whitespaces and tabs at the end of the scanned string
244 while (p != rWSrc.end() && (*p == ' ' || *p == '\t'))
245 p++;
246 if( pLen )
247 *pLen = p - pStart;
248 if (pHasNumber)
249 *pHasNumber = pNumberEnd > pNumberStart;
250 if( bMinus )
251 nVal = -nVal;
252 rType = eScanType;
253 return ERRCODE_NONE;
256 ErrCode ImpScan(std::u16string_view rSrc, double& nVal, SbxDataType& rType, sal_Int32* pLen)
258 using namespace officecfg::Office::Scripting;
259 static const bool bEnv = std::getenv("LIBREOFFICE6FLOATINGPOINTMODE") != nullptr;
260 bool bMode = bEnv || Basic::Compatibility::UseLibreOffice6FloatingPointConversion::get();
262 return ImpScan(rSrc, nVal, rType, pLen, nullptr, !bMode);
265 // port for CDbl in the Basic
266 ErrCode SbxValue::ScanNumIntnl( const OUString& rSrc, double& nVal, bool bSingle )
268 sal_Int32 nLen = 0;
269 ErrCode nRetError = ImpScan( rSrc, nVal, o3tl::temporary(SbxDataType()), &nLen, nullptr,
270 /*bOnlyIntntl*/true );
271 // read completely?
272 if( nRetError == ERRCODE_NONE && nLen != rSrc.getLength() )
274 nRetError = ERRCODE_BASIC_CONVERSION;
276 if( bSingle )
278 SbxValues aValues( nVal );
279 nVal = static_cast<double>(ImpGetSingle( &aValues )); // here error at overflow
281 return nRetError;
284 // The number is prepared unformattedly with the given number of
285 // NK-positions. A leading minus is added if applicable.
286 // This routine is public because it's also used by the Put-functions
287 // in the class SbxImpSTRING.
289 void ImpCvtNum( double nNum, short nPrec, OUString& rRes, bool bCoreString )
291 sal_Unicode cDecimalSep;
292 if( bCoreString )
293 cDecimalSep = '.';
294 else
295 ImpGetIntntlSep(cDecimalSep, o3tl::temporary(sal_Unicode()), o3tl::temporary(sal_Unicode()));
297 // tdf#143575 - use rtl::math::doubleToUString to convert numbers to strings in basic
298 rRes = rtl::math::doubleToUString(nNum, rtl_math_StringFormat_Automatic, nPrec, cDecimalSep, true);
301 // formatted number output
303 static void printfmtstr(std::u16string_view rStr, OUString& rRes, std::u16string_view rFmt)
305 if (rFmt.empty())
306 rFmt = u"&";
308 OUStringBuffer aTemp;
309 auto pStr = rStr.begin();
310 auto pFmt = rFmt.begin();
312 switch( *pFmt )
314 case '!':
315 if (pStr != rStr.end())
316 aTemp.append(*pStr);
317 break;
318 case '\\':
321 aTemp.append(pStr != rStr.end() ? *pStr++ : u' ');
322 } while (++pFmt != rFmt.end() && *pFmt != '\\');
323 aTemp.append(pStr != rStr.end() ? *pStr : u' ');
324 break;
325 case '&':
326 default:
327 aTemp = rStr;
328 break;
330 rRes = aTemp.makeStringAndClear();
334 bool SbxValue::Scan(std::u16string_view rSrc, sal_Int32* pLen)
336 ErrCode eRes = ERRCODE_NONE;
337 if( !CanWrite() )
339 eRes = ERRCODE_BASIC_PROP_READONLY;
341 else
343 double n;
344 SbxDataType t;
345 eRes = ImpScan( rSrc, n, t, pLen );
346 if( eRes == ERRCODE_NONE )
348 if( !IsFixed() )
350 SetType( t );
352 PutDouble( n );
355 if( eRes )
357 SetError( eRes );
358 return false;
360 else
362 return true;
366 std::locale BasResLocale()
368 return Translate::Create("sb");
371 OUString BasResId(TranslateId aId)
373 return Translate::get(aId, BasResLocale());
376 namespace
379 enum class VbaFormatType
381 Offset, // standard number format
382 UserDefined, // user defined number format
385 #if HAVE_FEATURE_SCRIPTING
387 struct VbaFormatInfo
389 VbaFormatType meType;
390 std::u16string_view mpVbaFormat; // Format string in vba
391 NfIndexTableOffset meOffset; // SvNumberFormatter format index, if meType = VbaFormatType::Offset
392 OUString mpOOoFormat; // if meType = VbaFormatType::UserDefined
395 const VbaFormatInfo* getFormatInfo( std::u16string_view rFmt )
397 static constexpr const VbaFormatInfo formatInfoTable[] =
399 { VbaFormatType::Offset, u"Long Date", NF_DATE_SYSTEM_LONG, u""_ustr },
400 { VbaFormatType::UserDefined, u"Medium Date", NF_NUMBER_STANDARD, u"DD-MMM-YY"_ustr },
401 { VbaFormatType::Offset, u"Short Date", NF_DATE_SYSTEM_SHORT, u""_ustr },
402 { VbaFormatType::UserDefined, u"Long Time", NF_NUMBER_STANDARD, u"H:MM:SS AM/PM"_ustr },
403 { VbaFormatType::Offset, u"Medium Time", NF_TIME_HHMMAMPM, u""_ustr },
404 { VbaFormatType::Offset, u"Short Time", NF_TIME_HHMM, u""_ustr },
405 { VbaFormatType::Offset, u"ddddd", NF_DATE_SYSTEM_SHORT, u""_ustr },
406 { VbaFormatType::Offset, u"dddddd", NF_DATE_SYSTEM_LONG, u""_ustr },
407 { VbaFormatType::UserDefined, u"ttttt", NF_NUMBER_STANDARD, u"H:MM:SS AM/PM"_ustr },
408 { VbaFormatType::Offset, u"ww", NF_DATE_WW, u""_ustr },
411 for (auto& info : formatInfoTable)
412 if (o3tl::equalsIgnoreAsciiCase(rFmt, info.mpVbaFormat))
413 return &info;
414 return nullptr;
416 #endif
418 void BasicFormatNum(double d, const OUString& rFmt, OUString& rRes)
420 SbxAppData& rAppData = GetSbxData_Impl();
422 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
423 if (rAppData.pBasicFormater)
424 if (rAppData.eBasicFormaterLangType != eLangType)
425 rAppData.pBasicFormater.reset();
426 rAppData.eBasicFormaterLangType = eLangType;
428 if (!rAppData.pBasicFormater)
430 SvtSysLocale aSysLocale;
431 const LocaleDataWrapper& rData = aSysLocale.GetLocaleData();
432 sal_Unicode cComma = rData.getNumDecimalSep()[0];
433 sal_Unicode c1000 = rData.getNumThousandSep()[0];
434 const OUString& aCurrencyStrg = rData.getCurrSymbol();
436 // initialize the Basic-formater help object:
437 // get resources for predefined output
438 // of the Format()-command, e. g. for "On/Off"
439 OUString aOnStrg = BasResId(STR_BASICKEY_FORMAT_ON);
440 OUString aOffStrg = BasResId(STR_BASICKEY_FORMAT_OFF);
441 OUString aYesStrg = BasResId(STR_BASICKEY_FORMAT_YES);
442 OUString aNoStrg = BasResId(STR_BASICKEY_FORMAT_NO);
443 OUString aTrueStrg = BasResId(STR_BASICKEY_FORMAT_TRUE);
444 OUString aFalseStrg = BasResId(STR_BASICKEY_FORMAT_FALSE);
445 OUString aCurrencyFormatStrg = BasResId(STR_BASICKEY_FORMAT_CURRENCY);
447 rAppData.pBasicFormater = std::make_unique<SbxBasicFormater>(
448 cComma, c1000, aOnStrg, aOffStrg, aYesStrg, aNoStrg, aTrueStrg, aFalseStrg,
449 aCurrencyStrg, aCurrencyFormatStrg);
451 // Remark: For performance reasons there's only ONE BasicFormater-
452 // object created and 'stored', so that the expensive resource-
453 // loading is saved (for country-specific predefined outputs,
454 // e. g. "On/Off") and the continuous string-creation
455 // operations, too.
456 // BUT: therefore this code is NOT multithreading capable!
457 rRes = rAppData.pBasicFormater->BasicFormat(d, rFmt);
460 void BasicFormatNum(double d, const OUString* pFmt, SbxDataType eType, OUString& rRes)
462 if (pFmt)
463 BasicFormatNum(d, *pFmt, rRes);
464 else
465 ImpCvtNum(d, eType == SbxSINGLE ? 6 : eType == SbxDOUBLE ? 14 : 0, rRes);
468 #if HAVE_FEATURE_SCRIPTING
469 // For numeric types, takes the number directly; otherwise, tries to take string and convert it
470 bool GetNumberIntl(const SbxValue& val, double& ret)
472 switch (val.GetType())
474 case SbxCHAR:
475 case SbxBYTE:
476 case SbxINTEGER:
477 case SbxUSHORT:
478 case SbxLONG:
479 case SbxULONG:
480 case SbxINT:
481 case SbxUINT:
482 case SbxSINGLE:
483 case SbxDOUBLE:
484 case SbxDATE:
485 ret = val.GetDouble();
486 return true;
487 case SbxSTRING:
488 default:
489 return SbxValue::ScanNumIntnl(val.GetOUString(), ret) == ERRCODE_NONE;
492 #endif
493 } // namespace
495 void SbxValue::Format( OUString& rRes, const OUString* pFmt ) const
497 if (pFmt)
499 if (*pFmt == "<") // VBA lowercase
501 rRes = SvtSysLocale().GetCharClass().lowercase(GetOUString());
502 return;
504 if (*pFmt == ">") // VBA uppercase
506 rRes = SvtSysLocale().GetCharClass().uppercase(GetOUString());
507 return;
510 // pflin, It is better to use SvNumberFormatter to handle the date/time/number format.
511 // the SvNumberFormatter output is mostly compatible with
512 // VBA output besides the OOo-basic output
513 #if HAVE_FEATURE_SCRIPTING
514 // number format, use SvNumberFormatter to handle it.
515 if (double nNumber; !SbxBasicFormater::isBasicFormat(*pFmt) && GetNumberIntl(*this, nNumber))
517 LanguageType eLangType = Application::GetSettings().GetLanguageTag().getLanguageType();
518 std::shared_ptr<SvNumberFormatter> pFormatter;
519 if (GetSbData()->pInst)
521 pFormatter = GetSbData()->pInst->GetNumberFormatter();
523 else
525 sal_uInt32 n; // Dummy
526 pFormatter = SbiInstance::PrepareNumberFormatter(n, n, n);
529 sal_uInt32 nIndex;
530 const Color* pCol;
531 sal_Int32 nCheckPos = 0;
532 SvNumFormatType nType;
533 OUString aFmtStr = *pFmt;
534 if (const VbaFormatInfo* pInfo = getFormatInfo(aFmtStr))
536 if( pInfo->meType == VbaFormatType::Offset )
538 nIndex = pFormatter->GetFormatIndex( pInfo->meOffset, eLangType );
540 else
542 aFmtStr = pInfo->mpOOoFormat;
543 pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
545 pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
547 else if (aFmtStr.equalsIgnoreAsciiCase("General Date") // VBA general date variants
548 || aFmtStr.equalsIgnoreAsciiCase("c"))
550 OUString dateStr;
551 if( nNumber <=-1.0 || nNumber >= 1.0 )
553 // short date
554 nIndex = pFormatter->GetFormatIndex( NF_DATE_SYSTEM_SHORT, eLangType );
555 pFormatter->GetOutputString(nNumber, nIndex, dateStr, &pCol);
557 if (floor(nNumber) == nNumber)
559 rRes = dateStr;
560 return;
563 // long time
564 aFmtStr = u"H:MM:SS AM/PM"_ustr;
565 pFormatter->PutandConvertEntry(aFmtStr, nCheckPos, nType, nIndex,
566 LANGUAGE_ENGLISH_US, eLangType, true);
567 pFormatter->GetOutputString(nNumber, nIndex, rRes, &pCol);
568 if (!dateStr.isEmpty())
569 rRes = dateStr + " " + rRes;
571 else if (aFmtStr.equalsIgnoreAsciiCase("n") // VBA minute variants
572 || aFmtStr.equalsIgnoreAsciiCase("nn"))
574 sal_Int32 nMin = implGetMinute( nNumber );
575 if (nMin < 10 && aFmtStr.equalsIgnoreAsciiCase("nn"))
577 // Minute in two digits
578 sal_Unicode aBuf[2];
579 aBuf[0] = '0';
580 aBuf[1] = '0' + nMin;
581 rRes = OUString(aBuf, std::size(aBuf));
583 else
585 rRes = OUString::number(nMin);
588 else if (aFmtStr.equalsIgnoreAsciiCase("w")) // VBA weekday number
590 rRes = OUString::number(implGetWeekDay(nNumber));
592 else if (aFmtStr.equalsIgnoreAsciiCase("y")) // VBA year day number
594 sal_Int16 nYear = implGetDateYear( nNumber );
595 double dBaseDate;
596 implDateSerial( nYear, 1, 1, true, SbDateCorrection::None, dBaseDate );
597 sal_Int32 nYear32 = 1 + sal_Int32( nNumber - dBaseDate );
598 rRes = OUString::number(nYear32);
600 else
602 pFormatter->PutandConvertEntry( aFmtStr, nCheckPos, nType, nIndex, LANGUAGE_ENGLISH_US, eLangType, true);
603 pFormatter->GetOutputString( nNumber, nIndex, rRes, &pCol );
606 return;
608 #endif
611 SbxDataType eType = GetType();
612 switch( eType )
614 case SbxNULL:
615 rRes = SbxBasicFormater::BasicFormatNull(pFmt ? *pFmt : std::u16string_view{});
616 break;
617 case SbxCHAR:
618 case SbxBYTE:
619 case SbxINTEGER:
620 case SbxUSHORT:
621 case SbxLONG:
622 case SbxULONG:
623 case SbxINT:
624 case SbxUINT:
625 case SbxSINGLE:
626 case SbxDOUBLE:
627 BasicFormatNum(GetDouble(), pFmt, eType, rRes);
628 break;
629 case SbxSTRING:
630 rRes = GetOUString();
631 if( pFmt )
633 // #45355 converting if numeric
634 if (double d; ScanNumIntnl(rRes, d) == ERRCODE_NONE)
635 BasicFormatNum(d, *pFmt, rRes);
636 else
637 printfmtstr(rRes, rRes, *pFmt);
639 break;
640 default:
641 rRes = GetOUString();
646 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */