Coverage Report

Created: 2026-03-31 11:00

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/basic/source/sbx/sbxform.cxx
Line
Count
Source
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
21
#include <stdlib.h>
22
23
#include <sbxform.hxx>
24
#include <rtl/ustrbuf.hxx>
25
26
#include <rtl/character.hxx>
27
#include <o3tl/sprintf.hxx>
28
#include <o3tl/string_view.hxx>
29
#include <string_view>
30
#include <utility>
31
32
/*
33
TODO: are there any Star-Basic characteristics unconsidered?
34
35
        what means: * as placeholder
36
37
COMMENT: Visual-Basic treats the following (invalid) format-strings
38
      as shown:
39
40
        ##0##.##0##     --> ##000.000##
41
42
      (this class behaves the same way)
43
*/
44
45
#include <float.h>
46
#include <math.h>
47
48
0
#define NO_DIGIT_                   -1
49
50
0
#define MAX_NO_OF_DIGITS            DBL_DIG
51
#define MAX_DOUBLE_BUFFER_LENGTH    MAX_NO_OF_DIGITS + 9
52
                    // +1 for leading sign
53
                    // +1 for digit before the decimal point
54
                    // +1 for decimal point
55
                    // +2 for exponent E and exp. leading sign
56
                    // +3 for the exponent's value
57
                    // +1 for closing 0
58
59
0
#define CREATE_1000SEP_CHAR         '@'
60
61
0
#define FORMAT_SEPARATOR            ';'
62
63
// predefined formats for the Format$()-command:
64
constexpr std::u16string_view BASICFORMAT_GENERALNUMBER = u"General Number";
65
constexpr std::u16string_view BASICFORMAT_CURRENCY = u"Currency";
66
constexpr std::u16string_view BASICFORMAT_FIXED = u"Fixed";
67
constexpr std::u16string_view BASICFORMAT_STANDARD = u"Standard";
68
constexpr std::u16string_view BASICFORMAT_PERCENT = u"Percent";
69
constexpr std::u16string_view BASICFORMAT_SCIENTIFIC = u"Scientific";
70
constexpr std::u16string_view BASICFORMAT_YESNO = u"Yes/No";
71
constexpr std::u16string_view BASICFORMAT_TRUEFALSE = u"True/False";
72
constexpr std::u16string_view BASICFORMAT_ONOFF = u"On/Off";
73
74
// Comment: Visual-Basic has a maximum of 12 positions after the
75
//          decimal point for floating-point-numbers.
76
// all format-strings are compatible to Visual-Basic:
77
constexpr OUString GENERALNUMBER_FORMAT = u"0.############"_ustr;
78
constexpr OUString FIXED_FORMAT = u"0.00"_ustr;
79
constexpr OUString STANDARD_FORMAT = u"@0.00"_ustr;
80
constexpr OUString PERCENT_FORMAT = u"0.00%"_ustr;
81
constexpr OUString SCIENTIFIC_FORMAT = u"#.00E+00"_ustr;
82
// Comment: the character @ means that thousand-separators shall
83
//          be generated. That's a StarBasic 'extension'.
84
85
86
static double get_number_of_digits( double dNumber )
87
//double floor_log10_fabs( double dNumber )
88
0
{
89
0
    if( dNumber==0.0 )
90
0
        return 0.0; // used to be 1.0, now 0.0 because of #40025;
91
0
    else
92
0
        return floor( log10( fabs( dNumber ) ) );
93
0
}
94
95
96
SbxBasicFormater::SbxBasicFormater( sal_Unicode _cDecPoint, sal_Unicode _cThousandSep,
97
                      OUString _sOnStrg,
98
                      OUString _sOffStrg,
99
                      OUString _sYesStrg,
100
                      OUString _sNoStrg,
101
                      OUString _sTrueStrg,
102
                      OUString _sFalseStrg,
103
                      OUString _sCurrencyStrg,
104
                      OUString _sCurrencyFormatStrg )
105
0
    : cDecPoint(_cDecPoint)
106
0
    , cThousandSep(_cThousandSep)
107
0
    , sOnStrg(std::move(_sOnStrg))
108
0
    , sOffStrg(std::move(_sOffStrg))
109
0
    , sYesStrg(std::move(_sYesStrg))
110
0
    , sNoStrg(std::move(_sNoStrg))
111
0
    , sTrueStrg(std::move(_sTrueStrg))
112
0
    , sFalseStrg(std::move(_sFalseStrg))
113
0
    , sCurrencyStrg(std::move(_sCurrencyStrg))
114
0
    , sCurrencyFormatStrg(std::move(_sCurrencyFormatStrg))
115
0
    , dNum(0.0)
116
0
    , nNumExp(0)
117
0
    , nExpExp(0)
118
0
{
119
0
}
120
121
// function to output an error-text (for debugging)
122
// displaces all characters of the string, starting from nStartPos
123
// for one position to larger indexes, i. e. place for a new
124
// character (which is to be inserted) is created.
125
// ATTENTION: the string MUST be long enough!
126
inline void SbxBasicFormater::ShiftString( OUStringBuffer& sStrg, sal_uInt16 nStartPos )
127
0
{
128
0
    sStrg.remove(nStartPos,1);
129
0
}
130
131
void SbxBasicFormater::AppendDigit( OUStringBuffer& sStrg, short nDigit )
132
0
{
133
0
    if( nDigit>=0 && nDigit<=9 )
134
0
    {
135
0
        sStrg.append(static_cast<sal_Unicode>(nDigit+'0'));
136
0
    }
137
0
}
138
139
void SbxBasicFormater::LeftShiftDecimalPoint( OUStringBuffer& sStrg )
140
0
{
141
0
    sal_Int32 nPos = -1;
142
143
0
    for(sal_Int32 i = 0; i < sStrg.getLength(); i++)
144
0
    {
145
0
        if(sStrg[i] == cDecPoint)
146
0
        {
147
0
            nPos = i;
148
0
            break;
149
0
        }
150
0
    }
151
0
    if( nPos >= 0 )
152
0
    {
153
0
        sStrg[nPos] = sStrg[nPos - 1];
154
0
        sStrg[nPos - 1] = cDecPoint;
155
0
    }
156
0
}
157
158
// returns a flag if rounding a 9
159
void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos, bool& bOverflow )
160
0
{
161
0
    if( nPos<0 )
162
0
    {
163
0
        return;
164
0
    }
165
0
    bOverflow = false;
166
0
    sal_Unicode c = sStrg[nPos];
167
0
    if( nPos > 0 && (c == cDecPoint || c == cThousandSep) )
168
0
    {
169
0
        StrRoundDigit( sStrg, nPos - 1, bOverflow );
170
        // CHANGE from 9.3.1997: end the method immediately after recursive call!
171
0
        return;
172
0
    }
173
    // skip non-digits:
174
    // COMMENT:
175
    // in a valid format-string the number's output should be done
176
    // in one piece, i. e. special characters should ONLY be in
177
    // front OR behind the number and not right in the middle of
178
    // the format information for the number
179
0
    while( nPos >= 0 && ! rtl::isAsciiDigit(sStrg[nPos]))
180
0
    {
181
0
        nPos--;
182
0
    }
183
0
    if( nPos==-1 )
184
0
    {
185
0
        ShiftString( sStrg, 0 );
186
0
        sStrg[0] = '1';
187
0
        bOverflow = true;
188
0
    }
189
0
    else
190
0
    {
191
0
        sal_Unicode c2 = sStrg[nPos];
192
0
        if( rtl::isAsciiDigit(c2) )
193
0
        {
194
0
            if( c2 == '9' )
195
0
            {
196
0
                sStrg[nPos] = '0';
197
0
                StrRoundDigit( sStrg, nPos - 1, bOverflow );
198
0
            }
199
0
            else
200
0
            {
201
0
                sStrg[nPos] = c2 + 1;
202
0
            }
203
0
        }
204
0
        else
205
0
        {
206
0
            ShiftString( sStrg,nPos+1 );
207
0
            sStrg[nPos + 1] = '1';
208
0
            bOverflow = true;
209
0
        }
210
0
    }
211
0
}
212
213
void SbxBasicFormater::StrRoundDigit( OUStringBuffer& sStrg, short nPos )
214
0
{
215
0
    bool bOverflow;
216
217
0
    StrRoundDigit( sStrg, nPos, bOverflow );
218
0
}
219
220
void SbxBasicFormater::ParseBack( OUStringBuffer& sStrg, std::u16string_view sFormatStrg,
221
                                  short nFormatPos )
222
0
{
223
0
    for( sal_Int32 i = nFormatPos;
224
0
         i>0 && sFormatStrg[ i ]  == '#' && sStrg[sStrg.getLength() - 1] == '0';
225
0
         i-- )
226
0
    {
227
0
        sStrg.setLength(sStrg.getLength() - 1 );
228
0
    }
229
0
}
230
231
void SbxBasicFormater::InitScan( double _dNum )
232
0
{
233
0
    char sBuffer[ MAX_DOUBLE_BUFFER_LENGTH ];
234
235
0
    dNum = _dNum;
236
0
    InitExp( get_number_of_digits( dNum ) );
237
    // maximum of 15 positions behind the decimal point, example: -1.234000000000000E-001
238
0
    /*int nCount =*/ o3tl::sprintf( sBuffer,"%+22.15lE",dNum );
239
0
    sSciNumStrg = OUString::createFromAscii( sBuffer );
240
0
}
241
242
243
void SbxBasicFormater::InitExp( double _dNewExp )
244
0
{
245
0
    nNumExp = static_cast<short>(_dNewExp);
246
0
    sNumExpStrg = (nNumExp >= 0 ? std::u16string_view(u"+") : std::u16string_view(u""))
247
0
        + OUString::number(nNumExp);
248
0
    nExpExp = static_cast<short>(get_number_of_digits( static_cast<double>(nNumExp) ));
249
0
}
250
251
252
short SbxBasicFormater::GetDigitAtPosScan( short nPos, bool& bFoundFirstDigit )
253
0
{
254
    // trying to read a higher digit,
255
    // e. g. position 4 in 1.234,
256
    // or to read a digit outside of the
257
    // number's dissolution (double)
258
0
    if( nPos>nNumExp || abs(nNumExp-nPos)>MAX_NO_OF_DIGITS )
259
0
    {
260
0
        return NO_DIGIT_;
261
0
    }
262
    // determine the index of the position in the number-string:
263
    // skip the leading sign
264
0
    sal_uInt16 no = 1;
265
    // skip the decimal point if necessary
266
0
    if( nPos<nNumExp )
267
0
        no++;
268
0
    no += nNumExp-nPos;
269
    // query of the number's first valid digit --> set flag
270
0
    if( nPos==nNumExp )
271
0
        bFoundFirstDigit = true;
272
0
    return static_cast<short>(sSciNumStrg[ no ] - '0');
273
0
}
274
275
short SbxBasicFormater::GetDigitAtPosExpScan( short nPos, bool& bFoundFirstDigit )
276
0
{
277
0
    if( nPos>nExpExp )
278
0
        return -1;
279
280
0
    sal_uInt16 no = 1;
281
0
    no += nExpExp-nPos;
282
283
0
    if( nPos==nExpExp )
284
0
        bFoundFirstDigit = true;
285
0
    return static_cast<short>(sNumExpStrg[ no ] - '0');
286
0
}
287
288
// a value for the exponent can be given because the number maybe shall
289
// not be displayed in a normed way (e. g. 1.2345e-03) but maybe 123.345e-3 !
290
short SbxBasicFormater::GetDigitAtPosExpScan( double dNewExponent, short nPos,
291
                                              bool& bFoundFirstDigit )
292
0
{
293
0
    InitExp( dNewExponent );
294
295
0
    return GetDigitAtPosExpScan( nPos,bFoundFirstDigit );
296
0
}
297
298
// Copies the respective part of the format-string, if existing, and returns it.
299
// So a new string is created, which has to be freed by the caller later.
300
OUString SbxBasicFormater::GetPosFormatString( std::u16string_view sFormatStrg, bool & bFound )
301
0
{
302
0
    bFound = false;     // default...
303
0
    size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
304
305
0
    if( nPos != std::u16string_view::npos )
306
0
    {
307
0
        bFound = true;
308
        // the format-string for positive numbers is
309
        // everything before the first ';'
310
0
        return OUString(sFormatStrg.substr( 0,nPos ));
311
0
    }
312
313
0
    return OUString();
314
0
}
315
316
// see also GetPosFormatString()
317
OUString SbxBasicFormater::GetNegFormatString( std::u16string_view sFormatStrg, bool & bFound )
318
0
{
319
0
    bFound = false;     // default...
320
0
    size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
321
322
0
    if( nPos != std::u16string_view::npos)
323
0
    {
324
        // the format-string for negative numbers is
325
        // everything between the first and the second ';'
326
0
        std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
327
0
        nPos = sTempStrg.find( FORMAT_SEPARATOR );
328
0
        bFound = true;
329
0
        if( nPos == std::u16string_view::npos )
330
0
        {
331
0
            return OUString(sTempStrg);
332
0
        }
333
0
        else
334
0
        {
335
0
            return OUString(sTempStrg.substr( 0,nPos ));
336
0
        }
337
0
    }
338
0
    return OUString();
339
0
}
340
341
// see also GetPosFormatString()
342
OUString SbxBasicFormater::Get0FormatString( std::u16string_view sFormatStrg, bool & bFound )
343
0
{
344
0
    bFound = false;     // default...
345
0
    size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
346
347
0
    if( nPos != std::u16string_view::npos )
348
0
    {
349
        // the format string for the zero is
350
        // everything after the second ';'
351
0
        std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
352
0
        nPos = sTempStrg.find( FORMAT_SEPARATOR );
353
0
        if( nPos != std::u16string_view::npos )
354
0
        {
355
0
            bFound = true;
356
0
            sTempStrg = sTempStrg.substr( nPos+1 );
357
0
            nPos = sTempStrg.find( FORMAT_SEPARATOR );
358
0
            if( nPos == std::u16string_view::npos )
359
0
            {
360
0
                return OUString(sTempStrg);
361
0
            }
362
0
            else
363
0
            {
364
0
                return OUString(sTempStrg.substr( 0,nPos ));
365
0
            }
366
0
        }
367
0
    }
368
369
0
    return OUString();
370
0
}
371
372
// see also GetPosFormatString()
373
OUString SbxBasicFormater::GetNullFormatString( std::u16string_view sFormatStrg, bool & bFound )
374
0
{
375
0
    bFound = false;     // default...
376
0
    size_t nPos = sFormatStrg.find( FORMAT_SEPARATOR );
377
378
0
    if( nPos != std::u16string_view::npos )
379
0
    {
380
        // the format-string for the Null is
381
        // everything after the third ';'
382
0
        std::u16string_view sTempStrg = sFormatStrg.substr( nPos+1 );
383
0
        nPos = sTempStrg.find( FORMAT_SEPARATOR );
384
0
        if( nPos != std::u16string_view::npos )
385
0
        {
386
0
            sTempStrg = sTempStrg.substr( nPos+1 );
387
0
            nPos = sTempStrg.find( FORMAT_SEPARATOR );
388
0
            if( nPos != std::u16string_view::npos )
389
0
            {
390
0
                bFound = true;
391
0
                return OUString(sTempStrg.substr( nPos+1 ));
392
0
            }
393
0
        }
394
0
    }
395
396
0
    return OUString();
397
0
}
398
399
// returns value <> 0 in case of an error
400
void SbxBasicFormater::AnalyseFormatString( std::u16string_view sFormatStrg,
401
                short& nNoOfDigitsLeft, short& nNoOfDigitsRight,
402
                short& nNoOfOptionalDigitsLeft,
403
                short& nNoOfExponentDigits, short& nNoOfOptionalExponentDigits,
404
                bool& bPercent, bool& bCurrency, bool& bScientific,
405
                bool& bGenerateThousandSeparator,
406
                short& nMultipleThousandSeparators )
407
0
{
408
0
    sal_Int32 nLen;
409
0
    short nState = 0;
410
411
0
    nLen = sFormatStrg.size();
412
0
    nNoOfDigitsLeft = 0;
413
0
    nNoOfDigitsRight = 0;
414
0
    nNoOfOptionalDigitsLeft = 0;
415
0
    nNoOfExponentDigits = 0;
416
0
    nNoOfOptionalExponentDigits = 0;
417
0
    bPercent = false;
418
0
    bCurrency = false;
419
0
    bScientific = false;
420
    // from 11.7.97: as soon as a comma (point?) is found in the format string,
421
    // all three decimal powers are marked (i. e. thousand, million, ...)
422
0
    bGenerateThousandSeparator = sFormatStrg.find( ',' ) != std::u16string_view::npos;
423
0
    nMultipleThousandSeparators = 0;
424
425
0
    for( sal_Int32 i = 0; i < nLen; i++ )
426
0
    {
427
0
        sal_Unicode c = sFormatStrg[ i ];
428
0
        switch( c )
429
0
        {
430
0
        case '#':
431
0
        case '0':
432
0
            if( nState==0 )
433
0
            {
434
0
                nNoOfDigitsLeft++;
435
// TODO  here maybe better error inspection of the mantissa for valid syntax (see grammar)h
436
                // ATTENTION: 'undefined' behaviour if # and 0 are combined!
437
                // REMARK: #-placeholders are actually useless for
438
                // scientific display before the decimal point!
439
0
                if( c=='#' )
440
0
                {
441
0
                    nNoOfOptionalDigitsLeft++;
442
0
                }
443
0
            }
444
0
            else if( nState==1 )
445
0
            {
446
0
                nNoOfDigitsRight++;
447
0
            }
448
0
            else if( nState==-1 )   // search 0 in the exponent
449
0
            {
450
0
                if( c=='#' )    // # switches on the condition
451
0
                {
452
0
                    nNoOfOptionalExponentDigits++;
453
0
                    nState = -2;
454
0
                }
455
0
                nNoOfExponentDigits++;
456
0
            }
457
0
            else if( nState==-2 )   // search # in the exponent
458
0
            {
459
0
                if( c=='0' )
460
0
                {
461
                    // ERROR: 0 after # in the exponent is NOT allowed!!
462
0
                    return;
463
0
                }
464
0
                nNoOfOptionalExponentDigits++;
465
0
                nNoOfExponentDigits++;
466
0
            }
467
0
            break;
468
0
        case '.':
469
0
            nState++;
470
0
            if( nState>1 )
471
0
            {
472
0
                return;  // ERROR: too many decimal points
473
0
            }
474
0
            break;
475
0
        case '%':
476
0
            bPercent = true;
477
0
            break;
478
0
        case '(':
479
0
            bCurrency = true;
480
0
            break;
481
0
        case ',':
482
0
            {
483
0
                sal_Unicode ch = sFormatStrg[ i+1 ];
484
485
0
                if( ch!=0 && (ch==',' || ch=='.') )
486
0
                {
487
0
                    nMultipleThousandSeparators++;
488
0
                }
489
0
            }
490
0
            break;
491
0
        case 'e':
492
0
        case 'E':
493
            // #i13821 not when no digits before
494
0
            if( nNoOfDigitsLeft > 0 || nNoOfDigitsRight > 0 )
495
0
            {
496
0
                nState = -1;   // abort counting digits
497
0
                bScientific = true;
498
0
            }
499
0
            break;
500
            // OWN command-character which turns on
501
            // the creation of thousand-separators
502
0
        case '\\':
503
            // Ignore next char
504
0
            i++;
505
0
            break;
506
0
        case CREATE_1000SEP_CHAR:
507
0
            bGenerateThousandSeparator = true;
508
0
            break;
509
0
        }
510
0
    }
511
0
}
512
513
// the flag bCreateSign says that at the mantissa a leading sign
514
// shall be created
515
void SbxBasicFormater::ScanFormatString( double dNumber,
516
                                         std::u16string_view sFormatStrg, OUString& sReturnStrgFinal,
517
                                         bool bCreateSign )
518
0
{
519
0
    short   /*nErr,*/nNoOfDigitsLeft,nNoOfDigitsRight,nNoOfOptionalDigitsLeft,
520
0
        nNoOfExponentDigits,nNoOfOptionalExponentDigits,
521
0
        nMultipleThousandSeparators;
522
0
    bool    bPercent,bCurrency,bScientific,bGenerateThousandSeparator;
523
524
0
    OUStringBuffer sReturnStrg(32);
525
526
    // analyse the format-string, i. e. determine the following values:
527
    /*
528
            - number of digits before decimal point
529
            - number of digits after decimal point
530
            - optional digits before decimal point
531
            - number of digits in the exponent
532
            - optional digits in the exponent
533
            - percent-character found?
534
            - () for negative leading sign?
535
            - exponential-notation?
536
            - shall thousand-separators be generated?
537
            - is a percent-character being found? --> dNumber *= 100.0;
538
            - are there thousand-separators in a row?
539
                ,, or ,. --> dNumber /= 1000.0;
540
            - other errors? multiple decimal points, E's, etc.
541
        --> errors are simply ignored at the moment
542
    */
543
0
    AnalyseFormatString( sFormatStrg, nNoOfDigitsLeft, nNoOfDigitsRight,
544
0
                         nNoOfOptionalDigitsLeft, nNoOfExponentDigits,
545
0
                         nNoOfOptionalExponentDigits,
546
0
                         bPercent, bCurrency, bScientific,
547
0
                         bGenerateThousandSeparator, nMultipleThousandSeparators );
548
    // special handling for special characters
549
0
    if( bPercent )
550
0
    {
551
0
        dNumber *= 100.0;
552
0
    }
553
// TODO: this condition (,, or ,.) is NOT Visual-Basic compatible!
554
        // Question: shall this stay here (requirements)?
555
0
    if( nMultipleThousandSeparators )
556
0
    {
557
0
        dNumber /= 1000.0;
558
0
    }
559
0
    double dExponent;
560
0
    short i,nLen;
561
0
    short nState,nDigitPos,nExponentPos,nMaxDigit,nMaxExponentDigit;
562
0
    bool bFirstDigit,bFirstExponentDigit,bFoundFirstDigit,
563
0
        bIsNegative,bZeroSpaceOn, bSignHappend,bDigitPosNegative;
564
565
0
    bSignHappend = false;
566
0
    bFoundFirstDigit = false;
567
0
    bIsNegative = dNumber < 0.0;
568
0
    nLen = sFormatStrg.size();
569
0
    dExponent = get_number_of_digits( dNumber );
570
0
    nExponentPos = 0;
571
0
    nMaxExponentDigit = 0;
572
0
    nMaxDigit = static_cast<short>(dExponent);
573
0
    bDigitPosNegative = false;
574
0
    if( bScientific )
575
0
    {
576
0
        dExponent = dExponent - static_cast<double>(nNoOfDigitsLeft-1);
577
0
        nDigitPos = nMaxDigit;
578
0
        nMaxExponentDigit = static_cast<short>(get_number_of_digits( dExponent ));
579
0
        nExponentPos = nNoOfExponentDigits - 1 - nNoOfOptionalExponentDigits;
580
0
    }
581
0
    else
582
0
    {
583
0
        nDigitPos = nNoOfDigitsLeft - 1; // counting starts at 0, 10^0
584
        // no exponent-data is needed here!
585
0
        bDigitPosNegative = (nDigitPos < 0);
586
0
    }
587
0
    bFirstDigit = true;
588
0
    bFirstExponentDigit = true;
589
0
    nState = 0; // 0 --> mantissa; 1 --> exponent
590
0
    bZeroSpaceOn = false;
591
592
593
0
    InitScan( dNumber );
594
    // scanning the format-string:
595
0
    sal_Unicode cForce = 0;
596
0
    for( i = 0; i < nLen; i++ )
597
0
    {
598
0
        sal_Unicode c;
599
0
        if( cForce )
600
0
        {
601
0
            c = cForce;
602
0
            cForce = 0;
603
0
        }
604
0
        else
605
0
        {
606
0
            c = sFormatStrg[ i ];
607
0
        }
608
0
        switch( c )
609
0
        {
610
0
        case '0':
611
0
        case '#':
612
0
            if( nState==0 )
613
0
            {
614
                // handling of the mantissa
615
0
                if( bFirstDigit )
616
0
                {
617
                    // remark: at bCurrency the negative
618
                    //         leading sign shall be shown with ()
619
0
                    if( bIsNegative && !bCreateSign && !bSignHappend )
620
0
                    {
621
0
                        bSignHappend = true;
622
0
                        sReturnStrg.append('-');
623
0
                    }
624
                    // output redundant positions, i. e. those which
625
                    // are undocumented by the format-string
626
0
                    if( nMaxDigit > nDigitPos )
627
0
                    {
628
0
                        for( short j = nMaxDigit; j > nDigitPos; j-- )
629
0
                        {
630
0
                            short nTempDigit = GetDigitAtPosScan( j, bFoundFirstDigit );
631
0
                            AppendDigit( sReturnStrg, nTempDigit );
632
0
                            if( nTempDigit != NO_DIGIT_ )
633
0
                            {
634
0
                                bFirstDigit = false;
635
0
                            }
636
0
                            if( bGenerateThousandSeparator && c=='0' && j > 0 && (j % 3 == 0) )
637
0
                            {
638
0
                                sReturnStrg.append(cThousandSep );
639
0
                            }
640
0
                        }
641
0
                    }
642
0
                }
643
644
0
                if( nMaxDigit<nDigitPos && ( c=='0' || bZeroSpaceOn ) )
645
0
                {
646
0
                    AppendDigit( sReturnStrg, 0 );
647
0
                    bFirstDigit = false;
648
0
                    bZeroSpaceOn = true;
649
                    // Remark: in Visual-Basic the first 0 turns on the 0 for
650
                    //         all the following # (up to the decimal point),
651
                    //         this behaviour is simulated here with the flag.
652
0
                    if (bGenerateThousandSeparator && c == '0' && nDigitPos > 0 && (nDigitPos % 3 == 0))
653
0
                    {
654
0
                        sReturnStrg.append(cThousandSep);
655
0
                    }
656
0
                }
657
0
                else
658
0
                {
659
0
                    short nTempDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit ) ;
660
0
                    AppendDigit( sReturnStrg, nTempDigit );
661
662
0
                    if( nTempDigit != NO_DIGIT_ )
663
0
                    {
664
0
                        bFirstDigit = false;
665
0
                    }
666
0
                    if( bGenerateThousandSeparator && ( c=='0' || nMaxDigit>=nDigitPos ) && nDigitPos>0 && (nDigitPos % 3 == 0) )
667
0
                    {
668
0
                        sReturnStrg.append(cThousandSep);
669
0
                    }
670
0
                }
671
0
                nDigitPos--;
672
0
            }
673
0
            else
674
0
            {
675
                // handling the exponent
676
0
                if( bFirstExponentDigit )
677
0
                {
678
                    // leading sign has been given out at e/E already
679
0
                    bFirstExponentDigit = false;
680
0
                    if( nMaxExponentDigit > nExponentPos )
681
                        // output redundant positions, i. e. those which
682
                        // are undocumented by the format-string
683
0
                    {
684
0
                        for( short j = nMaxExponentDigit; j > nExponentPos; j-- )
685
0
                        {
686
0
                            AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, j, bFoundFirstDigit ) );
687
0
                        }
688
0
                    }
689
0
                }
690
691
0
                if( nMaxExponentDigit < nExponentPos && c=='0' )
692
0
                {
693
0
                    AppendDigit( sReturnStrg, 0 );
694
0
                }
695
0
                else
696
0
                {
697
0
                    AppendDigit( sReturnStrg, GetDigitAtPosExpScan( dExponent, nExponentPos, bFoundFirstDigit ) );
698
0
                }
699
0
                nExponentPos--;
700
0
            }
701
0
            break;
702
0
        case '.':
703
0
            if( bDigitPosNegative ) // #i13821: If no digits before .
704
0
            {
705
0
                bDigitPosNegative = false;
706
0
                nDigitPos = 0;
707
0
                cForce = '#';
708
0
                i-=2;
709
0
                break;
710
0
            }
711
0
            sReturnStrg.append(cDecPoint);
712
0
            break;
713
0
        case '%':
714
            // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
715
0
            ParseBack( sReturnStrg, sFormatStrg, i-1 );
716
0
            sReturnStrg.append('%');
717
0
            break;
718
0
        case 'e':
719
0
        case 'E':
720
            // does mantissa have to be rounded, before the exponent is displayed?
721
0
            {
722
                // is there a mantissa at all?
723
0
                if( bFirstDigit )
724
0
                {
725
                    // apparently not, i. e. invalid format string, e. g. E000.00
726
                    // so ignore these e and E characters
727
                    // maybe output an error (like in Visual Basic)?
728
729
                    // #i13821: VB 6 behaviour
730
0
                    sReturnStrg.append(c);
731
0
                    break;
732
0
                }
733
734
0
                bool bOverflow = false;
735
0
                short nNextDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit );
736
0
                if( nNextDigit>=5 )
737
0
                {
738
0
                    StrRoundDigit( sReturnStrg, sReturnStrg.getLength() - 1, bOverflow );
739
0
                }
740
0
                if( bOverflow )
741
0
                {
742
                    // a leading 9 has been rounded
743
0
                    LeftShiftDecimalPoint( sReturnStrg );
744
0
                    sReturnStrg[sReturnStrg.getLength() - 1] = 0;
745
0
                    dExponent += 1.0;
746
0
                }
747
                // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
748
0
                ParseBack( sReturnStrg, sFormatStrg, i-1 );
749
0
            }
750
            // change the scanner's condition
751
0
            nState++;
752
            // output exponent character
753
0
            sReturnStrg.append(c);
754
            // i++; // MANIPULATION of the loop-variable!
755
0
            c = sFormatStrg[ ++i ];
756
            // output leading sign / exponent
757
0
            if( c != 0 )
758
0
            {
759
0
                if( c == '-' )
760
0
                {
761
0
                    if( dExponent < 0.0 )
762
0
                    {
763
0
                        sReturnStrg.append('-');
764
0
                    }
765
0
                }
766
0
                else if( c == '+' )
767
0
                {
768
0
                    if( dExponent < 0.0 )
769
0
                    {
770
0
                        sReturnStrg.append('-');
771
0
                    }
772
0
                    else
773
0
                    {
774
0
                        sReturnStrg.append('+');
775
0
                    }
776
0
                }
777
0
            }
778
0
            break;
779
0
        case ',':
780
0
            break;
781
0
        case ';':
782
0
            break;
783
0
        case '(':
784
0
        case ')':
785
            // maybe remove redundant 0s, e. g. 4.500e4 in 0.0##e-00
786
0
            ParseBack( sReturnStrg, sFormatStrg, i-1 );
787
0
            if( bIsNegative )
788
0
            {
789
0
                sReturnStrg.append(c);
790
0
            }
791
0
            break;
792
0
        case '$':
793
            // append the string for the currency:
794
0
            sReturnStrg.append(sCurrencyStrg);
795
0
            break;
796
0
        case ' ':
797
0
        case '-':
798
0
        case '+':
799
0
            ParseBack( sReturnStrg, sFormatStrg, i-1 );
800
0
            sReturnStrg.append(c);
801
0
            break;
802
0
        case '\\':
803
0
            ParseBack( sReturnStrg, sFormatStrg, i-1 );
804
            // special character found, output next
805
            // character directly (if existing)
806
0
            c = sFormatStrg[ ++i ];
807
0
            if( c!=0 )
808
0
            {
809
0
                sReturnStrg.append(c);
810
0
            }
811
0
            break;
812
0
        case CREATE_1000SEP_CHAR:
813
            // ignore here, action has already been
814
            // executed in AnalyseFormatString
815
0
            break;
816
0
        default:
817
            // output characters and digits, too (like in Visual-Basic)
818
0
            if( ( c>='a' && c<='z' ) ||
819
0
                ( c>='A' && c<='Z' ) ||
820
0
                ( c>='1' && c<='9' ) )
821
0
            {
822
0
                sReturnStrg.append(c);
823
0
            }
824
0
        }
825
0
    }
826
827
    // scan completed - rounding necessary?
828
0
    if( !bScientific )
829
0
    {
830
0
        short nNextDigit = GetDigitAtPosScan( nDigitPos, bFoundFirstDigit );
831
0
        if( nNextDigit>=5 )
832
0
        {
833
0
            StrRoundDigit( sReturnStrg, sReturnStrg.getLength() - 1 );
834
0
        }
835
0
    }
836
837
0
    if( nNoOfDigitsRight>0 )
838
0
    {
839
0
        ParseBack( sReturnStrg, sFormatStrg, sFormatStrg.size()-1 );
840
0
    }
841
0
    sReturnStrgFinal = sReturnStrg.makeStringAndClear();
842
0
}
843
844
OUString SbxBasicFormater::BasicFormatNull( std::u16string_view sFormatStrg )
845
0
{
846
0
    bool bNullFormatFound;
847
0
    OUString sNullFormatStrg = GetNullFormatString( sFormatStrg, bNullFormatFound );
848
849
0
    if( bNullFormatFound )
850
0
    {
851
0
        return sNullFormatStrg;
852
0
    }
853
0
    return u"null"_ustr;
854
0
}
855
856
OUString SbxBasicFormater::BasicFormat( double dNumber, const OUString& _sFormatStrg )
857
0
{
858
0
    bool bPosFormatFound,bNegFormatFound,b0FormatFound;
859
0
    OUString sFormatStrg = _sFormatStrg;
860
861
    // analyse format-string concerning predefined formats:
862
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_GENERALNUMBER ) )
863
0
    {
864
0
        sFormatStrg = GENERALNUMBER_FORMAT;
865
0
    }
866
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_CURRENCY ) )
867
0
    {
868
0
        sFormatStrg = sCurrencyFormatStrg;
869
0
    }
870
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_FIXED ) )
871
0
    {
872
0
        sFormatStrg = FIXED_FORMAT;
873
0
    }
874
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_STANDARD ) )
875
0
    {
876
0
        sFormatStrg = STANDARD_FORMAT;
877
0
    }
878
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_PERCENT ) )
879
0
    {
880
0
        sFormatStrg = PERCENT_FORMAT;
881
0
    }
882
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_SCIENTIFIC ) )
883
0
    {
884
0
        sFormatStrg = SCIENTIFIC_FORMAT;
885
0
    }
886
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_YESNO ) )
887
0
    {
888
0
        return ( dNumber==0.0 ) ? sNoStrg : sYesStrg ;
889
0
    }
890
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_TRUEFALSE ) )
891
0
    {
892
0
        return ( dNumber==0.0 ) ? sFalseStrg : sTrueStrg ;
893
0
    }
894
0
    if( sFormatStrg.equalsIgnoreAsciiCase( BASICFORMAT_ONOFF ) )
895
0
    {
896
0
        return ( dNumber==0.0 ) ? sOffStrg : sOnStrg ;
897
0
    }
898
899
    // analyse format-string concerning ';', i. e. format-strings for
900
    // positive-, negative- and 0-values
901
0
    OUString sPosFormatStrg = GetPosFormatString( sFormatStrg, bPosFormatFound );
902
0
    OUString sNegFormatStrg = GetNegFormatString( sFormatStrg, bNegFormatFound );
903
0
    OUString s0FormatStrg = Get0FormatString( sFormatStrg, b0FormatFound );
904
905
0
    OUString sReturnStrg;
906
0
    OUString sTempStrg;
907
908
0
    if( dNumber==0.0 )
909
0
    {
910
0
        sTempStrg = sFormatStrg;
911
0
        if( b0FormatFound )
912
0
        {
913
0
            if( s0FormatStrg.isEmpty() && bPosFormatFound )
914
0
            {
915
0
                sTempStrg = sPosFormatStrg;
916
0
            }
917
0
            else
918
0
            {
919
0
                sTempStrg = s0FormatStrg;
920
0
            }
921
0
        }
922
0
        else if( bPosFormatFound )
923
0
        {
924
0
            sTempStrg = sPosFormatStrg;
925
0
        }
926
0
        ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/false );
927
0
    }
928
0
    else
929
0
    {
930
0
        if( dNumber<0.0 )
931
0
        {
932
0
            if( bNegFormatFound )
933
0
            {
934
0
                if( sNegFormatStrg.isEmpty() && bPosFormatFound )
935
0
                {
936
0
                    sTempStrg = "-" + sPosFormatStrg;
937
0
                }
938
0
                else
939
0
                {
940
0
                    sTempStrg = sNegFormatStrg;
941
0
                }
942
0
            }
943
0
            else
944
0
            {
945
0
                sTempStrg = sFormatStrg;
946
0
            }
947
            // if NO format-string especially for negative
948
            // values is given, output the leading sign
949
0
            ScanFormatString( dNumber, sTempStrg, sReturnStrg,/*bCreateSign=*/bNegFormatFound/*sNegFormatStrg!=EMPTYFORMATSTRING*/ );
950
0
        }
951
0
        else // if( dNumber>0.0 )
952
0
        {
953
0
            ScanFormatString( dNumber,
954
0
                    (/*sPosFormatStrg!=EMPTYFORMATSTRING*/bPosFormatFound ? sPosFormatStrg : sFormatStrg),
955
0
                    sReturnStrg,/*bCreateSign=*/false );
956
0
        }
957
0
    }
958
0
    return sReturnStrg;
959
0
}
960
961
bool SbxBasicFormater::isBasicFormat( std::u16string_view sFormatStrg )
962
0
{
963
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_GENERALNUMBER ) )
964
0
    {
965
0
        return true;
966
0
    }
967
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_CURRENCY ) )
968
0
    {
969
0
        return true;
970
0
    }
971
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_FIXED ) )
972
0
    {
973
0
        return true;
974
0
    }
975
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_STANDARD ) )
976
0
    {
977
0
        return true;
978
0
    }
979
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_PERCENT ) )
980
0
    {
981
0
        return true;
982
0
    }
983
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_SCIENTIFIC ) )
984
0
    {
985
0
        return true;
986
0
    }
987
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_YESNO ) )
988
0
    {
989
0
        return true;
990
0
    }
991
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_TRUEFALSE ) )
992
0
    {
993
0
        return true;
994
0
    }
995
0
    if( o3tl::equalsIgnoreAsciiCase( sFormatStrg, BASICFORMAT_ONOFF ) )
996
0
    {
997
0
        return true;
998
0
    }
999
0
    return false;
1000
0
}
1001
1002
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */