Coverage Report

Created: 2026-04-09 11:41

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