Coverage Report

Created: 2025-12-31 10:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libreoffice/xmloff/source/style/xmlnumfi.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
#include <svl/zforlist.hxx>
21
#include <svl/numformat.hxx>
22
#include <svl/zformat.hxx>
23
#include <svl/numuno.hxx>
24
#include <i18nlangtag/languagetag.hxx>
25
#include <tools/color.hxx>
26
#include <osl/diagnose.h>
27
#include <rtl/math.hxx>
28
#include <rtl/ustrbuf.hxx>
29
#include <sal/log.hxx>
30
31
#include <sax/tools/converter.hxx>
32
33
#include <utility>
34
#include <xmloff/xmlement.hxx>
35
#include <xmloff/xmlnumfi.hxx>
36
#include <xmloff/xmlnamespace.hxx>
37
#include <xmloff/xmlictxt.hxx>
38
#include <xmloff/xmlimp.hxx>
39
#include <xmloff/xmluconv.hxx>
40
#include <xmloff/families.hxx>
41
#include <xmloff/xmltoken.hxx>
42
#include <xmloff/languagetagodf.hxx>
43
44
#include <memory>
45
#include <string_view>
46
#include <vector>
47
48
using namespace ::com::sun::star;
49
using namespace ::xmloff::token;
50
51
namespace {
52
53
struct SvXMLNumFmtEntry
54
{
55
    OUString   aName;
56
    sal_uInt32  nKey;
57
    bool        bRemoveAfterUse;
58
59
    SvXMLNumFmtEntry( OUString aN, sal_uInt32 nK, bool bR ) :
60
17.0k
        aName(std::move(aN)), nKey(nK), bRemoveAfterUse(bR) {}
61
};
62
63
}
64
65
class SvXMLNumImpData
66
{
67
    SvNumberFormatter*  pFormatter;
68
    const LocaleDataWrapper*  pLocaleData { nullptr };
69
    std::vector<SvXMLNumFmtEntry> m_NameEntries;
70
71
public:
72
    SvXMLNumImpData(SvNumberFormatter* pFmt);
73
74
69.3k
    SvNumberFormatter*      GetNumberFormatter() const  { return pFormatter; }
75
    const LocaleDataWrapper&    GetLocaleData( LanguageType nLang );
76
    sal_uInt32              GetKeyForName( std::u16string_view rName );
77
    void                    AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse );
78
    void                    SetUsed( sal_uInt32 nKey );
79
    void                    RemoveVolatileFormats();
80
};
81
82
struct SvXMLNumberInfo
83
{
84
    sal_Int32   nDecimals           = -1;
85
    sal_Int32   nInteger            = -1;       /// Total min number of digits in integer part ('0' + '?')
86
    sal_Int32   nBlankInteger       = -1;       /// Number of '?' in integer part
87
    sal_Int32   nExpDigits          = -1;       /// Number of '0' and '?' in exponent
88
    sal_Int32   nBlankExp           = -1;       /// Number of '?' in exponent
89
    sal_Int32   nExpInterval        = -1;
90
    sal_Int32   nMinNumerDigits     = -1;
91
    sal_Int32   nMinDenomDigits     = -1;
92
    sal_Int32   nMaxNumerDigits     = -1;
93
    sal_Int32   nMaxDenomDigits     = -1;
94
    sal_Int32   nFracDenominator    = -1;
95
    sal_Int32   nMinDecimalDigits   = -1;
96
    sal_Int32   nZerosNumerDigits   = -1;
97
    sal_Int32   nZerosDenomDigits   = -1;
98
    bool        bGrouping           = false;
99
    bool        bDecReplace         = false;
100
    bool        bExpSign            = true;
101
    bool        bExponentLowercase  = false;     /// Exponent is 'e' instead of 'E'
102
    bool        bDecAlign           = false;
103
    double      fDisplayFactor      = 1.0;
104
    OUString    aIntegerFractionDelimiter;
105
    std::map<sal_Int32, OUString> m_EmbeddedElements;
106
};
107
108
namespace {
109
110
enum class SvXMLStyleTokens;
111
112
class SvXMLNumFmtElementContext : public SvXMLImportContext
113
{
114
    SvXMLNumFormatContext&  rParent;
115
    SvXMLStyleTokens        nType;
116
    OUStringBuffer          aContent;
117
    SvXMLNumberInfo         aNumInfo;
118
    LanguageType            nElementLang;
119
    bool                    bLong;
120
    bool                    bTextual;
121
    OUString                sCalendar;
122
    OUString                sBlankWidthString;
123
124
public:
125
                SvXMLNumFmtElementContext( SvXMLImport& rImport, sal_Int32 nElement,
126
                                    SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType,
127
                                    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
128
129
    virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
130
        sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
131
    virtual void SAL_CALL characters( const OUString& rChars ) override;
132
    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
133
134
    void    AddEmbeddedElement( sal_Int32 nFormatPos, std::u16string_view rContent, std::u16string_view rBlankWidthString );
135
};
136
137
class SvXMLNumFmtEmbeddedTextContext : public SvXMLImportContext
138
{
139
    SvXMLNumFmtElementContext&  rParent;
140
    OUStringBuffer         aContent;
141
    sal_Int32                   nTextPosition;
142
    OUString                    aBlankWidthString;
143
144
public:
145
                SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport, sal_Int32 nElement,
146
                                    SvXMLNumFmtElementContext& rParentContext,
147
                                    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
148
149
    virtual void SAL_CALL characters( const OUString& rChars ) override;
150
    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
151
};
152
153
class SvXMLNumFmtMapContext : public SvXMLImportContext
154
{
155
    SvXMLNumFormatContext&  rParent;
156
    OUString           sCondition;
157
    OUString           sName;
158
159
public:
160
                SvXMLNumFmtMapContext( SvXMLImport& rImport, sal_Int32 nElement,
161
                                    SvXMLNumFormatContext& rParentContext,
162
                                    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
163
164
    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
165
};
166
167
class SvXMLNumFmtPropContext : public SvXMLImportContext
168
{
169
    SvXMLNumFormatContext&  rParent;
170
    Color                   m_nColor;
171
    bool                    bColSet;
172
173
public:
174
                SvXMLNumFmtPropContext( SvXMLImport& rImport, sal_Int32 nElement,
175
                                    SvXMLNumFormatContext& rParentContext,
176
                                    const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
177
178
    virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
179
};
180
181
enum class SvXMLStyleTokens
182
{
183
    Text,
184
    FillCharacter,
185
    Number,
186
    ScientificNumber,
187
    Fraction,
188
    CurrencySymbol,
189
    Day,
190
    Month,
191
    Year,
192
    Era,
193
    DayOfWeek,
194
    WeekOfYear,
195
    Quarter,
196
    Hours,
197
    AmPm,
198
    Minutes,
199
    Seconds,
200
    Boolean,
201
    TextContent
202
};
203
204
}
205
206
//  standard colors
207
208
209
9.52k
#define XML_NUMF_COLORCOUNT     10
210
211
const Color aNumFmtStdColors[XML_NUMF_COLORCOUNT] =
212
{
213
    COL_BLACK,
214
    COL_LIGHTBLUE,
215
    COL_LIGHTGREEN,
216
    COL_LIGHTCYAN,
217
    COL_LIGHTRED,
218
    COL_LIGHTMAGENTA,
219
    COL_BROWN,
220
    COL_GRAY,
221
    COL_YELLOW,
222
    COL_WHITE
223
};
224
225
226
//  token maps
227
228
229
// maps for SvXMLUnitConverter::convertEnum
230
231
const SvXMLEnumMapEntry<bool> aStyleValueMap[] =
232
{
233
    { XML_SHORT,            false },
234
    { XML_LONG,             true },
235
    { XML_TOKEN_INVALID,    false }
236
};
237
238
const SvXMLEnumMapEntry<bool> aFormatSourceMap[] =
239
{
240
    { XML_FIXED,            false },
241
    { XML_LANGUAGE,         true },
242
    { XML_TOKEN_INVALID,    false }
243
};
244
245
namespace {
246
247
struct SvXMLDefaultDateFormat
248
{
249
    NfIndexTableOffset          eFormat;
250
    SvXMLDateElementAttributes  eDOW;
251
    SvXMLDateElementAttributes  eDay;
252
    SvXMLDateElementAttributes  eMonth;
253
    SvXMLDateElementAttributes  eYear;
254
    SvXMLDateElementAttributes  eHours;
255
    SvXMLDateElementAttributes  eMins;
256
    SvXMLDateElementAttributes  eSecs;
257
    bool                        bSystem;
258
};
259
260
}
261
262
const SvXMLDefaultDateFormat aDefaultDateFormats[] =
263
{
264
    // format                           day-of-week     day             month               year            hours           minutes         seconds         format-source
265
266
    { NF_DATE_SYSTEM_SHORT,             XML_DEA_NONE,   XML_DEA_ANY,    XML_DEA_ANY,        XML_DEA_ANY,    XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   true },
267
    { NF_DATE_SYSTEM_LONG,              XML_DEA_ANY,    XML_DEA_ANY,    XML_DEA_ANY,        XML_DEA_ANY,    XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   true },
268
    { NF_DATE_SYS_MMYY,                 XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_LONG,       XML_DEA_SHORT,  XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
269
    { NF_DATE_SYS_DDMMM,                XML_DEA_NONE,   XML_DEA_LONG,   XML_DEA_TEXTSHORT,  XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
270
    { NF_DATE_SYS_DDMMYYYY,             XML_DEA_NONE,   XML_DEA_LONG,   XML_DEA_LONG,       XML_DEA_LONG,   XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
271
    { NF_DATE_SYS_DDMMYY,               XML_DEA_NONE,   XML_DEA_LONG,   XML_DEA_LONG,       XML_DEA_SHORT,  XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
272
    { NF_DATE_SYS_DMMMYY,               XML_DEA_NONE,   XML_DEA_SHORT,  XML_DEA_TEXTSHORT,  XML_DEA_SHORT,  XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
273
    { NF_DATE_SYS_DMMMYYYY,             XML_DEA_NONE,   XML_DEA_SHORT,  XML_DEA_TEXTSHORT,  XML_DEA_LONG,   XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
274
    { NF_DATE_SYS_DMMMMYYYY,            XML_DEA_NONE,   XML_DEA_SHORT,  XML_DEA_TEXTLONG,   XML_DEA_LONG,   XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
275
    { NF_DATE_SYS_NNDMMMYY,             XML_DEA_SHORT,  XML_DEA_SHORT,  XML_DEA_TEXTSHORT,  XML_DEA_SHORT,  XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
276
    { NF_DATE_SYS_NNDMMMMYYYY,          XML_DEA_SHORT,  XML_DEA_SHORT,  XML_DEA_TEXTLONG,   XML_DEA_LONG,   XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
277
    { NF_DATE_SYS_NNNNDMMMMYYYY,        XML_DEA_LONG,   XML_DEA_SHORT,  XML_DEA_TEXTLONG,   XML_DEA_LONG,   XML_DEA_NONE,   XML_DEA_NONE,   XML_DEA_NONE,   false },
278
    { NF_DATETIME_SYS_DDMMYYYY_HHMM,    XML_DEA_NONE,   XML_DEA_ANY,    XML_DEA_ANY,        XML_DEA_LONG,   XML_DEA_ANY,    XML_DEA_ANY,    XML_DEA_NONE,   false },
279
    { NF_DATETIME_SYSTEM_SHORT_HHMM,    XML_DEA_NONE,   XML_DEA_ANY,    XML_DEA_ANY,        XML_DEA_ANY,    XML_DEA_ANY,    XML_DEA_ANY,    XML_DEA_NONE,   true },
280
    { NF_DATETIME_SYS_DDMMYYYY_HHMMSS,  XML_DEA_NONE,   XML_DEA_ANY,    XML_DEA_ANY,        XML_DEA_ANY,    XML_DEA_ANY,    XML_DEA_ANY,    XML_DEA_ANY,    false }
281
};
282
283
284
//  SvXMLNumImpData
285
286
287
SvXMLNumImpData::SvXMLNumImpData(
288
    SvNumberFormatter* pFmt )
289
18.4k
:   pFormatter(pFmt)
290
18.4k
{
291
18.4k
}
292
293
sal_uInt32 SvXMLNumImpData::GetKeyForName( std::u16string_view rName )
294
5.54k
{
295
5.54k
    for (const auto& rObj : m_NameEntries)
296
142k
    {
297
142k
        if (rObj.aName == rName)
298
5.54k
            return rObj.nKey;              // found
299
142k
    }
300
0
    return NUMBERFORMAT_ENTRY_NOT_FOUND;
301
5.54k
}
302
303
void SvXMLNumImpData::AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse )
304
17.0k
{
305
17.0k
    if ( bRemoveAfterUse )
306
6.14k
    {
307
        //  if there is already an entry for this key without the bRemoveAfterUse flag,
308
        //  clear the flag for this entry, too
309
310
6.14k
        for (const auto& rObj : m_NameEntries)
311
189k
        {
312
189k
            if (rObj.nKey == nKey && !rObj.bRemoveAfterUse)
313
23
            {
314
23
                bRemoveAfterUse = false;        // clear flag for new entry
315
23
                break;
316
23
            }
317
189k
        }
318
6.14k
    }
319
10.9k
    else
320
10.9k
    {
321
        //  call SetUsed to clear the bRemoveAfterUse flag for other entries for this key
322
10.9k
        SetUsed( nKey );
323
10.9k
    }
324
325
17.0k
    m_NameEntries.emplace_back(rName, nKey, bRemoveAfterUse);
326
17.0k
}
327
328
void SvXMLNumImpData::SetUsed( sal_uInt32 nKey )
329
10.9k
{
330
10.9k
    for (auto& rObj : m_NameEntries)
331
206k
    {
332
206k
        if (rObj.nKey == nKey)
333
1.14k
        {
334
1.14k
            rObj.bRemoveAfterUse = false;      // used -> don't remove
335
336
            //  continue searching - there may be several entries for the same key
337
            //  (with different names), the format must not be deleted if any one of
338
            //  them is used
339
1.14k
        }
340
206k
    }
341
10.9k
}
342
343
void SvXMLNumImpData::RemoveVolatileFormats()
344
18.4k
{
345
    //  remove temporary (volatile) formats from NumberFormatter
346
    //  called at the end of each import (styles and content), so volatile formats
347
    //  from styles can't be used in content
348
349
18.4k
    if ( !pFormatter )
350
0
        return;
351
352
18.4k
    for (const auto& rObj : m_NameEntries)
353
17.0k
    {
354
17.0k
        if (rObj.bRemoveAfterUse )
355
6.10k
        {
356
6.10k
            const SvNumberformat* pFormat = pFormatter->GetEntry(rObj.nKey);
357
6.10k
            if (pFormat && (pFormat->GetType() & SvNumFormatType::DEFINED))
358
4.66k
                pFormatter->DeleteEntry(rObj.nKey);
359
6.10k
        }
360
17.0k
    }
361
18.4k
}
362
363
const LocaleDataWrapper& SvXMLNumImpData::GetLocaleData( LanguageType nLang )
364
9.40k
{
365
9.40k
    if ( !pLocaleData || pLocaleData->getLanguageTag() != LanguageTag(nLang) )
366
2.49k
        pLocaleData = LocaleDataWrapper::get( LanguageTag( nLang ) );
367
9.40k
    return *pLocaleData;
368
9.40k
}
369
370
371
//  SvXMLNumFmtMapContext
372
373
374
SvXMLNumFmtMapContext::SvXMLNumFmtMapContext( SvXMLImport& rImport,
375
                                    sal_Int32 /*nElement*/,
376
                                    SvXMLNumFormatContext& rParentContext,
377
                                    const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
378
7.58k
    SvXMLImportContext( rImport ),
379
7.58k
    rParent( rParentContext )
380
7.58k
{
381
7.58k
    for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
382
14.6k
    {
383
14.6k
        OUString sValue = aIter.toString();
384
14.6k
        switch(aIter.getToken())
385
14.6k
        {
386
7.15k
            case XML_ELEMENT(STYLE, XML_CONDITION):
387
7.15k
                sCondition = sValue;
388
7.15k
                break;
389
7.41k
            case XML_ELEMENT(STYLE, XML_APPLY_STYLE_NAME):
390
7.41k
                sName = sValue;
391
7.41k
                break;
392
40
            default:
393
40
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
394
14.6k
        }
395
14.6k
    }
396
7.58k
}
397
398
void SvXMLNumFmtMapContext::endFastElement(sal_Int32 )
399
7.58k
{
400
7.58k
    rParent.AddCondition( sCondition, sName );
401
7.58k
}
402
403
404
//  SvXMLNumFmtPropContext
405
406
407
SvXMLNumFmtPropContext::SvXMLNumFmtPropContext( SvXMLImport& rImport,
408
                                    sal_Int32 /*nElement*/,
409
                                    SvXMLNumFormatContext& rParentContext,
410
                                    const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
411
2.43k
    SvXMLImportContext( rImport ),
412
2.43k
    rParent( rParentContext ),
413
2.43k
    m_nColor( 0 ),
414
2.43k
    bColSet( false )
415
2.43k
{
416
2.43k
    for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
417
2.04k
    {
418
2.04k
        switch ( aIter.getToken())
419
2.04k
        {
420
0
            case XML_ELEMENT(FO, XML_COLOR):
421
1.91k
            case XML_ELEMENT(FO_COMPAT, XML_COLOR):
422
1.91k
                bColSet = ::sax::Converter::convertColor( m_nColor, aIter.toView() );
423
1.91k
                break;
424
133
            default:
425
133
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
426
2.04k
        }
427
2.04k
    }
428
2.43k
}
429
430
void SvXMLNumFmtPropContext::endFastElement(sal_Int32 )
431
2.43k
{
432
2.43k
    if (bColSet)
433
1.85k
        rParent.AddColor( m_nColor );
434
2.43k
}
435
436
437
//  SvXMLNumFmtEmbeddedTextContext
438
439
440
SvXMLNumFmtEmbeddedTextContext::SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport,
441
                                    sal_Int32 /*nElement*/,
442
                                    SvXMLNumFmtElementContext& rParentContext,
443
                                    const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
444
0
    SvXMLImportContext( rImport ),
445
0
    rParent( rParentContext ),
446
0
    nTextPosition( 0 )
447
0
{
448
0
    sal_Int32 nAttrVal;
449
450
0
    for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
451
0
    {
452
0
        if ( aIter.getToken() == XML_ELEMENT(NUMBER, XML_POSITION) )
453
0
        {
454
0
            if (::sax::Converter::convertNumber( nAttrVal, aIter.toView() ))
455
0
                nTextPosition = nAttrVal;
456
0
        }
457
0
        else if ( aIter.getToken() == XML_ELEMENT(LO_EXT, XML_BLANK_WIDTH_CHAR)
458
0
                || aIter.getToken() == XML_ELEMENT(NUMBER, XML_BLANK_WIDTH_CHAR) )
459
0
        {
460
0
            aBlankWidthString = aIter.toString();
461
0
        }
462
0
        else
463
0
            XMLOFF_WARN_UNKNOWN("xmloff", aIter);
464
0
    }
465
0
}
466
467
void SvXMLNumFmtEmbeddedTextContext::characters( const OUString& rChars )
468
0
{
469
0
    aContent.append( rChars );
470
0
}
471
472
void SvXMLNumFmtEmbeddedTextContext::endFastElement(sal_Int32 )
473
0
{
474
0
    rParent.AddEmbeddedElement( nTextPosition, aContent.makeStringAndClear(), aBlankWidthString );
475
0
}
476
477
static bool lcl_ValidChar( sal_Unicode cChar, const SvXMLNumFormatContext& rParent )
478
17.5k
{
479
17.5k
    SvXMLStylesTokens nFormatType = rParent.GetType();
480
481
    // Treat space equal to non-breaking space separator.
482
17.5k
    const sal_Unicode cNBSP = 0x00A0;
483
17.5k
    sal_Unicode cTS;
484
17.5k
    if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
485
12.3k
           nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
486
9.08k
           nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
487
8.52k
            (cChar == (cTS = rParent.GetLocaleData().getNumThousandSep()[0]) ||
488
8.52k
             (cChar == ' ' && cTS == cNBSP)) )
489
2
    {
490
        //  #i22394# Extra occurrences of thousands separator must be quoted, so they
491
        //  aren't mis-interpreted as display-factor.
492
        //  This must be limited to the format types that can contain a number element,
493
        //  because the same character can be a date separator that should not be quoted
494
        //  in date formats.
495
496
2
        return false;   // force quotes
497
2
    }
498
499
    //  see ImpSvNumberformatScan::Next_Symbol
500
501
    // All format types except BOOLEAN may contain minus sign or delimiter.
502
17.5k
    if ( cChar == '-' )
503
6.33k
        return nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE;
504
505
11.2k
    if ( ( cChar == ' ' ||
506
6.28k
           cChar == '/' ||
507
6.26k
           cChar == '.' ||
508
5.77k
           cChar == ',' ||
509
5.73k
           cChar == ':' ||
510
982
           cChar == '\''   ) &&
511
10.2k
         ( nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
512
8.79k
           nFormatType == SvXMLStylesTokens::DATE_STYLE ||
513
7.61k
           nFormatType == SvXMLStylesTokens::TIME_STYLE ) ) // other formats do not require delimiter tdf#97837
514
8.03k
        return true;
515
516
    //  percent sign must be used without quotes for percentage styles only
517
3.22k
    if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && cChar == '%' )
518
18
        return true;
519
520
    //  don't put quotes around single parentheses (often used for negative numbers)
521
3.21k
    if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
522
937
           nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
523
896
           nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
524
2.31k
         ( cChar == '(' || cChar == ')' ) )
525
917
        return true;
526
527
2.29k
    return false;
528
3.21k
}
529
530
static void lcl_EnquoteIfNecessary( OUStringBuffer& rContent, const SvXMLNumFormatContext& rParent )
531
24.8k
{
532
24.8k
    bool bQuote = true;
533
24.8k
    sal_Int32 nLength = rContent.getLength();
534
24.8k
    const SvXMLStylesTokens nFormatType = rParent.GetType();
535
536
24.8k
    if (nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE &&
537
24.8k
            ((nLength == 1 && lcl_ValidChar( rContent[0], rParent)) ||
538
9.59k
             (nLength == 2 &&
539
455
              ((rContent[0] == ' ' && rContent[1] == '-') ||
540
99
               (rContent[1] == ' ' && lcl_ValidChar( rContent[0], rParent))))))
541
15.6k
    {
542
        //  Don't quote single separator characters like space or percent,
543
        //  or separator characters followed by space (used in date formats).
544
        //  Or space followed by minus (used in currency formats) that would
545
        //  lead to almost duplicated formats with built-in formats just with
546
        //  the difference of quotes.
547
15.6k
        bQuote = false;
548
15.6k
    }
549
9.20k
    else if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && nLength > 1 )
550
0
    {
551
        //  the percent character in percentage styles must be left out of quoting
552
        //  (one occurrence is enough even if there are several percent characters in the string)
553
554
0
        sal_Int32 nPos = rContent.indexOf( '%' );
555
0
        if ( nPos >= 0 )
556
0
        {
557
0
            if ( nPos + 1 < nLength )
558
0
            {
559
0
                if ( nPos + 2 == nLength && lcl_ValidChar( rContent[nPos + 1], rParent ) )
560
0
                {
561
                    //  single character that doesn't need quoting
562
0
                }
563
0
                else
564
0
                {
565
                    //  quote text behind percent character
566
0
                    rContent.insert( nPos + 1, '"' );
567
0
                    rContent.append( '"' );
568
0
                }
569
0
            }
570
0
            if ( nPos > 0 )
571
0
            {
572
0
                if ( nPos == 1 && lcl_ValidChar( rContent[0], rParent ) )
573
0
                {
574
                    //  single character that doesn't need quoting
575
0
                }
576
0
                else
577
0
                {
578
                    //  quote text before percent character
579
0
                    rContent.insert( nPos, '"' );
580
0
                    rContent.insert( 0, '"' );
581
0
                }
582
0
            }
583
0
            bQuote = false;
584
0
        }
585
        // else: normal quoting (below)
586
0
    }
587
588
24.8k
    if ( !bQuote )
589
15.6k
        return;
590
591
    // #i55469# quotes in the string itself have to be escaped
592
9.20k
    bool bEscape = ( rContent.indexOf( '"' ) >= 0 );
593
9.20k
    if ( bEscape )
594
1
    {
595
        // A quote is turned into "\"" - a quote to end quoted text, an escaped quote,
596
        // and a quote to resume quoting.
597
1
        OUString aInsert(  u"\"\\\""_ustr  );
598
599
1
        sal_Int32 nPos = 0;
600
646
        while ( nPos < rContent.getLength() )
601
645
        {
602
645
            if ( rContent[nPos] == '"' )
603
2
            {
604
2
                rContent.insert( nPos, aInsert );
605
2
                nPos += aInsert.getLength();
606
2
            }
607
645
            ++nPos;
608
645
        }
609
1
    }
610
611
    //  quote string literals
612
9.20k
    rContent.insert( 0, '"' );
613
9.20k
    rContent.append( '"' );
614
615
    // remove redundant double quotes at start or end
616
9.20k
    if ( !bEscape )
617
9.20k
        return;
618
619
1
    if ( rContent.getLength() > 2 &&
620
1
         rContent[0] == '"' &&
621
1
         rContent[1] == '"' )
622
0
    {
623
0
        rContent.remove(0, 2);
624
0
    }
625
626
1
    sal_Int32 nLen = rContent.getLength();
627
1
    if ( nLen > 2 &&
628
1
         rContent[nLen - 1] == '"' &&
629
1
         rContent[nLen - 2] == '"' )
630
0
    {
631
0
        rContent.truncate(nLen - 2);
632
0
    }
633
1
}
634
635
636
//  SvXMLNumFmtElementContext
637
638
639
SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( SvXMLImport& rImport,
640
                                    sal_Int32 /*nElement*/,
641
                                    SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType,
642
                                    const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
643
58.7k
    SvXMLImportContext( rImport ),
644
58.7k
    rParent( rParentContext ),
645
58.7k
    nType( nNewType ),
646
58.7k
    nElementLang( LANGUAGE_SYSTEM ),
647
58.7k
    bLong( false ),
648
58.7k
    bTextual( false )
649
58.7k
{
650
58.7k
    LanguageTagODF aLanguageTagODF;
651
58.7k
    sal_Int32 nAttrVal;
652
58.7k
    bool bAttrBool(false);
653
58.7k
    bool bVarDecimals = false;
654
58.7k
    bool bIsMaxDenominator = false;
655
58.7k
    double fAttrDouble;
656
657
58.7k
    for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
658
52.1k
    {
659
52.1k
        switch (aIter.getToken())
660
52.1k
        {
661
11.4k
            case XML_ELEMENT(NUMBER, XML_DECIMAL_PLACES):
662
11.4k
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
663
11.3k
                {
664
                    // fdo#58539 & gnome#627420: limit number of digits during import
665
11.3k
                    aNumInfo.nDecimals = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
666
11.3k
                }
667
11.4k
                break;
668
0
            case XML_ELEMENT(LO_EXT, XML_MIN_DECIMAL_PLACES):
669
0
            case XML_ELEMENT(NUMBER, XML_MIN_DECIMAL_PLACES):
670
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
671
0
                    aNumInfo.nMinDecimalDigits = nAttrVal;
672
0
                break;
673
12.8k
            case XML_ELEMENT(NUMBER, XML_MIN_INTEGER_DIGITS):
674
12.8k
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
675
12.7k
                    aNumInfo.nInteger = nAttrVal;
676
12.8k
                break;
677
0
            case XML_ELEMENT(LO_EXT, XML_MAX_BLANK_INTEGER_DIGITS):
678
0
            case XML_ELEMENT(NUMBER, XML_MAX_BLANK_INTEGER_DIGITS):
679
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
680
0
                    aNumInfo.nBlankInteger = nAttrVal;
681
0
                break;
682
9.96k
            case XML_ELEMENT(NUMBER, XML_GROUPING):
683
9.96k
                if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
684
9.81k
                    aNumInfo.bGrouping = bAttrBool;
685
9.96k
                break;
686
0
            case XML_ELEMENT(NUMBER, XML_DISPLAY_FACTOR):
687
0
                if (::sax::Converter::convertDouble( fAttrDouble, aIter.toView() ))
688
0
                    aNumInfo.fDisplayFactor = fAttrDouble;
689
0
                break;
690
0
            case XML_ELEMENT(NUMBER, XML_DECIMAL_REPLACEMENT):
691
0
                if ( aIter.toView() == " " )
692
0
                {
693
0
                    aNumInfo.bDecAlign = true; // space replacement for "?"
694
0
                    bVarDecimals = true;
695
0
                }
696
0
                else
697
0
                    if ( aIter.isEmpty() )
698
0
                        bVarDecimals = true;   // empty replacement string: variable decimals
699
0
                    else                                // all other strings
700
0
                        aNumInfo.bDecReplace = true;    // decimal replacement with dashes
701
0
                break;
702
438
            case XML_ELEMENT(NUMBER, XML_MIN_EXPONENT_DIGITS):
703
438
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
704
438
                    aNumInfo.nExpDigits = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
705
438
                break;
706
0
            case XML_ELEMENT(NUMBER, XML_BLANK_EXPONENT_DIGITS):
707
0
            case XML_ELEMENT(LO_EXT, XML_BLANK_EXPONENT_DIGITS):
708
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
709
0
                    aNumInfo.nBlankExp = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
710
0
                break;
711
0
            case XML_ELEMENT(NUMBER, XML_EXPONENT_INTERVAL):
712
0
            case XML_ELEMENT(LO_EXT, XML_EXPONENT_INTERVAL):
713
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
714
0
                    aNumInfo.nExpInterval = nAttrVal;
715
0
                break;
716
0
            case XML_ELEMENT(NUMBER, XML_FORCED_EXPONENT_SIGN):
717
0
            case XML_ELEMENT(LO_EXT, XML_FORCED_EXPONENT_SIGN):
718
0
                if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
719
0
                    aNumInfo.bExpSign = bAttrBool;
720
0
                break;
721
0
            case XML_ELEMENT(NUMBER, XML_EXPONENT_LOWERCASE):
722
0
            case XML_ELEMENT(LO_EXT, XML_EXPONENT_LOWERCASE):
723
0
                if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
724
0
                    aNumInfo.bExponentLowercase = bAttrBool;
725
0
                break;
726
0
            case XML_ELEMENT(NUMBER, XML_MIN_NUMERATOR_DIGITS):
727
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
728
0
                    aNumInfo.nMinNumerDigits = nAttrVal;
729
0
                break;
730
0
            case XML_ELEMENT(NUMBER, XML_MIN_DENOMINATOR_DIGITS):
731
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
732
0
                    aNumInfo.nMinDenomDigits = nAttrVal;
733
0
                break;
734
0
            case XML_ELEMENT(LO_EXT, XML_MAX_NUMERATOR_DIGITS):
735
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 ))  // at least one '#'
736
0
                    aNumInfo.nMaxNumerDigits = nAttrVal;
737
0
                break;
738
0
            case XML_ELEMENT(NUMBER, XML_DENOMINATOR_VALUE):
739
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // 0 is not valid
740
0
                {
741
0
                    aNumInfo.nFracDenominator = nAttrVal;
742
0
                    bIsMaxDenominator = false;
743
0
                }
744
0
                break;
745
0
            case XML_ELEMENT(NUMBER, XML_MAX_DENOMINATOR_VALUE):  // part of ODF 1.3
746
0
            case XML_ELEMENT(LO_EXT, XML_MAX_DENOMINATOR_VALUE):
747
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 ) && aNumInfo.nFracDenominator <= 0)
748
0
                {   // if denominator value not yet defined
749
0
                    aNumInfo.nFracDenominator = nAttrVal;
750
0
                    bIsMaxDenominator = true;
751
0
                }
752
0
                break;
753
0
            case XML_ELEMENT(LO_EXT, XML_ZEROS_NUMERATOR_DIGITS):
754
0
            case XML_ELEMENT(NUMBER, XML_ZEROS_NUMERATOR_DIGITS):
755
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
756
0
                    aNumInfo.nZerosNumerDigits = nAttrVal;
757
0
                break;
758
0
            case XML_ELEMENT(NUMBER, XML_ZEROS_DENOMINATOR_DIGITS):
759
0
            case XML_ELEMENT(LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS):
760
0
                if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
761
0
                    aNumInfo.nZerosDenomDigits = nAttrVal;
762
0
                 break;
763
0
            case XML_ELEMENT(NUMBER, XML_INTEGER_FRACTION_DELIMITER):
764
0
            case XML_ELEMENT(LO_EXT, XML_INTEGER_FRACTION_DELIMITER):
765
0
                aNumInfo.aIntegerFractionDelimiter = aIter.toString();
766
0
                break;
767
0
            case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG):
768
0
                aLanguageTagODF.maRfcLanguageTag = aIter.toString();
769
0
                break;
770
3.78k
            case XML_ELEMENT(NUMBER, XML_LANGUAGE):
771
3.78k
                aLanguageTagODF.maLanguage = aIter.toString();
772
3.78k
                break;
773
0
            case XML_ELEMENT(NUMBER, XML_SCRIPT):
774
0
                aLanguageTagODF.maScript = aIter.toString();
775
0
                break;
776
3.61k
            case XML_ELEMENT(NUMBER, XML_COUNTRY):
777
3.61k
                aLanguageTagODF.maCountry = aIter.toString();
778
3.61k
                break;
779
9.22k
            case XML_ELEMENT(NUMBER, XML_STYLE):
780
9.22k
                SvXMLUnitConverter::convertEnum( bLong, aIter.toView(), aStyleValueMap );
781
9.22k
                break;
782
784
            case XML_ELEMENT(NUMBER, XML_TEXTUAL):
783
784
                if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
784
782
                    bTextual = bAttrBool;
785
784
                break;
786
0
            case XML_ELEMENT(NUMBER, XML_CALENDAR):
787
0
                sCalendar = aIter.toString();
788
0
                break;
789
0
            case XML_ELEMENT(NUMBER, XML_BLANK_WIDTH_CHAR):
790
0
            case XML_ELEMENT(LO_EXT, XML_BLANK_WIDTH_CHAR):
791
0
                sBlankWidthString = aIter.toString();
792
0
                break;
793
111
            default:
794
111
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
795
52.1k
        }
796
52.1k
    }
797
58.7k
    if ( aNumInfo.nBlankInteger > aNumInfo.nInteger )
798
0
        aNumInfo.nInteger = aNumInfo.nBlankInteger;
799
58.7k
    if ( aNumInfo.nMinDecimalDigits == -1)
800
58.7k
    {
801
58.7k
        if ( bVarDecimals || aNumInfo.bDecReplace )
802
0
            aNumInfo.nMinDecimalDigits = 0;
803
58.7k
        else
804
58.7k
            aNumInfo.nMinDecimalDigits = aNumInfo.nDecimals;
805
58.7k
    }
806
58.7k
    if ( aNumInfo.nExpDigits > 0 && aNumInfo.nBlankExp >= aNumInfo.nExpDigits )
807
0
        aNumInfo.nBlankExp = aNumInfo.nExpDigits - 1; // at least one '0' in exponent
808
809
58.7k
    if ( aNumInfo.nZerosDenomDigits > 0 )
810
0
    {   // nMin = count of '0' and '?'
811
0
        if ( aNumInfo.nMinDenomDigits < aNumInfo.nZerosDenomDigits )
812
0
            aNumInfo.nMinDenomDigits = aNumInfo.nZerosDenomDigits;
813
0
    }
814
58.7k
    else
815
58.7k
        aNumInfo.nZerosDenomDigits = 0;
816
58.7k
    if ( aNumInfo.nMinDenomDigits >= 0 )
817
0
        if ( aNumInfo.nMaxDenomDigits < aNumInfo.nMinDenomDigits )
818
0
            aNumInfo.nMaxDenomDigits = ( aNumInfo.nMinDenomDigits ? aNumInfo.nMinDenomDigits : 1 );
819
58.7k
    if ( aNumInfo.nZerosNumerDigits > 0 )
820
0
    {
821
0
        if ( aNumInfo.nMinNumerDigits < aNumInfo.nZerosNumerDigits )
822
0
            aNumInfo.nMinNumerDigits = aNumInfo.nZerosNumerDigits;
823
0
    }
824
58.7k
    else
825
58.7k
        aNumInfo.nZerosNumerDigits = 0;
826
58.7k
    if ( aNumInfo.nMinNumerDigits >= 0 )
827
0
        if ( aNumInfo.nMaxNumerDigits < aNumInfo.nMinNumerDigits )
828
0
            aNumInfo.nMaxNumerDigits = ( aNumInfo.nMinNumerDigits ? aNumInfo.nMinNumerDigits : 1 );
829
58.7k
    if ( bIsMaxDenominator && aNumInfo.nFracDenominator > 0 )
830
0
    {
831
0
        aNumInfo.nMaxDenomDigits = floor( log10( aNumInfo.nFracDenominator ) ) + 1;
832
0
        aNumInfo.nFracDenominator = -1;  // Max denominator value only gives number of digits at denominator
833
0
    }
834
58.7k
    if ( aNumInfo.nMaxDenomDigits > 0 )
835
0
    {
836
0
        if ( aNumInfo.nMinDenomDigits < 0 )
837
0
            aNumInfo.nMinDenomDigits = 0;
838
0
        else if ( aNumInfo.nMinDenomDigits > aNumInfo.nMaxDenomDigits )
839
0
            aNumInfo.nMinDenomDigits = aNumInfo.nMaxDenomDigits;
840
0
    }
841
842
58.7k
    if ( !aLanguageTagODF.isEmpty() )
843
3.86k
    {
844
3.86k
        nElementLang = aLanguageTagODF.getLanguageTag().getLanguageType( false);
845
3.86k
        if ( nElementLang == LANGUAGE_DONTKNOW )
846
95
            nElementLang = LANGUAGE_SYSTEM;         //! error handling for unknown locales?
847
3.86k
    }
848
849
58.7k
    if ( aNumInfo.aIntegerFractionDelimiter.isEmpty() )
850
58.7k
        aNumInfo.aIntegerFractionDelimiter = " ";
851
58.7k
}
852
853
css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFmtElementContext::createFastChildContext(
854
    sal_Int32 nElement,
855
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
856
219
{
857
    //  only number:number and number:scientific-number supports number:embedded-text child element
858
859
219
    if ( ( nType == SvXMLStyleTokens::Number || nType == SvXMLStyleTokens::ScientificNumber ) &&
860
119
         nElement == XML_ELEMENT(NUMBER, XML_EMBEDDED_TEXT) )
861
0
    {
862
0
        return new SvXMLNumFmtEmbeddedTextContext( GetImport(), nElement, *this, xAttrList );
863
0
    }
864
219
    else
865
219
        XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
866
219
    return nullptr;
867
219
}
868
869
void SvXMLNumFmtElementContext::characters( const OUString& rChars )
870
29.0k
{
871
29.0k
    aContent.append( rChars );
872
29.0k
}
873
874
namespace {
875
void lcl_InsertBlankWidthChars( std::u16string_view rBlankWidthString, OUStringBuffer& rContent )
876
0
{
877
0
    sal_Int32 nShiftPosition = 1; // rContent starts with a quote
878
0
    const size_t nLenBlank = rBlankWidthString.size();
879
0
    for ( size_t i = 0 ; i < nLenBlank ; i++ )
880
0
    {
881
0
        sal_Unicode nChar = rBlankWidthString[ i ];
882
0
        OUString aBlanks;
883
0
        SvNumberformat::InsertBlanks( aBlanks, 0, nChar );
884
0
        sal_Int32 nPositionContent = 0;
885
0
        if ( ++i < nLenBlank )
886
0
        {
887
0
            sal_Int32 nNext = rBlankWidthString.find( '_', i );
888
0
            if ( static_cast<sal_Int32>( i ) < nNext )
889
0
            {
890
0
                nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i, nNext - i ) );
891
0
                i = nNext;
892
0
            }
893
0
            else
894
0
                nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i ) );
895
0
        }
896
0
        nPositionContent += nShiftPosition;
897
0
        if ( nPositionContent >= 0 )
898
0
        {
899
0
            rContent.remove( nPositionContent, aBlanks.getLength() );
900
0
            if ( nPositionContent >= 1 && rContent[ nPositionContent-1 ] == '\"' )
901
0
            {
902
0
                nPositionContent--;
903
0
                rContent.insert( nPositionContent, nChar );
904
0
                rContent.insert( nPositionContent, '_' );
905
0
            }
906
0
            else
907
0
            {
908
0
                rContent.insert( nPositionContent, '\"' );
909
0
                rContent.insert( nPositionContent, nChar );
910
0
                rContent.insert( nPositionContent, "\"_" );
911
0
                nShiftPosition += 2;
912
0
            }
913
            // rContent length was modified: remove blanks, add "_x"
914
0
            nShiftPosition += 2 - aBlanks.getLength();
915
0
        }
916
0
    }
917
    // remove empty string at the end of rContent
918
0
    if ( std::u16string_view( rContent ).substr( rContent.getLength() - 2 ) == u"\"\"" )
919
0
    {
920
0
        sal_Int32 nLen = rContent.getLength();
921
0
        if ( nLen >= 3 && rContent[ nLen-3 ] != '\\' )
922
0
            rContent.truncate( nLen - 2 );
923
0
    }
924
0
}
925
}
926
927
void SvXMLNumFmtElementContext::AddEmbeddedElement( sal_Int32 nFormatPos, std::u16string_view rContentEmbedded, std::u16string_view rBlankWidthString )
928
0
{
929
0
    if ( rContentEmbedded.empty() )
930
0
        return;
931
0
    OUStringBuffer aContentEmbedded( rContentEmbedded );
932
    //  #107805# always quote embedded strings - even space would otherwise
933
    //  be recognized as thousands separator in French.
934
0
    aContentEmbedded.insert( 0, '"' );
935
0
    aContentEmbedded.append( '"' );
936
0
    if ( !rBlankWidthString.empty() )
937
0
        lcl_InsertBlankWidthChars( rBlankWidthString, aContentEmbedded );
938
939
0
    auto iterPair = aNumInfo.m_EmbeddedElements.emplace( nFormatPos, aContentEmbedded.toString() );
940
0
    if (!iterPair.second)
941
0
    {
942
        // there's already an element at this position - append text to existing element
943
0
        if ( iterPair.first->second.endsWith( "\"" ) && aContentEmbedded[ 0 ] == '"' )
944
0
        {   // remove double quote
945
0
            iterPair.first->second = OUString::Concat( iterPair.first->second.subView( 0, iterPair.first->second.getLength() - 1 ) )
946
0
                                + aContentEmbedded.subView( 1, aContentEmbedded.getLength() - 1 );
947
0
        }
948
0
        else
949
0
            iterPair.first->second += aContentEmbedded;
950
0
    }
951
0
}
952
953
void SvXMLNumFmtElementContext::endFastElement(sal_Int32 )
954
58.6k
{
955
58.6k
    bool bEffLong = bLong;
956
58.6k
    switch (nType)
957
58.6k
    {
958
24.8k
        case SvXMLStyleTokens::Text:
959
24.8k
            if ( rParent.HasLongDoW() &&
960
12
                 std::u16string_view(aContent) == rParent.GetLocaleData().getLongDateDayOfWeekSep() )
961
12
            {
962
                //  skip separator constant after long day of week
963
                //  (NF_KEY_NNNN contains the separator)
964
965
12
                if ( rParent.ReplaceNfKeyword( NF_KEY_NNN, NF_KEY_NNNN ) )
966
12
                {
967
12
                    aContent.truncate();
968
12
                }
969
970
12
                rParent.SetHasLongDoW( false );     // only once
971
12
            }
972
24.8k
            if ( !aContent.isEmpty() )
973
24.8k
            {
974
24.8k
                lcl_EnquoteIfNecessary( aContent, rParent );
975
24.8k
                if ( !sBlankWidthString.isEmpty() )
976
0
                {
977
0
                    lcl_InsertBlankWidthChars( sBlankWidthString, aContent );
978
0
                    sBlankWidthString = "";
979
0
                }
980
24.8k
                rParent.AddToCode( aContent );
981
24.8k
                aContent.setLength(0);
982
24.8k
            }
983
13
            else
984
13
            {
985
                // Quoted empty text may be significant to separate.
986
13
                aContent.append("\"\"");
987
13
                rParent.AddToCode( aContent );
988
13
                aContent.setLength(0);
989
13
                rParent.SetHasTrailingEmptyText(true);  // *after* AddToCode()
990
13
            }
991
24.8k
            break;
992
993
13.4k
        case SvXMLStyleTokens::Number:
994
13.4k
            rParent.AddNumber( aNumInfo );
995
13.4k
            break;
996
997
3.93k
        case SvXMLStyleTokens::CurrencySymbol:
998
3.93k
            rParent.AddCurrency( aContent.makeStringAndClear(), nElementLang );
999
3.93k
            break;
1000
1001
1.95k
        case SvXMLStyleTokens::TextContent:
1002
1.95k
            rParent.AddToCode( '@');
1003
1.95k
            break;
1004
0
        case SvXMLStyleTokens::FillCharacter:
1005
0
            if ( !aContent.isEmpty() )
1006
0
            {
1007
0
                rParent.AddToCode( '*' );
1008
0
                rParent.AddToCode( aContent[0] );
1009
0
            }
1010
0
            break;
1011
125
        case SvXMLStyleTokens::Boolean:
1012
125
            rParent.AddNfKeyword( NF_KEY_BOOLEAN );
1013
125
            break;
1014
1015
1.52k
        case SvXMLStyleTokens::Day:
1016
1.52k
            rParent.UpdateCalendar( sCalendar );
1017
//! I18N doesn't provide SYSTEM or extended date information yet
1018
1019
1.52k
            rParent.AddNfKeyword(
1020
1.52k
                sal::static_int_cast< sal_uInt16 >(
1021
1.52k
                    bEffLong ? NF_KEY_DD : NF_KEY_D ) );
1022
1.52k
            break;
1023
1.77k
        case SvXMLStyleTokens::Month:
1024
1.77k
            rParent.UpdateCalendar( sCalendar );
1025
//! I18N doesn't provide SYSTEM or extended date information yet
1026
1027
1.77k
            rParent.AddNfKeyword(
1028
1.77k
                sal::static_int_cast< sal_uInt16 >(
1029
1.77k
                    bTextual
1030
1.77k
                    ? ( bEffLong ? NF_KEY_MMMM : NF_KEY_MMM )
1031
1.77k
                    : ( bEffLong ? NF_KEY_MM : NF_KEY_M ) ) );
1032
1.77k
            break;
1033
1.46k
        case SvXMLStyleTokens::Year:
1034
//! I18N doesn't provide SYSTEM or extended date information yet
1035
1.46k
            {
1036
                // Y after G (era) is replaced by E for a secondary calendar.
1037
                // Do not replace for default calendar.
1038
                // Also replace Y by E if we're switching to the secondary
1039
                // calendar of a locale if it is known to implicitly use E.
1040
1.46k
                rParent.UpdateCalendar( sCalendar);
1041
1.46k
                const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState();
1042
1.46k
                if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
1043
1.46k
                        || eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER)
1044
0
                {
1045
0
                    rParent.AddNfKeyword(
1046
0
                            sal::static_int_cast< sal_uInt16 >(
1047
0
                                bEffLong ? NF_KEY_EEC : NF_KEY_EC ) );
1048
0
                }
1049
1.46k
                else
1050
1.46k
                {
1051
1.46k
                    rParent.AddNfKeyword(
1052
1.46k
                            sal::static_int_cast< sal_uInt16 >(
1053
1.46k
                                bEffLong ? NF_KEY_YYYY : NF_KEY_YY ) );
1054
1.46k
                }
1055
1.46k
            }
1056
1.46k
            break;
1057
0
        case SvXMLStyleTokens::Era:
1058
0
            rParent.UpdateCalendar( sCalendar );
1059
//! I18N doesn't provide SYSTEM or extended date information yet
1060
0
            rParent.AddNfKeyword(
1061
0
                sal::static_int_cast< sal_uInt16 >(
1062
0
                    bEffLong ? NF_KEY_GGG : NF_KEY_G ) );
1063
            //  HasEra flag is set
1064
0
            break;
1065
91
        case SvXMLStyleTokens::DayOfWeek:
1066
//! I18N doesn't provide SYSTEM or extended date information yet
1067
91
            {
1068
                // Implicit secondary calendar uses A keyword, default and
1069
                // explicit calendar N keyword.
1070
91
                rParent.UpdateCalendar( sCalendar);
1071
91
                const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState();
1072
91
                if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
1073
91
                        || eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER)
1074
0
                {
1075
0
                    rParent.AddNfKeyword(
1076
0
                            sal::static_int_cast< sal_uInt16 >(
1077
0
                                bEffLong ? NF_KEY_AAAA : NF_KEY_AAA ) );
1078
0
                }
1079
91
                else
1080
91
                {
1081
91
                    rParent.AddNfKeyword(
1082
91
                            sal::static_int_cast< sal_uInt16 >(
1083
91
                                bEffLong ? NF_KEY_NNNN : NF_KEY_NN ) );
1084
91
                }
1085
91
            }
1086
91
            break;
1087
0
        case SvXMLStyleTokens::WeekOfYear:
1088
0
            rParent.UpdateCalendar( sCalendar );
1089
0
            rParent.AddNfKeyword( NF_KEY_WW );
1090
0
            break;
1091
0
        case SvXMLStyleTokens::Quarter:
1092
0
            rParent.UpdateCalendar( sCalendar );
1093
0
            rParent.AddNfKeyword(
1094
0
                sal::static_int_cast< sal_uInt16 >(
1095
0
                    bEffLong ? NF_KEY_QQ : NF_KEY_Q ) );
1096
0
            break;
1097
2.63k
        case SvXMLStyleTokens::Hours:
1098
2.63k
            rParent.AddNfKeyword(
1099
2.63k
                sal::static_int_cast< sal_uInt16 >(
1100
2.63k
                    bEffLong ? NF_KEY_HH : NF_KEY_H ) );
1101
2.63k
            break;
1102
841
        case SvXMLStyleTokens::AmPm:
1103
            //! short/long?
1104
841
            rParent.AddNfKeyword( NF_KEY_AMPM );
1105
841
            break;
1106
3.61k
        case SvXMLStyleTokens::Minutes:
1107
3.61k
            rParent.AddNfKeyword(
1108
3.61k
                sal::static_int_cast< sal_uInt16 >(
1109
3.61k
                    bEffLong ? NF_KEY_MMI : NF_KEY_MI ) );
1110
3.61k
            break;
1111
1.92k
        case SvXMLStyleTokens::Seconds:
1112
1.92k
            rParent.AddNfKeyword(
1113
1.92k
                sal::static_int_cast< sal_uInt16 >(
1114
1.92k
                    bEffLong ? NF_KEY_SS : NF_KEY_S ) );
1115
1.92k
            if ( aNumInfo.nDecimals > 0 )
1116
283
            {
1117
                //  manually add the decimal places
1118
283
                rParent.AddToCode(rParent.GetLocaleData().getNumDecimalSep());
1119
622
                for (sal_Int32 i=0; i<aNumInfo.nDecimals; i++)
1120
339
                {
1121
339
                    rParent.AddToCode( '0');
1122
339
                }
1123
283
            }
1124
1.92k
            break;
1125
1126
0
        case SvXMLStyleTokens::Fraction:
1127
0
            {
1128
0
                if ( aNumInfo.nInteger >= 0 )
1129
0
                {
1130
                    // add integer part only if min-integer-digits attribute is there
1131
0
                    aNumInfo.nDecimals = 0;
1132
0
                    rParent.AddNumber( aNumInfo );      // number without decimals
1133
0
                    OUStringBuffer sIntegerFractionDelimiter(aNumInfo.aIntegerFractionDelimiter);
1134
0
                    lcl_EnquoteIfNecessary( sIntegerFractionDelimiter, rParent );
1135
0
                    rParent.AddToCode( sIntegerFractionDelimiter ); // default is ' '
1136
0
                }
1137
1138
                //! build string and add at once
1139
1140
0
                sal_Int32 i;
1141
0
                for (i=aNumInfo.nMaxNumerDigits; i > 0; i--)
1142
0
                {
1143
0
                    if ( i > aNumInfo.nMinNumerDigits )
1144
0
                        rParent.AddToCode( '#' );
1145
0
                    else if ( i > aNumInfo.nZerosNumerDigits )
1146
0
                        rParent.AddToCode( '?' );
1147
0
                    else
1148
0
                        rParent.AddToCode( '0' );
1149
0
                }
1150
0
                rParent.AddToCode( '/' );
1151
0
                if ( aNumInfo.nFracDenominator > 0 )
1152
0
                {
1153
0
                    rParent.AddToCode(  OUString::number( aNumInfo.nFracDenominator ) );
1154
0
                }
1155
0
                else
1156
0
                {
1157
0
                    for (i=aNumInfo.nMaxDenomDigits; i > 0 ; i--)
1158
0
                    {
1159
0
                        if ( i > aNumInfo.nMinDenomDigits )
1160
0
                            rParent.AddToCode( '#' );
1161
0
                        else if ( i > aNumInfo.nZerosDenomDigits )
1162
0
                            rParent.AddToCode( '?' );
1163
0
                        else
1164
0
                            rParent.AddToCode( '0' );
1165
0
                    }
1166
0
                }
1167
0
            }
1168
0
            break;
1169
1170
444
        case SvXMLStyleTokens::ScientificNumber:
1171
444
            {
1172
                // exponential interval for engineering notation
1173
444
                if( !aNumInfo.bGrouping && aNumInfo.nExpInterval > aNumInfo.nInteger )
1174
0
                {
1175
0
                    for (sal_Int32 i=aNumInfo.nInteger; i<aNumInfo.nExpInterval; i++)
1176
0
                    {
1177
0
                        rParent.AddToCode( '#' );
1178
0
                    }
1179
0
                }
1180
444
                rParent.AddNumber( aNumInfo );      //  number and exponent
1181
444
            }
1182
444
            break;
1183
1184
0
        default:
1185
0
            assert(false && "invalid element ID");
1186
58.6k
    }
1187
58.6k
}
1188
1189
sal_uInt16 SvXMLNumFmtDefaults::GetDefaultDateFormat( SvXMLDateElementAttributes eDOW,
1190
                SvXMLDateElementAttributes eDay, SvXMLDateElementAttributes eMonth,
1191
                SvXMLDateElementAttributes eYear, SvXMLDateElementAttributes eHours,
1192
                SvXMLDateElementAttributes eMins, SvXMLDateElementAttributes eSecs,
1193
                bool bSystem )
1194
168
{
1195
168
    for (const auto & rEntry : aDefaultDateFormats)
1196
1.11k
    {
1197
1.11k
        if ( bSystem == rEntry.bSystem &&
1198
767
            ( eDOW   == rEntry.eDOW   || ( rEntry.eDOW   == XML_DEA_ANY && eDOW   != XML_DEA_NONE ) ) &&
1199
728
            ( eDay   == rEntry.eDay   || ( rEntry.eDay   == XML_DEA_ANY && eDay   != XML_DEA_NONE ) ) &&
1200
524
            ( eMonth == rEntry.eMonth || ( rEntry.eMonth == XML_DEA_ANY && eMonth != XML_DEA_NONE ) ) &&
1201
341
            ( eYear  == rEntry.eYear  || ( rEntry.eYear  == XML_DEA_ANY && eYear  != XML_DEA_NONE ) ) &&
1202
160
            ( eHours == rEntry.eHours || ( rEntry.eHours == XML_DEA_ANY && eHours != XML_DEA_NONE ) ) &&
1203
155
            ( eMins  == rEntry.eMins  || ( rEntry.eMins  == XML_DEA_ANY && eMins  != XML_DEA_NONE ) ) &&
1204
155
            ( eSecs  == rEntry.eSecs  || ( rEntry.eSecs  == XML_DEA_ANY && eSecs  != XML_DEA_NONE ) ) )
1205
155
        {
1206
155
            return sal::static_int_cast< sal_uInt16 >(rEntry.eFormat);
1207
155
        }
1208
1.11k
    }
1209
1210
13
    return NF_INDEX_TABLE_ENTRIES;  // invalid
1211
168
}
1212
1213
1214
//  SvXMLNumFormatContext
1215
1216
SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport& rImport,
1217
                                    sal_Int32 /*nElement*/,
1218
                                    SvXMLNumImpData* pNewData, SvXMLStylesTokens nNewType,
1219
                                    const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
1220
                                    SvXMLStylesContext& rStyles ) :
1221
22.1k
    SvXMLStyleContext( rImport ),
1222
22.1k
    m_pData( pNewData ),
1223
22.1k
    m_pStyles( &rStyles ),
1224
22.1k
    m_nType( nNewType ),
1225
22.1k
    m_nKey(-1),
1226
22.1k
    m_eImplicitCalendar(ImplicitCalendar::DEFAULT),
1227
22.1k
    m_nFormatLang( LANGUAGE_SYSTEM ),
1228
22.1k
    m_bAutoOrder( false ),
1229
22.1k
    m_bFromSystem( false ),
1230
22.1k
    m_bTruncate( true ),
1231
22.1k
    m_bAutoDec( false ),
1232
22.1k
    m_bAutoInt( false ),
1233
22.1k
    m_bHasExtraText( false ),
1234
22.1k
    m_bHasTrailingEmptyText( false ),
1235
22.1k
    m_bHasLongDoW( false ),
1236
22.1k
    m_bHasDateTime( false ),
1237
22.1k
    m_bRemoveAfterUse( false ),
1238
22.1k
    m_eDateDOW( XML_DEA_NONE ),
1239
22.1k
    m_eDateDay( XML_DEA_NONE ),
1240
22.1k
    m_eDateMonth( XML_DEA_NONE ),
1241
22.1k
    m_eDateYear( XML_DEA_NONE ),
1242
22.1k
    m_eDateHours( XML_DEA_NONE ),
1243
22.1k
    m_eDateMins( XML_DEA_NONE ),
1244
22.1k
    m_eDateSecs( XML_DEA_NONE ),
1245
22.1k
    m_bDateNoDefault( false )
1246
22.1k
{
1247
22.1k
    LanguageTagODF aLanguageTagODF;
1248
22.1k
    css::i18n::NativeNumberXmlAttributes aNatNumAttr;
1249
22.1k
    OUString aSpellout;
1250
22.1k
    bool bAttrBool(false);
1251
1252
22.1k
    for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
1253
47.1k
    {
1254
47.1k
        switch (aIter.getToken())
1255
47.1k
        {
1256
        //  attributes for a style
1257
20.2k
            case XML_ELEMENT(STYLE, XML_NAME):
1258
20.2k
                break;
1259
0
            case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG):
1260
0
                aLanguageTagODF.maRfcLanguageTag = aIter.toString();
1261
0
                break;
1262
8.82k
            case XML_ELEMENT(NUMBER, XML_LANGUAGE):
1263
8.82k
                aLanguageTagODF.maLanguage = aIter.toString();
1264
8.82k
                break;
1265
0
            case XML_ELEMENT(NUMBER, XML_SCRIPT):
1266
0
                aLanguageTagODF.maScript = aIter.toString();
1267
0
                break;
1268
8.83k
            case XML_ELEMENT(NUMBER, XML_COUNTRY):
1269
8.83k
                aLanguageTagODF.maCountry = aIter.toString();
1270
8.83k
                break;
1271
12
            case XML_ELEMENT(NUMBER, XML_TITLE):
1272
12
                m_sFormatTitle = aIter.toString();
1273
12
                break;
1274
244
            case XML_ELEMENT(NUMBER, XML_AUTOMATIC_ORDER):
1275
244
                if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1276
243
                    m_bAutoOrder = bAttrBool;
1277
244
                break;
1278
0
            case XML_ELEMENT(NUMBER, XML_FORMAT_SOURCE):
1279
0
                SvXMLUnitConverter::convertEnum( m_bFromSystem, aIter.toView(), aFormatSourceMap );
1280
0
                break;
1281
1.19k
            case XML_ELEMENT(NUMBER, XML_TRUNCATE_ON_OVERFLOW):
1282
1.19k
                if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1283
1.18k
                    m_bTruncate = bAttrBool;
1284
1.19k
                break;
1285
7.58k
            case XML_ELEMENT(STYLE, XML_VOLATILE):
1286
                //  volatile formats can be removed after importing
1287
                //  if not used in other styles
1288
7.58k
                if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1289
7.52k
                    m_bRemoveAfterUse = bAttrBool;
1290
7.58k
                break;
1291
0
            case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_FORMAT):
1292
0
                aNatNumAttr.Format = aIter.toString();
1293
0
                break;
1294
0
            case XML_ELEMENT(LO_EXT, XML_TRANSLITERATION_SPELLOUT):
1295
0
            case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_SPELLOUT):
1296
0
                aSpellout = aIter.toString();
1297
0
                break;
1298
0
            case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_LANGUAGE):
1299
0
                aNatNumAttr.Locale.Language = aIter.toString();
1300
0
                break;
1301
0
            case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_COUNTRY):
1302
0
                aNatNumAttr.Locale.Country = aIter.toString();
1303
0
                break;
1304
0
            case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_STYLE):
1305
0
                aNatNumAttr.Style = aIter.toString();
1306
0
                break;
1307
231
            default:
1308
231
                XMLOFF_WARN_UNKNOWN("xmloff", aIter);
1309
47.1k
        }
1310
47.1k
    }
1311
1312
22.1k
    if (!aLanguageTagODF.isEmpty())
1313
9.06k
    {
1314
9.06k
        m_nFormatLang = aLanguageTagODF.getLanguageTag().getLanguageType( false);
1315
9.06k
        if ( m_nFormatLang == LANGUAGE_DONTKNOW )
1316
247
            m_nFormatLang = LANGUAGE_SYSTEM;          //! error handling for unknown locales?
1317
9.06k
    }
1318
1319
22.1k
    if (aNatNumAttr.Format.isEmpty() && aSpellout.isEmpty())
1320
22.1k
        return;
1321
1322
0
    LanguageTag aLanguageTag( OUString(), aNatNumAttr.Locale.Language,
1323
0
                std::u16string_view(), aNatNumAttr.Locale.Country);
1324
0
    aNatNumAttr.Locale = aLanguageTag.getLocale( false);
1325
1326
    // NatNum12 spell out formula (cardinal, ordinal, ordinal-feminine etc.)
1327
0
    if ( !aSpellout.isEmpty() )
1328
0
    {
1329
0
        m_aFormatCode.append( "[NatNum12 " );
1330
0
        m_aFormatCode.append( aSpellout );
1331
0
    } else {
1332
0
        SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1333
0
        if ( !pFormatter ) return;
1334
1335
0
        sal_Int32 nNatNum = pFormatter->GetNatNum().convertFromXmlAttributes( aNatNumAttr );
1336
0
        m_aFormatCode.append( "[NatNum" );
1337
0
        m_aFormatCode.append( nNatNum );
1338
0
    }
1339
1340
0
    LanguageType eLang = aLanguageTag.getLanguageType( false );
1341
0
    if ( eLang == LANGUAGE_DONTKNOW )
1342
0
        eLang = LANGUAGE_SYSTEM;            //! error handling for unknown locales?
1343
0
    if ( eLang != m_nFormatLang && eLang != LANGUAGE_SYSTEM )
1344
0
    {
1345
0
        m_aFormatCode.append("][$-" +
1346
                             // language code in upper hex:
1347
0
                             OUString::number(eLang.get(), 16).toAsciiUpperCase());
1348
0
    }
1349
0
    m_aFormatCode.append( ']' );
1350
0
}
1351
1352
SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport& rImport,
1353
                                    const OUString& rName,
1354
                                    const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/,
1355
                                    const sal_Int32 nTempKey, LanguageType nLang,
1356
                                    SvXMLStylesContext& rStyles ) :
1357
8.47k
    SvXMLStyleContext( rImport, XmlStyleFamily::DATA_STYLE ),
1358
8.47k
    m_pData( nullptr ),
1359
8.47k
    m_pStyles( &rStyles ),
1360
8.47k
    m_nType( SvXMLStylesTokens::NUMBER_STYLE ),
1361
8.47k
    m_nKey(nTempKey),
1362
8.47k
    m_eImplicitCalendar(ImplicitCalendar::DEFAULT),
1363
8.47k
    m_nFormatLang( nLang ),
1364
8.47k
    m_bAutoOrder( false ),
1365
8.47k
    m_bFromSystem( false ),
1366
8.47k
    m_bTruncate( true ),
1367
8.47k
    m_bAutoDec( false ),
1368
8.47k
    m_bAutoInt( false ),
1369
8.47k
    m_bHasExtraText( false ),
1370
8.47k
    m_bHasTrailingEmptyText( false ),
1371
8.47k
    m_bHasLongDoW( false ),
1372
8.47k
    m_bHasDateTime( false ),
1373
8.47k
    m_bRemoveAfterUse( false ),
1374
8.47k
    m_eDateDOW( XML_DEA_NONE ),
1375
8.47k
    m_eDateDay( XML_DEA_NONE ),
1376
8.47k
    m_eDateMonth( XML_DEA_NONE ),
1377
8.47k
    m_eDateYear( XML_DEA_NONE ),
1378
8.47k
    m_eDateHours( XML_DEA_NONE ),
1379
8.47k
    m_eDateMins( XML_DEA_NONE ),
1380
8.47k
    m_eDateSecs( XML_DEA_NONE ),
1381
8.47k
    m_bDateNoDefault( false )
1382
8.47k
{
1383
8.47k
    SetAttribute(XML_ELEMENT(STYLE, XML_NAME), rName);
1384
8.47k
}
1385
1386
SvXMLNumFormatContext::~SvXMLNumFormatContext()
1387
30.6k
{
1388
30.6k
}
1389
1390
css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFormatContext::createFastChildContext(
1391
    sal_Int32 nElement,
1392
    const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
1393
69.1k
{
1394
69.1k
    SvXMLImportContext* pContext = nullptr;
1395
1396
69.1k
    switch (nElement)
1397
69.1k
    {
1398
0
        case XML_ELEMENT(LO_EXT, XML_TEXT):
1399
24.8k
        case XML_ELEMENT(NUMBER, XML_TEXT):
1400
24.8k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1401
24.8k
                                                        *this, SvXMLStyleTokens::Text, xAttrList );
1402
24.8k
            break;
1403
0
        case XML_ELEMENT(LO_EXT, XML_FILL_CHARACTER):
1404
0
        case XML_ELEMENT(NUMBER, XML_FILL_CHARACTER):
1405
0
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1406
0
                                                        *this, SvXMLStyleTokens::FillCharacter, xAttrList );
1407
0
            break;
1408
13.4k
        case XML_ELEMENT(NUMBER, XML_NUMBER):
1409
13.4k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1410
13.4k
                                                        *this, SvXMLStyleTokens::Number, xAttrList );
1411
13.4k
            break;
1412
451
        case XML_ELEMENT(NUMBER, XML_SCIENTIFIC_NUMBER):
1413
451
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1414
451
                                                        *this, SvXMLStyleTokens::ScientificNumber, xAttrList );
1415
451
            break;
1416
0
        case XML_ELEMENT(NUMBER, XML_FRACTION):
1417
0
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1418
0
                                                        *this, SvXMLStyleTokens::Fraction, xAttrList );
1419
0
            break;
1420
3.96k
        case XML_ELEMENT(NUMBER, XML_CURRENCY_SYMBOL):
1421
3.96k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1422
3.96k
                                                        *this, SvXMLStyleTokens::CurrencySymbol, xAttrList );
1423
3.96k
            break;
1424
1.52k
        case XML_ELEMENT(NUMBER, XML_DAY):
1425
1.52k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1426
1.52k
                                                        *this, SvXMLStyleTokens::Day, xAttrList );
1427
1.52k
            break;
1428
1.77k
        case XML_ELEMENT(NUMBER, XML_MONTH):
1429
1.77k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1430
1.77k
                                                        *this, SvXMLStyleTokens::Month, xAttrList );
1431
1.77k
            break;
1432
1.46k
        case XML_ELEMENT(NUMBER, XML_YEAR):
1433
1.46k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1434
1.46k
                                                        *this, SvXMLStyleTokens::Year, xAttrList );
1435
1.46k
            break;
1436
0
        case XML_ELEMENT(NUMBER, XML_ERA):
1437
0
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1438
0
                                                        *this, SvXMLStyleTokens::Era, xAttrList );
1439
0
            break;
1440
91
        case XML_ELEMENT(NUMBER, XML_DAY_OF_WEEK):
1441
91
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1442
91
                                                        *this, SvXMLStyleTokens::DayOfWeek, xAttrList );
1443
91
            break;
1444
0
        case XML_ELEMENT(NUMBER, XML_WEEK_OF_YEAR):
1445
0
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1446
0
                                                        *this, SvXMLStyleTokens::WeekOfYear, xAttrList );
1447
0
            break;
1448
0
        case XML_ELEMENT(NUMBER, XML_QUARTER):
1449
0
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1450
0
                                                        *this, SvXMLStyleTokens::Quarter, xAttrList );
1451
0
            break;
1452
2.63k
        case XML_ELEMENT(NUMBER, XML_HOURS):
1453
2.63k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1454
2.63k
                                                        *this, SvXMLStyleTokens::Hours, xAttrList );
1455
2.63k
            break;
1456
841
        case XML_ELEMENT(NUMBER, XML_AM_PM):
1457
841
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1458
841
                                                        *this, SvXMLStyleTokens::AmPm, xAttrList );
1459
841
            break;
1460
3.61k
        case XML_ELEMENT(NUMBER, XML_MINUTES):
1461
3.61k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1462
3.61k
                                                        *this, SvXMLStyleTokens::Minutes, xAttrList );
1463
3.61k
            break;
1464
1.92k
        case XML_ELEMENT(NUMBER, XML_SECONDS):
1465
1.92k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1466
1.92k
                                                        *this, SvXMLStyleTokens::Seconds, xAttrList );
1467
1.92k
            break;
1468
125
        case XML_ELEMENT(NUMBER, XML_BOOLEAN):
1469
125
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1470
125
                                                        *this, SvXMLStyleTokens::Boolean, xAttrList );
1471
125
            break;
1472
1.95k
        case XML_ELEMENT(NUMBER, XML_TEXT_CONTENT):
1473
1.95k
            pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1474
1.95k
                                                        *this, SvXMLStyleTokens::TextContent, xAttrList );
1475
1.95k
            break;
1476
1477
2.43k
        case XML_ELEMENT(STYLE, XML_TEXT_PROPERTIES):
1478
2.43k
            pContext = new SvXMLNumFmtPropContext( GetImport(), nElement,
1479
2.43k
                                                        *this, xAttrList );
1480
2.43k
            break;
1481
7.58k
        case XML_ELEMENT(STYLE, XML_MAP):
1482
7.58k
            {
1483
                //  SvXMLNumFmtMapContext::EndElement adds to aMyConditions,
1484
                //  so there's no need for an extra flag
1485
7.58k
                pContext = new SvXMLNumFmtMapContext( GetImport(), nElement,
1486
7.58k
                                                            *this, xAttrList );
1487
7.58k
            }
1488
7.58k
            break;
1489
69.1k
    }
1490
1491
69.1k
    if( !pContext )
1492
428
    {
1493
428
        SAL_WARN("xmloff.core", "No context for unknown-element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
1494
428
        pContext = new SvXMLImportContext(GetImport());
1495
428
    }
1496
1497
69.1k
    return pContext;
1498
69.1k
}
1499
1500
sal_Int32 SvXMLNumFormatContext::GetKey()
1501
1.34k
{
1502
1.34k
    if (m_nKey > -1)
1503
1.05k
    {
1504
1.05k
        if (m_bRemoveAfterUse)
1505
0
        {
1506
            //  format is used -> don't remove
1507
0
            m_bRemoveAfterUse = false;
1508
0
            if (m_pData)
1509
0
                m_pData->SetUsed(m_nKey);
1510
1511
            //  Add to import's list of keys now - CreateAndInsert didn't add
1512
            //  the style if bRemoveAfterUse was set.
1513
0
            GetImport().AddNumberStyle( m_nKey, GetName() );
1514
0
        }
1515
1.05k
        return m_nKey;
1516
1.05k
    }
1517
295
    else
1518
295
    {
1519
        // reset bRemoveAfterUse before CreateAndInsert, so AddKey is called without bRemoveAfterUse set
1520
295
        m_bRemoveAfterUse = false;
1521
295
        CreateAndInsert(true);
1522
295
        return m_nKey;
1523
295
    }
1524
1.34k
}
1525
1526
sal_Int32 SvXMLNumFormatContext::PrivateGetKey(std::vector<SvXMLNumFormatContext*>& rCreateStack)
1527
5.54k
{
1528
    //  used for map elements in CreateAndInsert - don't reset bRemoveAfterUse flag
1529
1530
5.54k
    if (m_nKey > -1)
1531
5.50k
        return m_nKey;
1532
47
    else
1533
47
    {
1534
47
        CreateAndInsert(true, rCreateStack);
1535
47
        return m_nKey;
1536
47
    }
1537
5.54k
}
1538
1539
sal_Int32 SvXMLNumFormatContext::CreateAndInsert( css::uno::Reference< css::util::XNumberFormatsSupplier > const & xFormatsSupplier )
1540
0
{
1541
0
    if (m_nKey <= -1)
1542
0
    {
1543
0
        SvNumberFormatter* pFormatter = nullptr;
1544
0
        SvNumberFormatsSupplierObj* pObj =
1545
0
                        comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xFormatsSupplier );
1546
0
        if (pObj)
1547
0
            pFormatter = pObj->GetNumberFormatter();
1548
1549
0
        if ( pFormatter )
1550
0
        {
1551
0
            std::vector<SvXMLNumFormatContext*> aCreateStack;
1552
0
            return CreateAndInsert(pFormatter, aCreateStack);
1553
0
        }
1554
0
        else
1555
0
            return -1;
1556
0
    }
1557
0
    else
1558
0
        return m_nKey;
1559
0
}
1560
1561
void SvXMLNumFormatContext::CreateAndInsert(bool bOverwrite)
1562
17.0k
{
1563
17.0k
    std::vector<SvXMLNumFormatContext*> aCreateStack;
1564
17.0k
    return CreateAndInsert(bOverwrite, aCreateStack);
1565
17.0k
}
1566
1567
void SvXMLNumFormatContext::CreateAndInsert(bool /*bOverwrite*/, std::vector<SvXMLNumFormatContext*>& rCreateStack)
1568
17.1k
{
1569
17.1k
    if (m_nKey <= -1)
1570
17.0k
        CreateAndInsert(m_pData->GetNumberFormatter(), rCreateStack);
1571
17.1k
}
1572
1573
sal_Int32 SvXMLNumFormatContext::CreateAndInsert(SvNumberFormatter* pFormatter, std::vector<SvXMLNumFormatContext*>& rCreateStack)
1574
17.0k
{
1575
17.0k
    if (!pFormatter)
1576
0
    {
1577
0
        OSL_FAIL("no number formatter");
1578
0
        return -1;
1579
0
    }
1580
1581
17.0k
    rCreateStack.push_back(this);
1582
1583
17.0k
    sal_uInt32 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1584
1585
23.4k
    for (size_t i = 0; i < m_aMyConditions.size(); i++)
1586
6.34k
    {
1587
6.34k
        SvXMLNumFormatContext* pStyle = const_cast<SvXMLNumFormatContext*>( static_cast<const SvXMLNumFormatContext *>(m_pStyles->FindStyleChildContext(
1588
6.34k
            XmlStyleFamily::DATA_STYLE, m_aMyConditions[i].sMapName)));
1589
6.34k
        if (std::find(rCreateStack.begin(), rCreateStack.end(), pStyle) != rCreateStack.end())
1590
113
        {
1591
113
            SAL_INFO("xmloff.style", "invalid style:map references containing style");
1592
113
            pStyle = nullptr;
1593
113
        }
1594
6.34k
        if (pStyle)
1595
5.54k
        {
1596
5.54k
            if (pStyle->PrivateGetKey(rCreateStack) > -1)     // don't reset pStyle's bRemoveAfterUse flag
1597
5.54k
                AddCondition(i);
1598
5.54k
        }
1599
6.34k
    }
1600
1601
17.0k
    sal_Int32 nBufLen;
1602
17.0k
    if ( m_aFormatCode.isEmpty() )
1603
350
    {
1604
        //  insert empty format as empty string (with quotes)
1605
        //  #93901# this check has to be done before inserting the conditions
1606
350
        m_aFormatCode.append("\"\"");    // ""
1607
350
    }
1608
16.7k
    else if (m_bHasTrailingEmptyText && (nBufLen = m_aFormatCode.getLength()) >= 3)
1609
1
    {
1610
        // Remove a trailing empty text. Earlier this may had been written to
1611
        // file, like in "General;General" written with elements for
1612
        // 'General"";General""' (whyever); when reading, empty text was
1613
        // ignored, which it isn't anymore, so get rid of those.
1614
1
        if (m_aFormatCode[nBufLen-1] == '"' && m_aFormatCode[nBufLen-2] == '"')
1615
0
            m_aFormatCode.truncate( nBufLen - 2);
1616
1
    }
1617
1618
17.0k
    m_aFormatCode.insert( 0, m_aConditions );
1619
17.0k
    m_aConditions.setLength(0);
1620
17.0k
    OUString sFormat = m_aFormatCode.makeStringAndClear();
1621
1622
    //  test special cases
1623
1624
17.0k
    if ( m_bAutoDec )         // automatic decimal places
1625
1.91k
    {
1626
        //  #99391# adjust only if the format contains no text elements, no conditions
1627
        //  and no color definition (detected by the '[' at the start)
1628
1629
1.91k
        if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText &&
1630
1.30k
                m_aMyConditions.empty() && sFormat.toChar() != '[' )
1631
1.30k
            nIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1632
1.91k
    }
1633
17.0k
    if ( m_bAutoInt )         // automatic integer digits
1634
635
    {
1635
        //! only if two decimal places was set?
1636
1637
635
        if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText &&
1638
176
                m_aMyConditions.empty() && sFormat.toChar() != '[' )
1639
176
            nIndex = pFormatter->GetFormatIndex( NF_NUMBER_SYSTEM, m_nFormatLang );
1640
635
    }
1641
1642
17.0k
    if ( m_nType == SvXMLStylesTokens::BOOLEAN_STYLE && !m_bHasExtraText &&
1643
0
            m_aMyConditions.empty() && sFormat.toChar() != '[' )
1644
0
        nIndex = pFormatter->GetFormatIndex( NF_BOOLEAN, m_nFormatLang );
1645
1646
    //  check for default date formats
1647
17.0k
    if ( m_nType == SvXMLStylesTokens::DATE_STYLE && m_bAutoOrder && !m_bDateNoDefault )
1648
168
    {
1649
168
        NfIndexTableOffset eFormat = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1650
168
            m_eDateDOW, m_eDateDay, m_eDateMonth, m_eDateYear,
1651
168
            m_eDateHours, m_eDateMins, m_eDateSecs, m_bFromSystem ));
1652
168
        if ( eFormat < NF_INDEX_TABLE_RESERVED_START )
1653
155
        {
1654
            //  #109651# if a date format has the automatic-order attribute and
1655
            //  contains exactly the elements of one of the default date formats,
1656
            //  use that default format, with the element order and separators
1657
            //  from the current locale settings
1658
1659
155
            nIndex = pFormatter->GetFormatIndex( eFormat, m_nFormatLang );
1660
155
        }
1661
168
    }
1662
1663
17.0k
    if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND && !sFormat.isEmpty() )
1664
15.5k
    {
1665
        //  insert by format string
1666
1667
15.5k
        OUString aFormatStr( sFormat );
1668
15.5k
        nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang );
1669
15.5k
        if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
1670
12.7k
        {
1671
12.7k
            sal_Int32  nErrPos = 0;
1672
12.7k
            SvNumFormatType l_nType = SvNumFormatType::ALL;
1673
12.7k
            bool bOk = pFormatter->PutEntry( aFormatStr, nErrPos, l_nType, nIndex, m_nFormatLang );
1674
12.7k
            if ( !bOk && nErrPos == 0 && aFormatStr != sFormat )
1675
0
            {
1676
                //  if the string was modified by PutEntry, look for an existing format
1677
                //  with the modified string
1678
0
                nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang );
1679
0
                if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND )
1680
0
                    bOk = true;
1681
0
            }
1682
12.7k
            if (!bOk)
1683
61
                nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1684
12.7k
        }
1685
15.5k
    }
1686
1687
//! I18N doesn't provide SYSTEM or extended date information yet
1688
17.0k
    if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND && !m_bAutoOrder )
1689
16.8k
    {
1690
        //  use fixed-order formats instead of SYS... if bAutoOrder is false
1691
        //  (only if the format strings are equal for the locale)
1692
1693
16.8k
        NfIndexTableOffset eOffset = SvNumberFormatter::GetIndexTableOffset( nIndex );
1694
16.8k
        if ( eOffset == NF_DATE_SYS_DMMMYYYY )
1695
0
        {
1696
0
            sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMYYYY, m_nFormatLang );
1697
0
            const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex );
1698
0
            const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex );
1699
0
            if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() )
1700
0
                nIndex = nNewIndex;
1701
0
        }
1702
16.8k
        else if ( eOffset == NF_DATE_SYS_DMMMMYYYY )
1703
0
        {
1704
0
            sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMMYYYY, m_nFormatLang );
1705
0
            const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex );
1706
0
            const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex );
1707
0
            if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() )
1708
0
                nIndex = nNewIndex;
1709
0
        }
1710
16.8k
    }
1711
1712
17.0k
    if ((nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND) && !m_sFormatTitle.isEmpty())
1713
0
    {
1714
0
        SvNumberformat* pFormat = const_cast<SvNumberformat*>(pFormatter->GetEntry( nIndex ));
1715
0
        if (pFormat)
1716
0
        {
1717
0
            pFormat->SetComment(m_sFormatTitle);
1718
0
        }
1719
0
    }
1720
1721
17.0k
    if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
1722
61
    {
1723
61
        OSL_FAIL("invalid number format");
1724
61
        nIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1725
61
    }
1726
1727
17.0k
    m_pData->AddKey( nIndex, GetName(), m_bRemoveAfterUse );
1728
17.0k
    m_nKey = nIndex;
1729
1730
    //  Add to import's list of keys (shared between styles and content import)
1731
    //  only if not volatile - formats are removed from NumberFormatter at the
1732
    //  end of each import (in SvXMLNumFmtHelper dtor).
1733
    //  If bRemoveAfterUse is reset later in GetKey, AddNumberStyle is called there.
1734
1735
17.0k
    if (!m_bRemoveAfterUse)
1736
10.9k
        GetImport().AddNumberStyle( m_nKey, GetName() );
1737
1738
17.0k
    rCreateStack.pop_back();
1739
1740
17.0k
    return m_nKey;
1741
17.0k
}
1742
1743
const LocaleDataWrapper& SvXMLNumFormatContext::GetLocaleData() const
1744
8.82k
{
1745
8.82k
    return m_pData->GetLocaleData( m_nFormatLang );
1746
8.82k
}
1747
1748
void SvXMLNumFormatContext::AddToCode( sal_Unicode c )
1749
2.29k
{
1750
2.29k
    m_aFormatCode.append( c );
1751
2.29k
    m_bHasExtraText = true;
1752
2.29k
}
1753
1754
void SvXMLNumFormatContext::AddToCode( std::u16string_view rString )
1755
25.1k
{
1756
25.1k
    m_aFormatCode.append( rString );
1757
25.1k
    m_bHasExtraText = true;
1758
25.1k
    m_bHasTrailingEmptyText = false;  // is set by caller again if so
1759
25.1k
}
1760
1761
void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo& rInfo )
1762
13.8k
{
1763
13.8k
    SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1764
13.8k
    if (!pFormatter)
1765
0
        return;
1766
1767
    //  store special conditions
1768
13.8k
    m_bAutoDec = ( rInfo.nDecimals < 0 );
1769
13.8k
    m_bAutoInt = ( rInfo.nInteger < 0 );
1770
1771
13.8k
    sal_uInt16 nPrec = 0;
1772
13.8k
    sal_uInt16 nLeading = 0;
1773
13.8k
    if ( rInfo.nDecimals >= 0 )                     //  < 0 : Default
1774
10.9k
        nPrec = static_cast<sal_uInt16>(rInfo.nDecimals);
1775
13.8k
    if ( rInfo.nInteger >= 0 )                      //  < 0 : Default
1776
12.7k
        nLeading = static_cast<sal_uInt16>(rInfo.nInteger);
1777
1778
13.8k
    if ( m_bAutoDec )
1779
2.92k
    {
1780
2.92k
        if ( m_nType == SvXMLStylesTokens::CURRENCY_STYLE )
1781
583
        {
1782
            //  for currency formats, "automatic decimals" is used for the automatic
1783
            //  currency format with (fixed) decimals from the locale settings
1784
1785
583
            const LocaleDataWrapper& rLoc = m_pData->GetLocaleData( m_nFormatLang );
1786
583
            nPrec = rLoc.getCurrDigits();
1787
583
        }
1788
2.34k
        else
1789
2.34k
        {
1790
            //  for other types, "automatic decimals" means dynamic determination of
1791
            //  decimals, as achieved with the "general" keyword
1792
1793
2.34k
            m_aFormatCode.append( pFormatter->GetStandardName( m_nFormatLang ) );
1794
2.34k
            return;
1795
2.34k
        }
1796
2.92k
    }
1797
11.5k
    if ( m_bAutoInt )
1798
717
    {
1799
        //!...
1800
717
    }
1801
1802
11.5k
    sal_uInt16 nGenPrec = nPrec;
1803
11.5k
    if ( rInfo.nMinDecimalDigits >= 0 )
1804
10.9k
        nGenPrec = rInfo.nMinDecimalDigits;
1805
11.5k
    if ( rInfo.bDecReplace )
1806
0
        nGenPrec = 0;               // generate format without decimals...
1807
1808
11.5k
    bool bGrouping = rInfo.bGrouping;
1809
11.5k
    size_t const nEmbeddedCount = rInfo.m_EmbeddedElements.size();
1810
11.5k
    if ( nEmbeddedCount && rInfo.m_EmbeddedElements.rbegin()->first > 0 )
1811
0
        bGrouping = false;      // grouping and embedded characters in integer part can't be used together
1812
1813
11.5k
    sal_uInt32 nStdIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1814
11.5k
    OUStringBuffer aNumStr(pFormatter->GenerateFormat( nStdIndex, m_nFormatLang,
1815
11.5k
                                                         bGrouping, false, nGenPrec, nLeading ));
1816
1817
11.5k
    if ( rInfo.nExpDigits >= 0 && nLeading == 0 && !bGrouping && nEmbeddedCount == 0 )
1818
53
    {
1819
        // #i43959# For scientific numbers, "#" in the integer part forces a digit,
1820
        // so it has to be removed if nLeading is 0 (".00E+0", not "#.00E+0").
1821
1822
53
        aNumStr.stripStart('#');
1823
53
    }
1824
1825
11.5k
    if ( rInfo.nBlankInteger > 0 )
1826
0
    {
1827
        // Replace nBlankInteger '0' by '?'
1828
0
        sal_Int32 nIndex = 0;
1829
0
        sal_Int32 nBlanks = rInfo.nBlankInteger;
1830
0
        sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() );
1831
0
        if ( nIntegerEnd < 0 )
1832
0
            nIntegerEnd = aNumStr.getLength();
1833
0
        while ( nIndex < nIntegerEnd && nBlanks > 0 )
1834
0
        {
1835
0
            if ( aNumStr[nIndex] == '0' )
1836
0
            {
1837
0
                aNumStr[nIndex] = '?';
1838
0
                nBlanks--;
1839
0
            }
1840
0
            nIndex++;
1841
0
        }
1842
0
    }
1843
1844
11.5k
    if ( bGrouping && rInfo.nExpInterval > rInfo.nInteger )
1845
0
    {
1846
0
        sal_Int32 nIndex = 0;
1847
0
        sal_Int32 nDigits = rInfo.nInteger;
1848
0
        sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() );
1849
0
        if ( nIntegerEnd < 0 )
1850
0
            nIntegerEnd = aNumStr.getLength();
1851
0
        while ( nIndex >= 0 && nIndex < nIntegerEnd )
1852
0
        {
1853
0
            if ( ( nIndex = aNumStr.indexOf( '#', nIndex ) ) >= 0 )
1854
0
            {
1855
0
                nDigits ++;
1856
0
                nIndex ++;
1857
0
            }
1858
0
            else
1859
0
                nIndex = -1;
1860
0
        }
1861
0
        while ( rInfo.nExpInterval > nDigits )
1862
0
        {
1863
0
            nDigits++;
1864
0
            aNumStr.insert( 0, '#' );
1865
0
        }
1866
0
    }
1867
1868
11.5k
    if ( ( rInfo.bDecReplace || rInfo.nMinDecimalDigits < rInfo.nDecimals ) && nPrec )     // add decimal replacement (dashes)
1869
0
    {
1870
        //  add dashes for explicit decimal replacement, # or ? for variable decimals
1871
0
        sal_Unicode cAdd = rInfo.bDecReplace ? '-' : ( rInfo.bDecAlign ? '?': '#' );
1872
1873
0
        if ( rInfo.nMinDecimalDigits == 0 )
1874
0
            aNumStr.append( m_pData->GetLocaleData( m_nFormatLang ).getNumDecimalSep() );
1875
0
        for ( sal_uInt16 i=rInfo.nMinDecimalDigits; i<nPrec; i++)
1876
0
            aNumStr.append( cAdd );
1877
0
    }
1878
1879
    // Scientific number
1880
11.5k
    sal_Int32 nExpPos = -1;
1881
11.5k
    if ( rInfo.nExpDigits > 0 )
1882
397
    {
1883
397
        nExpPos = aNumStr.getLength();
1884
397
        aNumStr.append( rInfo.bExponentLowercase ? u"e" : u"E" );
1885
                                // exponent sign is required with embedded text in exponent
1886
397
        if ( rInfo.bExpSign || ( nEmbeddedCount && ( rInfo.nDecimals + 1 < -rInfo.m_EmbeddedElements.begin()->first ) ) )
1887
397
        {
1888
397
            aNumStr.append( u"+" );
1889
397
        }
1890
795
        for (sal_Int32 i=0; i<rInfo.nExpDigits; i++)
1891
398
        {
1892
398
            if ( i < rInfo.nBlankExp )
1893
0
                aNumStr.append( '?' );
1894
398
            else
1895
398
                aNumStr.append( '0' );
1896
398
        }
1897
397
    }
1898
1899
11.5k
    if ( nEmbeddedCount )
1900
0
    {
1901
        //  insert embedded strings into number string
1902
        //  support integer (position >=0) and decimal (position <0) part
1903
        //  nZeroPos is the string position where format position 0 is inserted
1904
1905
0
        sal_Int32 nZeroPos = aNumStr.indexOf( m_pData->GetLocaleData( m_nFormatLang ).getNumDecimalSep() );
1906
0
        if ( nZeroPos < 0 )
1907
0
        {
1908
0
            nZeroPos = aNumStr.getLength();
1909
0
        }
1910
1911
        // m_EmbeddedElements is sorted - last entry has the largest position (leftmost)
1912
0
        sal_Int32 const nLastFormatPos = rInfo.m_EmbeddedElements.rbegin()->first;
1913
0
        if ( nLastFormatPos >= nZeroPos )
1914
0
        {
1915
            //  add '#' characters so all embedded texts are really embedded in digits
1916
            //  (there always has to be a digit before the leftmost embedded text)
1917
1918
0
            sal_Int32 nAddCount = nLastFormatPos + 1 - nZeroPos;
1919
0
            for(sal_Int32 index = 0; index < nAddCount; ++index)
1920
0
            {
1921
0
                aNumStr.insert(0, '#');
1922
0
            }
1923
0
            nZeroPos = nZeroPos + nAddCount;
1924
0
            if ( nExpPos > 0 )
1925
0
                nExpPos = nExpPos + nAddCount;
1926
0
        }
1927
1928
        // m_EmbeddedElements is sorted with ascending positions - loop is from right to left
1929
0
        for (auto const& it : rInfo.m_EmbeddedElements)
1930
0
        {
1931
0
            sal_Int32 const nFormatPos = it.first;
1932
0
            sal_Int32 nInsertPos = nZeroPos - nFormatPos;
1933
0
            if ( nExpPos > 0 && nInsertPos > nExpPos )
1934
0
                nInsertPos ++;
1935
0
            if ( 0 <= nInsertPos && nInsertPos <= aNumStr.getLength() )
1936
0
            {
1937
0
                aNumStr.insert( nInsertPos, it.second );
1938
0
            }
1939
0
        }
1940
0
    }
1941
1942
11.5k
    m_aFormatCode.append( aNumStr );
1943
1944
    //  add extra thousands separators for display factor
1945
1946
11.5k
    if (rInfo.fDisplayFactor == 1.0 || rInfo.fDisplayFactor <= 0.0)
1947
11.5k
        return;
1948
1949
    //  test for 1.0 is just for optimization - nSepCount would be 0
1950
1951
    //  one separator for each factor of 1000
1952
0
    sal_Int32 nSepCount = static_cast<sal_Int32>(::rtl::math::round( log10(rInfo.fDisplayFactor) / 3.0 ));
1953
0
    if ( nSepCount > 0 )
1954
0
    {
1955
0
        OUString aSep = m_pData->GetLocaleData( m_nFormatLang ).getNumThousandSep();
1956
0
        for ( sal_Int32 i=0; i<nSepCount; i++ )
1957
0
            m_aFormatCode.append( aSep );
1958
0
    }
1959
0
}
1960
1961
void SvXMLNumFormatContext::AddCurrency( const OUString& rContent, LanguageType nLang )
1962
3.93k
{
1963
3.93k
    bool bAutomatic = false;
1964
3.93k
    OUString aSymbol = rContent;
1965
3.93k
    if ( aSymbol.isEmpty())
1966
11
    {
1967
11
        SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1968
11
        if ( pFormatter )
1969
11
        {
1970
11
            pFormatter->ChangeIntl( m_nFormatLang );
1971
11
            OUString sCurString, sDummy;
1972
11
            pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
1973
11
            aSymbol = sCurString;
1974
1975
11
            bAutomatic = true;
1976
11
        }
1977
11
    }
1978
3.92k
    else if ( nLang == LANGUAGE_SYSTEM && aSymbol == "CCC" )
1979
0
    {
1980
        //  "CCC" is used for automatic long symbol
1981
0
        bAutomatic = true;
1982
0
    }
1983
1984
3.93k
    if ( bAutomatic )
1985
11
    {
1986
        //  remove unnecessary quotes before automatic symbol (formats like "-(0DM)")
1987
        //  otherwise the currency symbol isn't recognized (#94048#)
1988
1989
11
        sal_Int32 nLength = m_aFormatCode.getLength();
1990
11
        if ( nLength > 1 && m_aFormatCode[nLength - 1] == '"' )
1991
0
        {
1992
            //  find start of quoted string
1993
            //  When SvXMLNumFmtElementContext::EndElement creates escaped quotes,
1994
            //  they must be handled here, too.
1995
1996
0
            sal_Int32 nFirst = nLength - 2;
1997
0
            while ( nFirst >= 0 && m_aFormatCode[nFirst] != '"' )
1998
0
                --nFirst;
1999
0
            if ( nFirst >= 0 )
2000
0
            {
2001
                //  remove both quotes from aFormatCode
2002
0
                OUString aOld = m_aFormatCode.makeStringAndClear();
2003
0
                if ( nFirst > 0 )
2004
0
                    m_aFormatCode.append( aOld.subView( 0, nFirst ) );
2005
0
                if ( nLength > nFirst + 2 )
2006
0
                    m_aFormatCode.append( aOld.subView( nFirst + 1, nLength - nFirst - 2 ) );
2007
0
            }
2008
0
        }
2009
11
    }
2010
2011
3.93k
    if (!bAutomatic)
2012
3.92k
        m_aFormatCode.append( "[$" );            // intro for "new" currency symbols
2013
2014
3.93k
    m_aFormatCode.append( aSymbol );
2015
2016
3.93k
    if (!bAutomatic)
2017
3.92k
    {
2018
3.92k
        if ( nLang != LANGUAGE_SYSTEM )
2019
3.67k
        {
2020
            //  '-' sign and language code in hex:
2021
3.67k
            m_aFormatCode.append("-" + OUString(OUString::number(sal_uInt16(nLang), 16)).toAsciiUpperCase());
2022
3.67k
        }
2023
2024
3.92k
        m_aFormatCode.append( ']' );    // end of "new" currency symbol
2025
3.92k
    }
2026
3.93k
}
2027
2028
void SvXMLNumFormatContext::AddNfKeyword( sal_uInt16 nIndex )
2029
13.9k
{
2030
13.9k
    SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2031
13.9k
    if (!pFormatter)
2032
0
        return;
2033
2034
13.9k
    if ( nIndex == NF_KEY_NNNN )
2035
13
    {
2036
13
        nIndex = NF_KEY_NNN;
2037
13
        m_bHasLongDoW = true;         // to remove string constant with separator
2038
13
    }
2039
2040
13.9k
    OUString sKeyword = pFormatter->GetKeyword( m_nFormatLang, nIndex );
2041
2042
13.9k
    if ( nIndex == NF_KEY_H  || nIndex == NF_KEY_HH  ||
2043
11.3k
         nIndex == NF_KEY_MI || nIndex == NF_KEY_MMI ||
2044
7.74k
         nIndex == NF_KEY_S  || nIndex == NF_KEY_SS )
2045
8.17k
    {
2046
8.17k
        if ( !m_bTruncate && !m_bHasDateTime )
2047
1.17k
        {
2048
            //  with truncate-on-overflow = false, add "[]" to first time part
2049
1.17k
            m_aFormatCode.append("[" + sKeyword + "]");
2050
1.17k
        }
2051
6.99k
        else
2052
6.99k
        {
2053
6.99k
            m_aFormatCode.append( sKeyword );
2054
6.99k
        }
2055
8.17k
        m_bHasDateTime = true;
2056
8.17k
    }
2057
5.82k
    else
2058
5.82k
    {
2059
5.82k
        m_aFormatCode.append( sKeyword );
2060
5.82k
    }
2061
    //  collect the date elements that the format contains, to recognize default date formats
2062
13.9k
    switch ( nIndex )
2063
13.9k
    {
2064
78
        case NF_KEY_NN:     m_eDateDOW = XML_DEA_SHORT;       break;
2065
13
        case NF_KEY_NNN:
2066
13
        case NF_KEY_NNNN:   m_eDateDOW = XML_DEA_LONG;        break;
2067
65
        case NF_KEY_D:      m_eDateDay = XML_DEA_SHORT;       break;
2068
1.45k
        case NF_KEY_DD:     m_eDateDay = XML_DEA_LONG;        break;
2069
23
        case NF_KEY_M:      m_eDateMonth = XML_DEA_SHORT;     break;
2070
968
        case NF_KEY_MM:     m_eDateMonth = XML_DEA_LONG;      break;
2071
775
        case NF_KEY_MMM:    m_eDateMonth = XML_DEA_TEXTSHORT; break;
2072
7
        case NF_KEY_MMMM:   m_eDateMonth = XML_DEA_TEXTLONG;  break;
2073
1.10k
        case NF_KEY_YY:     m_eDateYear = XML_DEA_SHORT;      break;
2074
359
        case NF_KEY_YYYY:   m_eDateYear = XML_DEA_LONG;       break;
2075
1.71k
        case NF_KEY_H:      m_eDateHours = XML_DEA_SHORT;     break;
2076
915
        case NF_KEY_HH:     m_eDateHours = XML_DEA_LONG;      break;
2077
109
        case NF_KEY_MI:     m_eDateMins = XML_DEA_SHORT;      break;
2078
3.50k
        case NF_KEY_MMI:    m_eDateMins = XML_DEA_LONG;       break;
2079
38
        case NF_KEY_S:      m_eDateSecs = XML_DEA_SHORT;      break;
2080
1.89k
        case NF_KEY_SS:     m_eDateSecs = XML_DEA_LONG;       break;
2081
0
        case NF_KEY_AP:
2082
841
        case NF_KEY_AMPM:   break;          // AM/PM may or may not be in date/time formats -> ignore by itself
2083
125
        default:
2084
125
            m_bDateNoDefault = true;      // any other element -> no default format
2085
13.9k
    }
2086
13.9k
}
2087
2088
static bool lcl_IsAtEnd( OUStringBuffer& rBuffer, std::u16string_view rToken )
2089
12
{
2090
12
    sal_Int32 nBufLen = rBuffer.getLength();
2091
12
    sal_Int32 nTokLen = rToken.size();
2092
2093
12
    if ( nTokLen > nBufLen )
2094
0
        return false;
2095
2096
12
    sal_Int32 nStartPos = nBufLen - nTokLen;
2097
48
    for ( sal_Int32 nTokPos = 0; nTokPos < nTokLen; nTokPos++ )
2098
36
        if ( rToken[ nTokPos ] != rBuffer[nStartPos + nTokPos] )
2099
0
            return false;
2100
2101
12
    return true;
2102
12
}
2103
2104
bool SvXMLNumFormatContext::ReplaceNfKeyword( sal_uInt16 nOld, sal_uInt16 nNew )
2105
12
{
2106
    //  replaces one keyword with another if it is found at the end of the code
2107
2108
12
    SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2109
12
    if (!pFormatter)
2110
0
        return false;
2111
2112
12
    OUString sOldStr = pFormatter->GetKeyword( m_nFormatLang, nOld );
2113
12
    if ( lcl_IsAtEnd( m_aFormatCode, sOldStr ) )
2114
12
    {
2115
        // remove old keyword
2116
12
        m_aFormatCode.setLength( m_aFormatCode.getLength() - sOldStr.getLength() );
2117
2118
        // add new keyword
2119
12
        OUString sNewStr = pFormatter->GetKeyword( m_nFormatLang, nNew );
2120
12
        m_aFormatCode.append( sNewStr );
2121
2122
12
        return true;    // changed
2123
12
    }
2124
0
    return false;       // not found
2125
12
}
2126
2127
void SvXMLNumFormatContext::AddCondition( const sal_Int32 nIndex )
2128
5.54k
{
2129
5.54k
    OUString rApplyName = m_aMyConditions[nIndex].sMapName;
2130
5.54k
    OUString rCondition = m_aMyConditions[nIndex].sCondition;
2131
5.54k
    SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2132
5.54k
    sal_uInt32 l_nKey = m_pData->GetKeyForName( rApplyName );
2133
2134
5.54k
    OUString sRealCond;
2135
5.54k
    if ( !(pFormatter && l_nKey != NUMBERFORMAT_ENTRY_NOT_FOUND &&
2136
5.54k
            rCondition.startsWith("value()", &sRealCond)) )
2137
388
        return;
2138
2139
    //! test for valid conditions
2140
    //! test for default conditions
2141
2142
5.15k
    bool bDefaultCond = false;
2143
2144
    //! collect all conditions first and adjust default to >=0, >0 or <0 depending on count
2145
    //! allow blanks in conditions
2146
5.15k
    if ( m_aConditions.isEmpty() && m_aMyConditions.size() == 1 && sRealCond == ">=0" )
2147
3.15k
        bDefaultCond = true;
2148
2149
5.15k
    if ( m_nType == SvXMLStylesTokens::TEXT_STYLE && static_cast<size_t>(nIndex) == m_aMyConditions.size() - 1 )
2150
1.36k
    {
2151
        //  The last condition in a number format with a text part can only
2152
        //  be "all other numbers", the condition string must be empty.
2153
1.36k
        bDefaultCond = true;
2154
1.36k
    }
2155
2156
5.15k
    if (!bDefaultCond)
2157
1.35k
    {
2158
        // Convert != to <>
2159
1.35k
        sal_Int32 nPos = sRealCond.indexOf( "!=" );
2160
1.35k
        if ( nPos >= 0 )
2161
0
        {
2162
0
            sRealCond = sRealCond.replaceAt( nPos, 2, u"<>" );
2163
0
        }
2164
2165
1.35k
        nPos = sRealCond.indexOf( '.' );
2166
1.35k
        if ( nPos >= 0 )
2167
1
        {
2168
            // #i8026# #103991# localize decimal separator
2169
1
            const OUString& rDecSep = GetLocaleData().getNumDecimalSep();
2170
1
            if ( rDecSep.getLength() > 1 || rDecSep[0] != '.' )
2171
0
            {
2172
0
                sRealCond = sRealCond.replaceAt( nPos, 1, rDecSep );
2173
0
            }
2174
1
        }
2175
1.35k
        m_aConditions.append("[" + sRealCond + "]");
2176
1.35k
    }
2177
2178
5.15k
    const SvNumberformat* pFormat = pFormatter->GetEntry(l_nKey);
2179
5.15k
    if ( pFormat )
2180
5.15k
        m_aConditions.append( pFormat->GetFormatstring() );
2181
2182
5.15k
    m_aConditions.append( ';' );
2183
5.15k
}
2184
2185
void SvXMLNumFormatContext::AddCondition( const OUString& rCondition, const OUString& rApplyName )
2186
7.58k
{
2187
7.58k
    MyCondition aCondition;
2188
7.58k
    aCondition.sCondition = rCondition;
2189
7.58k
    aCondition.sMapName = rApplyName;
2190
7.58k
    m_aMyConditions.push_back(aCondition);
2191
7.58k
}
2192
2193
void SvXMLNumFormatContext::AddColor( Color const nColor )
2194
1.85k
{
2195
1.85k
    SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2196
1.85k
    if (!pFormatter)
2197
0
        return;
2198
2199
1.85k
    OUStringBuffer aColName;
2200
9.52k
    for ( sal_uInt16 i=0; i<XML_NUMF_COLORCOUNT; i++ )
2201
9.48k
        if (nColor == aNumFmtStdColors[i])
2202
1.81k
        {
2203
1.81k
            aColName = pFormatter->GetKeyword( m_nFormatLang, sal::static_int_cast< sal_uInt16 >(NF_KEY_FIRSTCOLOR + i) );
2204
1.81k
            break;
2205
1.81k
        }
2206
2207
1.85k
    if ( !aColName.isEmpty() )
2208
1.81k
    {
2209
1.81k
        aColName.insert( 0, '[' );
2210
1.81k
        aColName.append( ']' );
2211
1.81k
        m_aFormatCode.insert( 0, aColName );
2212
1.81k
    }
2213
1.85k
}
2214
2215
void SvXMLNumFormatContext::UpdateCalendar( const OUString& rNewCalendar )
2216
4.85k
{
2217
4.85k
    if ( rNewCalendar == m_sCalendar )
2218
4.85k
        return;
2219
2220
0
    if (rNewCalendar.isEmpty() || rNewCalendar == m_aImplicitCalendar[0])
2221
0
    {
2222
0
        m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2223
0
                ImplicitCalendar::DEFAULT_FROM_OTHER : ImplicitCalendar::DEFAULT);
2224
0
    }
2225
0
    else if (m_aImplicitCalendar[0].isEmpty() && rNewCalendar == GetLocaleData().getDefaultCalendar()->Name)
2226
0
    {
2227
0
        m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2228
0
                ImplicitCalendar::DEFAULT_FROM_OTHER : ImplicitCalendar::DEFAULT);
2229
0
        m_aImplicitCalendar[0] = rNewCalendar;
2230
0
    }
2231
0
    else if (rNewCalendar == m_aImplicitCalendar[1])
2232
0
    {
2233
0
        m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2234
0
                ImplicitCalendar::SECONDARY_FROM_OTHER : ImplicitCalendar::SECONDARY);
2235
0
    }
2236
0
    else if (m_aImplicitCalendar[1].isEmpty() && GetLocaleData().doesSecondaryCalendarUseEC( rNewCalendar))
2237
0
    {
2238
0
        m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2239
0
                ImplicitCalendar::SECONDARY_FROM_OTHER : ImplicitCalendar::SECONDARY);
2240
0
        m_aImplicitCalendar[1] = rNewCalendar;
2241
0
    }
2242
0
    else
2243
0
    {
2244
0
        m_eImplicitCalendar = ImplicitCalendar::OTHER;
2245
0
    }
2246
2247
0
    if (m_eImplicitCalendar != ImplicitCalendar::DEFAULT && m_eImplicitCalendar != ImplicitCalendar::SECONDARY)
2248
0
    {
2249
        // A switch from empty default calendar to named default calendar or
2250
        // vice versa is not a switch.
2251
0
        bool bSameDefault = false;
2252
0
        if (m_sCalendar.isEmpty() || rNewCalendar.isEmpty())
2253
0
        {
2254
            // As both are not equal, only one can be empty here, the other
2255
            // can not.
2256
0
            const OUString& rDefaultCalendar = GetLocaleData().getDefaultCalendar()->Name;
2257
            // So if one is the named default calendar the other is the
2258
            // empty default calendar.
2259
0
            bSameDefault = (rNewCalendar == rDefaultCalendar || m_sCalendar == rDefaultCalendar);
2260
0
        }
2261
0
        if (!bSameDefault)
2262
0
        {
2263
0
            m_aFormatCode.append( "[~" );   // intro for calendar code
2264
0
            if (rNewCalendar.isEmpty())
2265
0
            {
2266
                // Empty calendar name here means switching to default calendar
2267
                // from a different calendar. Needs to be explicitly stated in
2268
                // format code.
2269
0
                m_aFormatCode.append( GetLocaleData().getDefaultCalendar()->Name );
2270
0
            }
2271
0
            else
2272
0
            {
2273
0
                m_aFormatCode.append( rNewCalendar );
2274
0
            }
2275
0
            m_aFormatCode.append( ']' );    // end of calendar code
2276
0
        }
2277
0
    }
2278
0
    m_sCalendar = rNewCalendar;
2279
0
}
2280
2281
bool SvXMLNumFormatContext::IsSystemLanguage() const
2282
312
{
2283
312
    return m_nFormatLang == LANGUAGE_SYSTEM;
2284
312
}
2285
2286
2287
//  SvXMLNumFmtHelper
2288
2289
2290
SvXMLNumFmtHelper::SvXMLNumFmtHelper(
2291
    const uno::Reference<util::XNumberFormatsSupplier>& rSupp )
2292
12.4k
{
2293
12.4k
    SvNumberFormatter* pFormatter = nullptr;
2294
12.4k
    SvNumberFormatsSupplierObj* pObj =
2295
12.4k
                    comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
2296
12.4k
    if (pObj)
2297
12.4k
        pFormatter = pObj->GetNumberFormatter();
2298
2299
12.4k
    m_pData = std::make_unique<SvXMLNumImpData>( pFormatter );
2300
12.4k
}
2301
2302
SvXMLNumFmtHelper::SvXMLNumFmtHelper( SvNumberFormatter* pNumberFormatter )
2303
6.02k
{
2304
6.02k
    m_pData = std::make_unique<SvXMLNumImpData>( pNumberFormatter );
2305
6.02k
}
2306
2307
SvXMLNumFmtHelper::~SvXMLNumFmtHelper()
2308
18.4k
{
2309
    //  remove temporary (volatile) formats from NumberFormatter
2310
18.4k
    m_pData->RemoveVolatileFormats();
2311
18.4k
}
2312
2313
2314
SvXMLStyleContext*  SvXMLNumFmtHelper::CreateChildContext( SvXMLImport& rImport,
2315
                sal_Int32 nElement,
2316
                const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
2317
                SvXMLStylesContext& rStyles )
2318
169k
{
2319
169k
    SvXMLStylesTokens nStyleToken;
2320
169k
    switch (nElement)
2321
169k
    {
2322
10.8k
        case XML_ELEMENT(NUMBER, XML_NUMBER_STYLE):
2323
10.8k
            nStyleToken = SvXMLStylesTokens::NUMBER_STYLE;
2324
10.8k
            break;
2325
3.67k
        case XML_ELEMENT(NUMBER, XML_CURRENCY_STYLE):
2326
3.67k
            nStyleToken = SvXMLStylesTokens::CURRENCY_STYLE;
2327
3.67k
            break;
2328
19
        case XML_ELEMENT(NUMBER, XML_PERCENTAGE_STYLE):
2329
19
            nStyleToken = SvXMLStylesTokens::PERCENTAGE_STYLE;
2330
19
            break;
2331
1.81k
        case XML_ELEMENT(NUMBER, XML_DATE_STYLE):
2332
1.81k
            nStyleToken = SvXMLStylesTokens::DATE_STYLE;
2333
1.81k
            break;
2334
3.43k
        case XML_ELEMENT(NUMBER, XML_TIME_STYLE):
2335
3.43k
            nStyleToken = SvXMLStylesTokens::TIME_STYLE;
2336
3.43k
            break;
2337
125
        case XML_ELEMENT(NUMBER, XML_BOOLEAN_STYLE):
2338
125
            nStyleToken = SvXMLStylesTokens::BOOLEAN_STYLE;
2339
125
            break;
2340
2.04k
        case XML_ELEMENT(NUMBER, XML_TEXT_STYLE):
2341
2.04k
            nStyleToken = SvXMLStylesTokens::TEXT_STYLE;
2342
2.04k
            break;
2343
147k
        default:
2344
            // return NULL if not a data style, caller must handle other elements
2345
147k
            return nullptr;
2346
169k
    }
2347
22.0k
    return new SvXMLNumFormatContext( rImport, nElement,
2348
22.0k
                                      m_pData.get(), nStyleToken, xAttrList, rStyles );
2349
169k
}
2350
2351
LanguageType SvXMLNumFmtHelper::GetLanguageForKey(sal_Int32 nKey) const
2352
8.47k
{
2353
8.47k
    if (m_pData->GetNumberFormatter())
2354
8.47k
    {
2355
8.47k
        const SvNumberformat* pEntry = m_pData->GetNumberFormatter()->GetEntry(nKey);
2356
8.47k
        if (pEntry)
2357
8.47k
            return pEntry->GetLanguage();
2358
8.47k
    }
2359
2360
0
    return LANGUAGE_SYSTEM;
2361
8.47k
}
2362
2363
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */