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