Version 24.2.2.2, tag libreoffice-24.2.2.2
[LibreOffice.git] / xmloff / source / style / xmlnumfi.cxx
blobf6d05e94c1bf113f5fbfd6c126560c2c00da4bac
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <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>
31 #include <sax/tools/converter.hxx>
33 #include <utility>
34 #include <xmloff/xmlement.hxx>
35 #include <xmloff/xmlnumfi.hxx>
36 #include <xmloff/xmltkmap.hxx>
37 #include <xmloff/xmlnamespace.hxx>
38 #include <xmloff/xmlictxt.hxx>
39 #include <xmloff/xmlimp.hxx>
40 #include <xmloff/xmluconv.hxx>
41 #include <xmloff/namespacemap.hxx>
42 #include <xmloff/families.hxx>
43 #include <xmloff/xmltoken.hxx>
44 #include <xmloff/languagetagodf.hxx>
46 #include <memory>
47 #include <string_view>
48 #include <vector>
50 using namespace ::com::sun::star;
51 using namespace ::xmloff::token;
53 namespace {
55 struct SvXMLNumFmtEntry
57 OUString aName;
58 sal_uInt32 nKey;
59 bool bRemoveAfterUse;
61 SvXMLNumFmtEntry( OUString aN, sal_uInt32 nK, bool bR ) :
62 aName(std::move(aN)), nKey(nK), bRemoveAfterUse(bR) {}
67 class SvXMLNumImpData
69 SvNumberFormatter* pFormatter;
70 std::unique_ptr<LocaleDataWrapper> pLocaleData;
71 std::vector<SvXMLNumFmtEntry> m_NameEntries;
73 uno::Reference< uno::XComponentContext > m_xContext;
75 public:
76 SvXMLNumImpData(
77 SvNumberFormatter* pFmt,
78 const uno::Reference<uno::XComponentContext>& rxContext );
80 SvNumberFormatter* GetNumberFormatter() const { return pFormatter; }
81 const LocaleDataWrapper& GetLocaleData( LanguageType nLang );
82 sal_uInt32 GetKeyForName( std::u16string_view rName );
83 void AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse );
84 void SetUsed( sal_uInt32 nKey );
85 void RemoveVolatileFormats();
88 struct SvXMLNumberInfo
90 sal_Int32 nDecimals = -1;
91 sal_Int32 nInteger = -1; /// Total min number of digits in integer part ('0' + '?')
92 sal_Int32 nBlankInteger = -1; /// Number of '?' in integer part
93 sal_Int32 nExpDigits = -1; /// Number of '0' and '?' in exponent
94 sal_Int32 nBlankExp = -1; /// Number of '?' in exponent
95 sal_Int32 nExpInterval = -1;
96 sal_Int32 nMinNumerDigits = -1;
97 sal_Int32 nMinDenomDigits = -1;
98 sal_Int32 nMaxNumerDigits = -1;
99 sal_Int32 nMaxDenomDigits = -1;
100 sal_Int32 nFracDenominator = -1;
101 sal_Int32 nMinDecimalDigits = -1;
102 sal_Int32 nZerosNumerDigits = -1;
103 sal_Int32 nZerosDenomDigits = -1;
104 bool bGrouping = false;
105 bool bDecReplace = false;
106 bool bExpSign = true;
107 bool bExponentLowercase = false; /// Exponent is 'e' instead of 'E'
108 bool bDecAlign = false;
109 double fDisplayFactor = 1.0;
110 OUString aIntegerFractionDelimiter;
111 std::map<sal_Int32, OUString> m_EmbeddedElements;
114 namespace {
116 enum class SvXMLStyleTokens;
118 class SvXMLNumFmtElementContext : public SvXMLImportContext
120 SvXMLNumFormatContext& rParent;
121 SvXMLStyleTokens nType;
122 OUStringBuffer aContent;
123 SvXMLNumberInfo aNumInfo;
124 LanguageType nElementLang;
125 bool bLong;
126 bool bTextual;
127 OUString sCalendar;
128 OUString sBlankWidthString;
130 public:
131 SvXMLNumFmtElementContext( SvXMLImport& rImport, sal_Int32 nElement,
132 SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType,
133 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
135 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
136 sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
137 virtual void SAL_CALL characters( const OUString& rChars ) override;
138 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
140 void AddEmbeddedElement( sal_Int32 nFormatPos, std::u16string_view rContent, std::u16string_view rBlankWidthString );
143 class SvXMLNumFmtEmbeddedTextContext : public SvXMLImportContext
145 SvXMLNumFmtElementContext& rParent;
146 OUStringBuffer aContent;
147 sal_Int32 nTextPosition;
148 OUString aBlankWidthString;
150 public:
151 SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport, sal_Int32 nElement,
152 SvXMLNumFmtElementContext& rParentContext,
153 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
155 virtual void SAL_CALL characters( const OUString& rChars ) override;
156 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
159 class SvXMLNumFmtMapContext : public SvXMLImportContext
161 SvXMLNumFormatContext& rParent;
162 OUString sCondition;
163 OUString sName;
165 public:
166 SvXMLNumFmtMapContext( SvXMLImport& rImport, sal_Int32 nElement,
167 SvXMLNumFormatContext& rParentContext,
168 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
170 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
173 class SvXMLNumFmtPropContext : public SvXMLImportContext
175 SvXMLNumFormatContext& rParent;
176 Color m_nColor;
177 bool bColSet;
179 public:
180 SvXMLNumFmtPropContext( SvXMLImport& rImport, sal_Int32 nElement,
181 SvXMLNumFormatContext& rParentContext,
182 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
184 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
187 enum class SvXMLStyleTokens
189 Text,
190 FillCharacter,
191 Number,
192 ScientificNumber,
193 Fraction,
194 CurrencySymbol,
195 Day,
196 Month,
197 Year,
198 Era,
199 DayOfWeek,
200 WeekOfYear,
201 Quarter,
202 Hours,
203 AmPm,
204 Minutes,
205 Seconds,
206 Boolean,
207 TextContent
212 // standard colors
215 #define XML_NUMF_COLORCOUNT 10
217 const Color aNumFmtStdColors[XML_NUMF_COLORCOUNT] =
219 COL_BLACK,
220 COL_LIGHTBLUE,
221 COL_LIGHTGREEN,
222 COL_LIGHTCYAN,
223 COL_LIGHTRED,
224 COL_LIGHTMAGENTA,
225 COL_BROWN,
226 COL_GRAY,
227 COL_YELLOW,
228 COL_WHITE
232 // token maps
235 // maps for SvXMLUnitConverter::convertEnum
237 const SvXMLEnumMapEntry<bool> aStyleValueMap[] =
239 { XML_SHORT, false },
240 { XML_LONG, true },
241 { XML_TOKEN_INVALID, false }
244 const SvXMLEnumMapEntry<bool> aFormatSourceMap[] =
246 { XML_FIXED, false },
247 { XML_LANGUAGE, true },
248 { XML_TOKEN_INVALID, false }
251 namespace {
253 struct SvXMLDefaultDateFormat
255 NfIndexTableOffset eFormat;
256 SvXMLDateElementAttributes eDOW;
257 SvXMLDateElementAttributes eDay;
258 SvXMLDateElementAttributes eMonth;
259 SvXMLDateElementAttributes eYear;
260 SvXMLDateElementAttributes eHours;
261 SvXMLDateElementAttributes eMins;
262 SvXMLDateElementAttributes eSecs;
263 bool bSystem;
268 const SvXMLDefaultDateFormat aDefaultDateFormats[] =
270 // format day-of-week day month year hours minutes seconds format-source
272 { 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 },
273 { 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 },
274 { 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 },
275 { 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 },
276 { 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 },
277 { 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 },
278 { 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 },
279 { 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 },
280 { 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 },
281 { 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 },
282 { 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 },
283 { 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 },
284 { 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 },
285 { 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 },
286 { 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 }
290 // SvXMLNumImpData
293 SvXMLNumImpData::SvXMLNumImpData(
294 SvNumberFormatter* pFmt,
295 const uno::Reference<uno::XComponentContext>& rxContext )
296 : pFormatter(pFmt),
297 m_xContext(rxContext)
299 SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
302 sal_uInt32 SvXMLNumImpData::GetKeyForName( std::u16string_view rName )
304 for (const auto& rObj : m_NameEntries)
306 if (rObj.aName == rName)
307 return rObj.nKey; // found
309 return NUMBERFORMAT_ENTRY_NOT_FOUND;
312 void SvXMLNumImpData::AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse )
314 if ( bRemoveAfterUse )
316 // if there is already an entry for this key without the bRemoveAfterUse flag,
317 // clear the flag for this entry, too
319 for (const auto& rObj : m_NameEntries)
321 if (rObj.nKey == nKey && !rObj.bRemoveAfterUse)
323 bRemoveAfterUse = false; // clear flag for new entry
324 break;
328 else
330 // call SetUsed to clear the bRemoveAfterUse flag for other entries for this key
331 SetUsed( nKey );
334 m_NameEntries.emplace_back(rName, nKey, bRemoveAfterUse);
337 void SvXMLNumImpData::SetUsed( sal_uInt32 nKey )
339 for (auto& rObj : m_NameEntries)
341 if (rObj.nKey == nKey)
343 rObj.bRemoveAfterUse = false; // used -> don't remove
345 // continue searching - there may be several entries for the same key
346 // (with different names), the format must not be deleted if any one of
347 // them is used
352 void SvXMLNumImpData::RemoveVolatileFormats()
354 // remove temporary (volatile) formats from NumberFormatter
355 // called at the end of each import (styles and content), so volatile formats
356 // from styles can't be used in content
358 if ( !pFormatter )
359 return;
361 for (const auto& rObj : m_NameEntries)
363 if (rObj.bRemoveAfterUse )
365 const SvNumberformat* pFormat = pFormatter->GetEntry(rObj.nKey);
366 if (pFormat && (pFormat->GetType() & SvNumFormatType::DEFINED))
367 pFormatter->DeleteEntry(rObj.nKey);
372 const LocaleDataWrapper& SvXMLNumImpData::GetLocaleData( LanguageType nLang )
374 if ( !pLocaleData || pLocaleData->getLanguageTag() != LanguageTag(nLang) )
375 pLocaleData = std::make_unique<LocaleDataWrapper>(
376 pFormatter ? pFormatter->GetComponentContext() : m_xContext,
377 LanguageTag( nLang ) );
378 return *pLocaleData;
382 // SvXMLNumFmtMapContext
385 SvXMLNumFmtMapContext::SvXMLNumFmtMapContext( SvXMLImport& rImport,
386 sal_Int32 /*nElement*/,
387 SvXMLNumFormatContext& rParentContext,
388 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
389 SvXMLImportContext( rImport ),
390 rParent( rParentContext )
392 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
394 OUString sValue = aIter.toString();
395 switch(aIter.getToken())
397 case XML_ELEMENT(STYLE, XML_CONDITION):
398 sCondition = sValue;
399 break;
400 case XML_ELEMENT(STYLE, XML_APPLY_STYLE_NAME):
401 sName = sValue;
402 break;
403 default:
404 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
409 void SvXMLNumFmtMapContext::endFastElement(sal_Int32 )
411 rParent.AddCondition( sCondition, sName );
415 // SvXMLNumFmtPropContext
418 SvXMLNumFmtPropContext::SvXMLNumFmtPropContext( SvXMLImport& rImport,
419 sal_Int32 /*nElement*/,
420 SvXMLNumFormatContext& rParentContext,
421 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
422 SvXMLImportContext( rImport ),
423 rParent( rParentContext ),
424 m_nColor( 0 ),
425 bColSet( false )
427 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
429 switch ( aIter.getToken())
431 case XML_ELEMENT(FO, XML_COLOR):
432 case XML_ELEMENT(FO_COMPAT, XML_COLOR):
433 bColSet = ::sax::Converter::convertColor( m_nColor, aIter.toView() );
434 break;
435 default:
436 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
441 void SvXMLNumFmtPropContext::endFastElement(sal_Int32 )
443 if (bColSet)
444 rParent.AddColor( m_nColor );
448 // SvXMLNumFmtEmbeddedTextContext
451 SvXMLNumFmtEmbeddedTextContext::SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport,
452 sal_Int32 /*nElement*/,
453 SvXMLNumFmtElementContext& rParentContext,
454 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
455 SvXMLImportContext( rImport ),
456 rParent( rParentContext ),
457 nTextPosition( 0 )
459 sal_Int32 nAttrVal;
461 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
463 if ( aIter.getToken() == XML_ELEMENT(NUMBER, XML_POSITION) )
465 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView() ))
466 nTextPosition = nAttrVal;
468 else if ( aIter.getToken() == XML_ELEMENT(LO_EXT, XML_BLANK_WIDTH_CHAR)
469 || aIter.getToken() == XML_ELEMENT(NUMBER, XML_BLANK_WIDTH_CHAR) )
471 aBlankWidthString = aIter.toString();
473 else
474 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
478 void SvXMLNumFmtEmbeddedTextContext::characters( const OUString& rChars )
480 aContent.append( rChars );
483 void SvXMLNumFmtEmbeddedTextContext::endFastElement(sal_Int32 )
485 rParent.AddEmbeddedElement( nTextPosition, aContent.makeStringAndClear(), aBlankWidthString );
488 static bool lcl_ValidChar( sal_Unicode cChar, const SvXMLNumFormatContext& rParent )
490 SvXMLStylesTokens nFormatType = rParent.GetType();
492 // Treat space equal to non-breaking space separator.
493 const sal_Unicode cNBSP = 0x00A0;
494 sal_Unicode cTS;
495 if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
496 nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
497 nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
498 (cChar == (cTS = rParent.GetLocaleData().getNumThousandSep()[0]) ||
499 (cChar == ' ' && cTS == cNBSP)) )
501 // #i22394# Extra occurrences of thousands separator must be quoted, so they
502 // aren't mis-interpreted as display-factor.
503 // This must be limited to the format types that can contain a number element,
504 // because the same character can be a date separator that should not be quoted
505 // in date formats.
507 return false; // force quotes
510 // see ImpSvNumberformatScan::Next_Symbol
512 // All format types except BOOLEAN may contain minus sign or delimiter.
513 if ( cChar == '-' )
514 return nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE;
516 if ( ( cChar == ' ' ||
517 cChar == '/' ||
518 cChar == '.' ||
519 cChar == ',' ||
520 cChar == ':' ||
521 cChar == '\'' ) &&
522 ( nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
523 nFormatType == SvXMLStylesTokens::DATE_STYLE ||
524 nFormatType == SvXMLStylesTokens::TIME_STYLE ) ) // other formats do not require delimiter tdf#97837
525 return true;
527 // percent sign must be used without quotes for percentage styles only
528 if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && cChar == '%' )
529 return true;
531 // don't put quotes around single parentheses (often used for negative numbers)
532 if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
533 nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
534 nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
535 ( cChar == '(' || cChar == ')' ) )
536 return true;
538 return false;
541 static void lcl_EnquoteIfNecessary( OUStringBuffer& rContent, const SvXMLNumFormatContext& rParent )
543 bool bQuote = true;
544 sal_Int32 nLength = rContent.getLength();
545 const SvXMLStylesTokens nFormatType = rParent.GetType();
547 if (nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE &&
548 ((nLength == 1 && lcl_ValidChar( rContent[0], rParent)) ||
549 (nLength == 2 &&
550 ((rContent[0] == ' ' && rContent[1] == '-') ||
551 (rContent[1] == ' ' && lcl_ValidChar( rContent[0], rParent))))))
553 // Don't quote single separator characters like space or percent,
554 // or separator characters followed by space (used in date formats).
555 // Or space followed by minus (used in currency formats) that would
556 // lead to almost duplicated formats with built-in formats just with
557 // the difference of quotes.
558 bQuote = false;
560 else if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && nLength > 1 )
562 // the percent character in percentage styles must be left out of quoting
563 // (one occurrence is enough even if there are several percent characters in the string)
565 sal_Int32 nPos = rContent.indexOf( '%' );
566 if ( nPos >= 0 )
568 if ( nPos + 1 < nLength )
570 if ( nPos + 2 == nLength && lcl_ValidChar( rContent[nPos + 1], rParent ) )
572 // single character that doesn't need quoting
574 else
576 // quote text behind percent character
577 rContent.insert( nPos + 1, '"' );
578 rContent.append( '"' );
581 if ( nPos > 0 )
583 if ( nPos == 1 && lcl_ValidChar( rContent[0], rParent ) )
585 // single character that doesn't need quoting
587 else
589 // quote text before percent character
590 rContent.insert( nPos, '"' );
591 rContent.insert( 0, '"' );
594 bQuote = false;
596 // else: normal quoting (below)
599 if ( !bQuote )
600 return;
602 // #i55469# quotes in the string itself have to be escaped
603 bool bEscape = ( rContent.indexOf( '"' ) >= 0 );
604 if ( bEscape )
606 // A quote is turned into "\"" - a quote to end quoted text, an escaped quote,
607 // and a quote to resume quoting.
608 OUString aInsert( "\"\\\"" );
610 sal_Int32 nPos = 0;
611 while ( nPos < rContent.getLength() )
613 if ( rContent[nPos] == '"' )
615 rContent.insert( nPos, aInsert );
616 nPos += aInsert.getLength();
618 ++nPos;
622 // quote string literals
623 rContent.insert( 0, '"' );
624 rContent.append( '"' );
626 // remove redundant double quotes at start or end
627 if ( !bEscape )
628 return;
630 if ( rContent.getLength() > 2 &&
631 rContent[0] == '"' &&
632 rContent[1] == '"' )
634 rContent.remove(0, 2);
637 sal_Int32 nLen = rContent.getLength();
638 if ( nLen > 2 &&
639 rContent[nLen - 1] == '"' &&
640 rContent[nLen - 2] == '"' )
642 rContent.truncate(nLen - 2);
647 // SvXMLNumFmtElementContext
650 SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( SvXMLImport& rImport,
651 sal_Int32 /*nElement*/,
652 SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType,
653 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
654 SvXMLImportContext( rImport ),
655 rParent( rParentContext ),
656 nType( nNewType ),
657 nElementLang( LANGUAGE_SYSTEM ),
658 bLong( false ),
659 bTextual( false )
661 LanguageTagODF aLanguageTagODF;
662 sal_Int32 nAttrVal;
663 bool bAttrBool(false);
664 bool bVarDecimals = false;
665 bool bIsMaxDenominator = false;
666 double fAttrDouble;
668 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
670 switch (aIter.getToken())
672 case XML_ELEMENT(NUMBER, XML_DECIMAL_PLACES):
673 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
675 // fdo#58539 & gnome#627420: limit number of digits during import
676 aNumInfo.nDecimals = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
678 break;
679 case XML_ELEMENT(LO_EXT, XML_MIN_DECIMAL_PLACES):
680 case XML_ELEMENT(NUMBER, XML_MIN_DECIMAL_PLACES):
681 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
682 aNumInfo.nMinDecimalDigits = nAttrVal;
683 break;
684 case XML_ELEMENT(NUMBER, XML_MIN_INTEGER_DIGITS):
685 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
686 aNumInfo.nInteger = nAttrVal;
687 break;
688 case XML_ELEMENT(LO_EXT, XML_MAX_BLANK_INTEGER_DIGITS):
689 case XML_ELEMENT(NUMBER, XML_MAX_BLANK_INTEGER_DIGITS):
690 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
691 aNumInfo.nBlankInteger = nAttrVal;
692 break;
693 case XML_ELEMENT(NUMBER, XML_GROUPING):
694 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
695 aNumInfo.bGrouping = bAttrBool;
696 break;
697 case XML_ELEMENT(NUMBER, XML_DISPLAY_FACTOR):
698 if (::sax::Converter::convertDouble( fAttrDouble, aIter.toView() ))
699 aNumInfo.fDisplayFactor = fAttrDouble;
700 break;
701 case XML_ELEMENT(NUMBER, XML_DECIMAL_REPLACEMENT):
702 if ( aIter.toView() == " " )
704 aNumInfo.bDecAlign = true; // space replacement for "?"
705 bVarDecimals = true;
707 else
708 if ( aIter.isEmpty() )
709 bVarDecimals = true; // empty replacement string: variable decimals
710 else // all other strings
711 aNumInfo.bDecReplace = true; // decimal replacement with dashes
712 break;
713 case XML_ELEMENT(NUMBER, XML_MIN_EXPONENT_DIGITS):
714 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
715 aNumInfo.nExpDigits = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
716 break;
717 case XML_ELEMENT(NUMBER, XML_BLANK_EXPONENT_DIGITS):
718 case XML_ELEMENT(LO_EXT, XML_BLANK_EXPONENT_DIGITS):
719 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
720 aNumInfo.nBlankExp = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
721 break;
722 case XML_ELEMENT(NUMBER, XML_EXPONENT_INTERVAL):
723 case XML_ELEMENT(LO_EXT, XML_EXPONENT_INTERVAL):
724 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
725 aNumInfo.nExpInterval = nAttrVal;
726 break;
727 case XML_ELEMENT(NUMBER, XML_FORCED_EXPONENT_SIGN):
728 case XML_ELEMENT(LO_EXT, XML_FORCED_EXPONENT_SIGN):
729 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
730 aNumInfo.bExpSign = bAttrBool;
731 break;
732 case XML_ELEMENT(NUMBER, XML_EXPONENT_LOWERCASE):
733 case XML_ELEMENT(LO_EXT, XML_EXPONENT_LOWERCASE):
734 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
735 aNumInfo.bExponentLowercase = bAttrBool;
736 break;
737 case XML_ELEMENT(NUMBER, XML_MIN_NUMERATOR_DIGITS):
738 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
739 aNumInfo.nMinNumerDigits = nAttrVal;
740 break;
741 case XML_ELEMENT(NUMBER, XML_MIN_DENOMINATOR_DIGITS):
742 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
743 aNumInfo.nMinDenomDigits = nAttrVal;
744 break;
745 case XML_ELEMENT(LO_EXT, XML_MAX_NUMERATOR_DIGITS):
746 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // at least one '#'
747 aNumInfo.nMaxNumerDigits = nAttrVal;
748 break;
749 case XML_ELEMENT(NUMBER, XML_DENOMINATOR_VALUE):
750 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // 0 is not valid
752 aNumInfo.nFracDenominator = nAttrVal;
753 bIsMaxDenominator = false;
755 break;
756 case XML_ELEMENT(NUMBER, XML_MAX_DENOMINATOR_VALUE): // part of ODF 1.3
757 case XML_ELEMENT(LO_EXT, XML_MAX_DENOMINATOR_VALUE):
758 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 ) && aNumInfo.nFracDenominator <= 0)
759 { // if denominator value not yet defined
760 aNumInfo.nFracDenominator = nAttrVal;
761 bIsMaxDenominator = true;
763 break;
764 case XML_ELEMENT(LO_EXT, XML_ZEROS_NUMERATOR_DIGITS):
765 case XML_ELEMENT(NUMBER, XML_ZEROS_NUMERATOR_DIGITS):
766 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
767 aNumInfo.nZerosNumerDigits = nAttrVal;
768 break;
769 case XML_ELEMENT(NUMBER, XML_ZEROS_DENOMINATOR_DIGITS):
770 case XML_ELEMENT(LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS):
771 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
772 aNumInfo.nZerosDenomDigits = nAttrVal;
773 break;
774 case XML_ELEMENT(NUMBER, XML_INTEGER_FRACTION_DELIMITER):
775 case XML_ELEMENT(LO_EXT, XML_INTEGER_FRACTION_DELIMITER):
776 aNumInfo.aIntegerFractionDelimiter = aIter.toString();
777 break;
778 case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG):
779 aLanguageTagODF.maRfcLanguageTag = aIter.toString();
780 break;
781 case XML_ELEMENT(NUMBER, XML_LANGUAGE):
782 aLanguageTagODF.maLanguage = aIter.toString();
783 break;
784 case XML_ELEMENT(NUMBER, XML_SCRIPT):
785 aLanguageTagODF.maScript = aIter.toString();
786 break;
787 case XML_ELEMENT(NUMBER, XML_COUNTRY):
788 aLanguageTagODF.maCountry = aIter.toString();
789 break;
790 case XML_ELEMENT(NUMBER, XML_STYLE):
791 SvXMLUnitConverter::convertEnum( bLong, aIter.toView(), aStyleValueMap );
792 break;
793 case XML_ELEMENT(NUMBER, XML_TEXTUAL):
794 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
795 bTextual = bAttrBool;
796 break;
797 case XML_ELEMENT(NUMBER, XML_CALENDAR):
798 sCalendar = aIter.toString();
799 break;
800 case XML_ELEMENT(NUMBER, XML_BLANK_WIDTH_CHAR):
801 case XML_ELEMENT(LO_EXT, XML_BLANK_WIDTH_CHAR):
802 sBlankWidthString = aIter.toString();
803 break;
804 default:
805 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
808 if ( aNumInfo.nBlankInteger > aNumInfo.nInteger )
809 aNumInfo.nInteger = aNumInfo.nBlankInteger;
810 if ( aNumInfo.nMinDecimalDigits == -1)
812 if ( bVarDecimals || aNumInfo.bDecReplace )
813 aNumInfo.nMinDecimalDigits = 0;
814 else
815 aNumInfo.nMinDecimalDigits = aNumInfo.nDecimals;
817 if ( aNumInfo.nExpDigits > 0 && aNumInfo.nBlankExp >= aNumInfo.nExpDigits )
818 aNumInfo.nBlankExp = aNumInfo.nExpDigits - 1; // at least one '0' in exponent
820 if ( aNumInfo.nZerosDenomDigits > 0 )
821 { // nMin = count of '0' and '?'
822 if ( aNumInfo.nMinDenomDigits < aNumInfo.nZerosDenomDigits )
823 aNumInfo.nMinDenomDigits = aNumInfo.nZerosDenomDigits;
825 else
826 aNumInfo.nZerosDenomDigits = 0;
827 if ( aNumInfo.nMinDenomDigits >= 0 )
828 if ( aNumInfo.nMaxDenomDigits < aNumInfo.nMinDenomDigits )
829 aNumInfo.nMaxDenomDigits = ( aNumInfo.nMinDenomDigits ? aNumInfo.nMinDenomDigits : 1 );
830 if ( aNumInfo.nZerosNumerDigits > 0 )
832 if ( aNumInfo.nMinNumerDigits < aNumInfo.nZerosNumerDigits )
833 aNumInfo.nMinNumerDigits = aNumInfo.nZerosNumerDigits;
835 else
836 aNumInfo.nZerosNumerDigits = 0;
837 if ( aNumInfo.nMinNumerDigits >= 0 )
838 if ( aNumInfo.nMaxNumerDigits < aNumInfo.nMinNumerDigits )
839 aNumInfo.nMaxNumerDigits = ( aNumInfo.nMinNumerDigits ? aNumInfo.nMinNumerDigits : 1 );
840 if ( bIsMaxDenominator && aNumInfo.nFracDenominator > 0 )
842 aNumInfo.nMaxDenomDigits = floor( log10( aNumInfo.nFracDenominator ) ) + 1;
843 aNumInfo.nFracDenominator = -1; // Max denominator value only gives number of digits at denominator
845 if ( aNumInfo.nMaxDenomDigits > 0 )
847 if ( aNumInfo.nMinDenomDigits < 0 )
848 aNumInfo.nMinDenomDigits = 0;
849 else if ( aNumInfo.nMinDenomDigits > aNumInfo.nMaxDenomDigits )
850 aNumInfo.nMinDenomDigits = aNumInfo.nMaxDenomDigits;
853 if ( !aLanguageTagODF.isEmpty() )
855 nElementLang = aLanguageTagODF.getLanguageTag().getLanguageType( false);
856 if ( nElementLang == LANGUAGE_DONTKNOW )
857 nElementLang = LANGUAGE_SYSTEM; //! error handling for unknown locales?
860 if ( aNumInfo.aIntegerFractionDelimiter.isEmpty() )
861 aNumInfo.aIntegerFractionDelimiter = " ";
864 css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFmtElementContext::createFastChildContext(
865 sal_Int32 nElement,
866 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
868 // only number:number and number:scientific-number supports number:embedded-text child element
870 if ( ( nType == SvXMLStyleTokens::Number || nType == SvXMLStyleTokens::ScientificNumber ) &&
871 nElement == XML_ELEMENT(NUMBER, XML_EMBEDDED_TEXT) )
873 return new SvXMLNumFmtEmbeddedTextContext( GetImport(), nElement, *this, xAttrList );
875 else
876 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
877 return nullptr;
880 void SvXMLNumFmtElementContext::characters( const OUString& rChars )
882 aContent.append( rChars );
885 namespace {
886 void lcl_InsertBlankWidthChars( std::u16string_view rBlankWidthString, OUStringBuffer& rContent )
888 sal_Int32 nShiftPosition = 1; // rContent starts with a quote
889 const size_t nLenBlank = rBlankWidthString.size();
890 for ( size_t i = 0 ; i < nLenBlank ; i++ )
892 sal_Unicode nChar = rBlankWidthString[ i ];
893 OUString aBlanks;
894 SvNumberformat::InsertBlanks( aBlanks, 0, nChar );
895 sal_Int32 nPositionContent = 0;
896 if ( ++i < nLenBlank )
898 sal_Int32 nNext = rBlankWidthString.find( '_', i );
899 if ( static_cast<sal_Int32>( i ) < nNext )
901 nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i, nNext - i ) );
902 i = nNext;
904 else
905 nPositionContent = o3tl::toInt32( rBlankWidthString.substr( i ) );
907 nPositionContent += nShiftPosition;
908 if ( nPositionContent >= 0 )
910 rContent.remove( nPositionContent, aBlanks.getLength() );
911 if ( nPositionContent >= 1 && rContent[ nPositionContent-1 ] == '\"' )
913 nPositionContent--;
914 rContent.insert( nPositionContent, nChar );
915 rContent.insert( nPositionContent, '_' );
917 else
919 rContent.insert( nPositionContent, '\"' );
920 rContent.insert( nPositionContent, nChar );
921 rContent.insert( nPositionContent, "\"_" );
922 nShiftPosition += 2;
924 // rContent length was modified: remove blanks, add "_x"
925 nShiftPosition += 2 - aBlanks.getLength();
928 // remove empty string at the end of rContent
929 if ( std::u16string_view( rContent ).substr( rContent.getLength() - 2 ) == u"\"\"" )
931 sal_Int32 nLen = rContent.getLength();
932 if ( nLen >= 3 && rContent[ nLen-3 ] != '\\' )
933 rContent.truncate( nLen - 2 );
938 void SvXMLNumFmtElementContext::AddEmbeddedElement( sal_Int32 nFormatPos, std::u16string_view rContentEmbedded, std::u16string_view rBlankWidthString )
940 if ( rContentEmbedded.empty() )
941 return;
942 OUStringBuffer aContentEmbedded( rContentEmbedded );
943 // #107805# always quote embedded strings - even space would otherwise
944 // be recognized as thousands separator in French.
945 aContentEmbedded.insert( 0, '"' );
946 aContentEmbedded.append( '"' );
947 if ( !rBlankWidthString.empty() )
948 lcl_InsertBlankWidthChars( rBlankWidthString, aContentEmbedded );
950 auto iterPair = aNumInfo.m_EmbeddedElements.emplace( nFormatPos, aContentEmbedded.toString() );
951 if (!iterPair.second)
953 // there's already an element at this position - append text to existing element
954 if ( iterPair.first->second.endsWith( "\"" ) && aContentEmbedded[ 0 ] == '"' )
955 { // remove double quote
956 iterPair.first->second = OUString::Concat( iterPair.first->second.subView( 0, iterPair.first->second.getLength() - 1 ) )
957 + aContentEmbedded.subView( 1, aContentEmbedded.getLength() - 1 );
959 else
960 iterPair.first->second += aContentEmbedded;
964 void SvXMLNumFmtElementContext::endFastElement(sal_Int32 )
966 bool bEffLong = bLong;
967 switch (nType)
969 case SvXMLStyleTokens::Text:
970 if ( rParent.HasLongDoW() &&
971 std::u16string_view(aContent) == rParent.GetLocaleData().getLongDateDayOfWeekSep() )
973 // skip separator constant after long day of week
974 // (NF_KEY_NNNN contains the separator)
976 if ( rParent.ReplaceNfKeyword( NF_KEY_NNN, NF_KEY_NNNN ) )
978 aContent.truncate();
981 rParent.SetHasLongDoW( false ); // only once
983 if ( !aContent.isEmpty() )
985 lcl_EnquoteIfNecessary( aContent, rParent );
986 if ( !sBlankWidthString.isEmpty() )
988 lcl_InsertBlankWidthChars( sBlankWidthString, aContent );
989 sBlankWidthString = "";
991 rParent.AddToCode( aContent );
992 aContent.setLength(0);
994 else
996 // Quoted empty text may be significant to separate.
997 aContent.append("\"\"");
998 rParent.AddToCode( aContent );
999 aContent.setLength(0);
1000 rParent.SetHasTrailingEmptyText(true); // *after* AddToCode()
1002 break;
1004 case SvXMLStyleTokens::Number:
1005 rParent.AddNumber( aNumInfo );
1006 break;
1008 case SvXMLStyleTokens::CurrencySymbol:
1009 rParent.AddCurrency( aContent.makeStringAndClear(), nElementLang );
1010 break;
1012 case SvXMLStyleTokens::TextContent:
1013 rParent.AddToCode( '@');
1014 break;
1015 case SvXMLStyleTokens::FillCharacter:
1016 if ( !aContent.isEmpty() )
1018 rParent.AddToCode( '*' );
1019 rParent.AddToCode( aContent[0] );
1021 break;
1022 case SvXMLStyleTokens::Boolean:
1023 rParent.AddNfKeyword( NF_KEY_BOOLEAN );
1024 break;
1026 case SvXMLStyleTokens::Day:
1027 rParent.UpdateCalendar( sCalendar );
1028 //! I18N doesn't provide SYSTEM or extended date information yet
1030 rParent.AddNfKeyword(
1031 sal::static_int_cast< sal_uInt16 >(
1032 bEffLong ? NF_KEY_DD : NF_KEY_D ) );
1033 break;
1034 case SvXMLStyleTokens::Month:
1035 rParent.UpdateCalendar( sCalendar );
1036 //! I18N doesn't provide SYSTEM or extended date information yet
1038 rParent.AddNfKeyword(
1039 sal::static_int_cast< sal_uInt16 >(
1040 bTextual
1041 ? ( bEffLong ? NF_KEY_MMMM : NF_KEY_MMM )
1042 : ( bEffLong ? NF_KEY_MM : NF_KEY_M ) ) );
1043 break;
1044 case SvXMLStyleTokens::Year:
1045 //! I18N doesn't provide SYSTEM or extended date information yet
1047 // Y after G (era) is replaced by E for a secondary calendar.
1048 // Do not replace for default calendar.
1049 // Also replace Y by E if we're switching to the secondary
1050 // calendar of a locale if it is known to implicitly use E.
1051 rParent.UpdateCalendar( sCalendar);
1052 const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState();
1053 if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
1054 || eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER)
1056 rParent.AddNfKeyword(
1057 sal::static_int_cast< sal_uInt16 >(
1058 bEffLong ? NF_KEY_EEC : NF_KEY_EC ) );
1060 else
1062 rParent.AddNfKeyword(
1063 sal::static_int_cast< sal_uInt16 >(
1064 bEffLong ? NF_KEY_YYYY : NF_KEY_YY ) );
1067 break;
1068 case SvXMLStyleTokens::Era:
1069 rParent.UpdateCalendar( sCalendar );
1070 //! I18N doesn't provide SYSTEM or extended date information yet
1071 rParent.AddNfKeyword(
1072 sal::static_int_cast< sal_uInt16 >(
1073 bEffLong ? NF_KEY_GGG : NF_KEY_G ) );
1074 // HasEra flag is set
1075 break;
1076 case SvXMLStyleTokens::DayOfWeek:
1077 //! I18N doesn't provide SYSTEM or extended date information yet
1079 // Implicit secondary calendar uses A keyword, default and
1080 // explicit calendar N keyword.
1081 rParent.UpdateCalendar( sCalendar);
1082 const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState();
1083 if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
1084 || eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER)
1086 rParent.AddNfKeyword(
1087 sal::static_int_cast< sal_uInt16 >(
1088 bEffLong ? NF_KEY_AAAA : NF_KEY_AAA ) );
1090 else
1092 rParent.AddNfKeyword(
1093 sal::static_int_cast< sal_uInt16 >(
1094 bEffLong ? NF_KEY_NNNN : NF_KEY_NN ) );
1097 break;
1098 case SvXMLStyleTokens::WeekOfYear:
1099 rParent.UpdateCalendar( sCalendar );
1100 rParent.AddNfKeyword( NF_KEY_WW );
1101 break;
1102 case SvXMLStyleTokens::Quarter:
1103 rParent.UpdateCalendar( sCalendar );
1104 rParent.AddNfKeyword(
1105 sal::static_int_cast< sal_uInt16 >(
1106 bEffLong ? NF_KEY_QQ : NF_KEY_Q ) );
1107 break;
1108 case SvXMLStyleTokens::Hours:
1109 rParent.AddNfKeyword(
1110 sal::static_int_cast< sal_uInt16 >(
1111 bEffLong ? NF_KEY_HH : NF_KEY_H ) );
1112 break;
1113 case SvXMLStyleTokens::AmPm:
1114 //! short/long?
1115 rParent.AddNfKeyword( NF_KEY_AMPM );
1116 break;
1117 case SvXMLStyleTokens::Minutes:
1118 rParent.AddNfKeyword(
1119 sal::static_int_cast< sal_uInt16 >(
1120 bEffLong ? NF_KEY_MMI : NF_KEY_MI ) );
1121 break;
1122 case SvXMLStyleTokens::Seconds:
1123 rParent.AddNfKeyword(
1124 sal::static_int_cast< sal_uInt16 >(
1125 bEffLong ? NF_KEY_SS : NF_KEY_S ) );
1126 if ( aNumInfo.nDecimals > 0 )
1128 // manually add the decimal places
1129 rParent.AddToCode(rParent.GetLocaleData().getNumDecimalSep());
1130 for (sal_Int32 i=0; i<aNumInfo.nDecimals; i++)
1132 rParent.AddToCode( '0');
1135 break;
1137 case SvXMLStyleTokens::Fraction:
1139 if ( aNumInfo.nInteger >= 0 )
1141 // add integer part only if min-integer-digits attribute is there
1142 aNumInfo.nDecimals = 0;
1143 rParent.AddNumber( aNumInfo ); // number without decimals
1144 OUStringBuffer sIntegerFractionDelimiter(aNumInfo.aIntegerFractionDelimiter);
1145 lcl_EnquoteIfNecessary( sIntegerFractionDelimiter, rParent );
1146 rParent.AddToCode( sIntegerFractionDelimiter ); // default is ' '
1149 //! build string and add at once
1151 sal_Int32 i;
1152 for (i=aNumInfo.nMaxNumerDigits; i > 0; i--)
1154 if ( i > aNumInfo.nMinNumerDigits )
1155 rParent.AddToCode( '#' );
1156 else if ( i > aNumInfo.nZerosNumerDigits )
1157 rParent.AddToCode( '?' );
1158 else
1159 rParent.AddToCode( '0' );
1161 rParent.AddToCode( '/' );
1162 if ( aNumInfo.nFracDenominator > 0 )
1164 rParent.AddToCode( OUString::number( aNumInfo.nFracDenominator ) );
1166 else
1168 for (i=aNumInfo.nMaxDenomDigits; i > 0 ; i--)
1170 if ( i > aNumInfo.nMinDenomDigits )
1171 rParent.AddToCode( '#' );
1172 else if ( i > aNumInfo.nZerosDenomDigits )
1173 rParent.AddToCode( '?' );
1174 else
1175 rParent.AddToCode( '0' );
1179 break;
1181 case SvXMLStyleTokens::ScientificNumber:
1183 // exponential interval for engineering notation
1184 if( !aNumInfo.bGrouping && aNumInfo.nExpInterval > aNumInfo.nInteger )
1186 for (sal_Int32 i=aNumInfo.nInteger; i<aNumInfo.nExpInterval; i++)
1188 rParent.AddToCode( '#' );
1191 rParent.AddNumber( aNumInfo ); // number and exponent
1193 break;
1195 default:
1196 assert(false && "invalid element ID");
1200 sal_uInt16 SvXMLNumFmtDefaults::GetDefaultDateFormat( SvXMLDateElementAttributes eDOW,
1201 SvXMLDateElementAttributes eDay, SvXMLDateElementAttributes eMonth,
1202 SvXMLDateElementAttributes eYear, SvXMLDateElementAttributes eHours,
1203 SvXMLDateElementAttributes eMins, SvXMLDateElementAttributes eSecs,
1204 bool bSystem )
1206 for (const auto & rEntry : aDefaultDateFormats)
1208 if ( bSystem == rEntry.bSystem &&
1209 ( eDOW == rEntry.eDOW || ( rEntry.eDOW == XML_DEA_ANY && eDOW != XML_DEA_NONE ) ) &&
1210 ( eDay == rEntry.eDay || ( rEntry.eDay == XML_DEA_ANY && eDay != XML_DEA_NONE ) ) &&
1211 ( eMonth == rEntry.eMonth || ( rEntry.eMonth == XML_DEA_ANY && eMonth != XML_DEA_NONE ) ) &&
1212 ( eYear == rEntry.eYear || ( rEntry.eYear == XML_DEA_ANY && eYear != XML_DEA_NONE ) ) &&
1213 ( eHours == rEntry.eHours || ( rEntry.eHours == XML_DEA_ANY && eHours != XML_DEA_NONE ) ) &&
1214 ( eMins == rEntry.eMins || ( rEntry.eMins == XML_DEA_ANY && eMins != XML_DEA_NONE ) ) &&
1215 ( eSecs == rEntry.eSecs || ( rEntry.eSecs == XML_DEA_ANY && eSecs != XML_DEA_NONE ) ) )
1217 return sal::static_int_cast< sal_uInt16 >(rEntry.eFormat);
1221 return NF_INDEX_TABLE_ENTRIES; // invalid
1225 // SvXMLNumFormatContext
1227 SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport& rImport,
1228 sal_Int32 /*nElement*/,
1229 SvXMLNumImpData* pNewData, SvXMLStylesTokens nNewType,
1230 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
1231 SvXMLStylesContext& rStyles ) :
1232 SvXMLStyleContext( rImport ),
1233 m_pData( pNewData ),
1234 m_pStyles( &rStyles ),
1235 m_nType( nNewType ),
1236 m_nKey(-1),
1237 m_eImplicitCalendar(ImplicitCalendar::DEFAULT),
1238 m_nFormatLang( LANGUAGE_SYSTEM ),
1239 m_bAutoOrder( false ),
1240 m_bFromSystem( false ),
1241 m_bTruncate( true ),
1242 m_bAutoDec( false ),
1243 m_bAutoInt( false ),
1244 m_bHasExtraText( false ),
1245 m_bHasTrailingEmptyText( false ),
1246 m_bHasLongDoW( false ),
1247 m_bHasDateTime( false ),
1248 m_bRemoveAfterUse( false ),
1249 m_eDateDOW( XML_DEA_NONE ),
1250 m_eDateDay( XML_DEA_NONE ),
1251 m_eDateMonth( XML_DEA_NONE ),
1252 m_eDateYear( XML_DEA_NONE ),
1253 m_eDateHours( XML_DEA_NONE ),
1254 m_eDateMins( XML_DEA_NONE ),
1255 m_eDateSecs( XML_DEA_NONE ),
1256 m_bDateNoDefault( false )
1258 LanguageTagODF aLanguageTagODF;
1259 css::i18n::NativeNumberXmlAttributes aNatNumAttr;
1260 OUString aSpellout;
1261 bool bAttrBool(false);
1263 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
1265 switch (aIter.getToken())
1267 // attributes for a style
1268 case XML_ELEMENT(STYLE, XML_NAME):
1269 break;
1270 case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG):
1271 aLanguageTagODF.maRfcLanguageTag = aIter.toString();
1272 break;
1273 case XML_ELEMENT(NUMBER, XML_LANGUAGE):
1274 aLanguageTagODF.maLanguage = aIter.toString();
1275 break;
1276 case XML_ELEMENT(NUMBER, XML_SCRIPT):
1277 aLanguageTagODF.maScript = aIter.toString();
1278 break;
1279 case XML_ELEMENT(NUMBER, XML_COUNTRY):
1280 aLanguageTagODF.maCountry = aIter.toString();
1281 break;
1282 case XML_ELEMENT(NUMBER, XML_TITLE):
1283 m_sFormatTitle = aIter.toString();
1284 break;
1285 case XML_ELEMENT(NUMBER, XML_AUTOMATIC_ORDER):
1286 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1287 m_bAutoOrder = bAttrBool;
1288 break;
1289 case XML_ELEMENT(NUMBER, XML_FORMAT_SOURCE):
1290 SvXMLUnitConverter::convertEnum( m_bFromSystem, aIter.toView(), aFormatSourceMap );
1291 break;
1292 case XML_ELEMENT(NUMBER, XML_TRUNCATE_ON_OVERFLOW):
1293 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1294 m_bTruncate = bAttrBool;
1295 break;
1296 case XML_ELEMENT(STYLE, XML_VOLATILE):
1297 // volatile formats can be removed after importing
1298 // if not used in other styles
1299 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1300 m_bRemoveAfterUse = bAttrBool;
1301 break;
1302 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_FORMAT):
1303 aNatNumAttr.Format = aIter.toString();
1304 break;
1305 case XML_ELEMENT(LO_EXT, XML_TRANSLITERATION_SPELLOUT):
1306 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_SPELLOUT):
1307 aSpellout = aIter.toString();
1308 break;
1309 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_LANGUAGE):
1310 aNatNumAttr.Locale.Language = aIter.toString();
1311 break;
1312 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_COUNTRY):
1313 aNatNumAttr.Locale.Country = aIter.toString();
1314 break;
1315 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_STYLE):
1316 aNatNumAttr.Style = aIter.toString();
1317 break;
1318 default:
1319 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
1323 if (!aLanguageTagODF.isEmpty())
1325 m_nFormatLang = aLanguageTagODF.getLanguageTag().getLanguageType( false);
1326 if ( m_nFormatLang == LANGUAGE_DONTKNOW )
1327 m_nFormatLang = LANGUAGE_SYSTEM; //! error handling for unknown locales?
1330 if (aNatNumAttr.Format.isEmpty() && aSpellout.isEmpty())
1331 return;
1333 LanguageTag aLanguageTag( OUString(), aNatNumAttr.Locale.Language,
1334 std::u16string_view(), aNatNumAttr.Locale.Country);
1335 aNatNumAttr.Locale = aLanguageTag.getLocale( false);
1337 // NatNum12 spell out formula (cardinal, ordinal, ordinal-feminine etc.)
1338 if ( !aSpellout.isEmpty() )
1340 m_aFormatCode.append( "[NatNum12 " );
1341 m_aFormatCode.append( aSpellout );
1342 } else {
1343 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1344 if ( !pFormatter ) return;
1346 sal_Int32 nNatNum = pFormatter->GetNatNum()->convertFromXmlAttributes( aNatNumAttr );
1347 m_aFormatCode.append( "[NatNum" );
1348 m_aFormatCode.append( nNatNum );
1351 LanguageType eLang = aLanguageTag.getLanguageType( false );
1352 if ( eLang == LANGUAGE_DONTKNOW )
1353 eLang = LANGUAGE_SYSTEM; //! error handling for unknown locales?
1354 if ( eLang != m_nFormatLang && eLang != LANGUAGE_SYSTEM )
1356 m_aFormatCode.append( "][$-" );
1357 // language code in upper hex:
1358 m_aFormatCode.append(OUString::number(static_cast<sal_uInt16>(eLang), 16).toAsciiUpperCase());
1360 m_aFormatCode.append( ']' );
1363 SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport& rImport,
1364 const OUString& rName,
1365 const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/,
1366 const sal_Int32 nTempKey, LanguageType nLang,
1367 SvXMLStylesContext& rStyles ) :
1368 SvXMLStyleContext( rImport, XmlStyleFamily::DATA_STYLE ),
1369 m_pData( nullptr ),
1370 m_pStyles( &rStyles ),
1371 m_nType( SvXMLStylesTokens::NUMBER_STYLE ),
1372 m_nKey(nTempKey),
1373 m_eImplicitCalendar(ImplicitCalendar::DEFAULT),
1374 m_nFormatLang( nLang ),
1375 m_bAutoOrder( false ),
1376 m_bFromSystem( false ),
1377 m_bTruncate( true ),
1378 m_bAutoDec( false ),
1379 m_bAutoInt( false ),
1380 m_bHasExtraText( false ),
1381 m_bHasTrailingEmptyText( false ),
1382 m_bHasLongDoW( false ),
1383 m_bHasDateTime( false ),
1384 m_bRemoveAfterUse( false ),
1385 m_eDateDOW( XML_DEA_NONE ),
1386 m_eDateDay( XML_DEA_NONE ),
1387 m_eDateMonth( XML_DEA_NONE ),
1388 m_eDateYear( XML_DEA_NONE ),
1389 m_eDateHours( XML_DEA_NONE ),
1390 m_eDateMins( XML_DEA_NONE ),
1391 m_eDateSecs( XML_DEA_NONE ),
1392 m_bDateNoDefault( false )
1394 SetAttribute(XML_ELEMENT(STYLE, XML_NAME), rName);
1397 SvXMLNumFormatContext::~SvXMLNumFormatContext()
1401 css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFormatContext::createFastChildContext(
1402 sal_Int32 nElement,
1403 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
1405 SvXMLImportContext* pContext = nullptr;
1407 switch (nElement)
1409 case XML_ELEMENT(LO_EXT, XML_TEXT):
1410 case XML_ELEMENT(NUMBER, XML_TEXT):
1411 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1412 *this, SvXMLStyleTokens::Text, xAttrList );
1413 break;
1414 case XML_ELEMENT(LO_EXT, XML_FILL_CHARACTER):
1415 case XML_ELEMENT(NUMBER, XML_FILL_CHARACTER):
1416 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1417 *this, SvXMLStyleTokens::FillCharacter, xAttrList );
1418 break;
1419 case XML_ELEMENT(NUMBER, XML_NUMBER):
1420 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1421 *this, SvXMLStyleTokens::Number, xAttrList );
1422 break;
1423 case XML_ELEMENT(NUMBER, XML_SCIENTIFIC_NUMBER):
1424 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1425 *this, SvXMLStyleTokens::ScientificNumber, xAttrList );
1426 break;
1427 case XML_ELEMENT(NUMBER, XML_FRACTION):
1428 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1429 *this, SvXMLStyleTokens::Fraction, xAttrList );
1430 break;
1431 case XML_ELEMENT(NUMBER, XML_CURRENCY_SYMBOL):
1432 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1433 *this, SvXMLStyleTokens::CurrencySymbol, xAttrList );
1434 break;
1435 case XML_ELEMENT(NUMBER, XML_DAY):
1436 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1437 *this, SvXMLStyleTokens::Day, xAttrList );
1438 break;
1439 case XML_ELEMENT(NUMBER, XML_MONTH):
1440 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1441 *this, SvXMLStyleTokens::Month, xAttrList );
1442 break;
1443 case XML_ELEMENT(NUMBER, XML_YEAR):
1444 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1445 *this, SvXMLStyleTokens::Year, xAttrList );
1446 break;
1447 case XML_ELEMENT(NUMBER, XML_ERA):
1448 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1449 *this, SvXMLStyleTokens::Era, xAttrList );
1450 break;
1451 case XML_ELEMENT(NUMBER, XML_DAY_OF_WEEK):
1452 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1453 *this, SvXMLStyleTokens::DayOfWeek, xAttrList );
1454 break;
1455 case XML_ELEMENT(NUMBER, XML_WEEK_OF_YEAR):
1456 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1457 *this, SvXMLStyleTokens::WeekOfYear, xAttrList );
1458 break;
1459 case XML_ELEMENT(NUMBER, XML_QUARTER):
1460 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1461 *this, SvXMLStyleTokens::Quarter, xAttrList );
1462 break;
1463 case XML_ELEMENT(NUMBER, XML_HOURS):
1464 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1465 *this, SvXMLStyleTokens::Hours, xAttrList );
1466 break;
1467 case XML_ELEMENT(NUMBER, XML_AM_PM):
1468 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1469 *this, SvXMLStyleTokens::AmPm, xAttrList );
1470 break;
1471 case XML_ELEMENT(NUMBER, XML_MINUTES):
1472 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1473 *this, SvXMLStyleTokens::Minutes, xAttrList );
1474 break;
1475 case XML_ELEMENT(NUMBER, XML_SECONDS):
1476 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1477 *this, SvXMLStyleTokens::Seconds, xAttrList );
1478 break;
1479 case XML_ELEMENT(NUMBER, XML_BOOLEAN):
1480 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1481 *this, SvXMLStyleTokens::Boolean, xAttrList );
1482 break;
1483 case XML_ELEMENT(NUMBER, XML_TEXT_CONTENT):
1484 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1485 *this, SvXMLStyleTokens::TextContent, xAttrList );
1486 break;
1488 case XML_ELEMENT(STYLE, XML_TEXT_PROPERTIES):
1489 pContext = new SvXMLNumFmtPropContext( GetImport(), nElement,
1490 *this, xAttrList );
1491 break;
1492 case XML_ELEMENT(STYLE, XML_MAP):
1494 // SvXMLNumFmtMapContext::EndElement adds to aMyConditions,
1495 // so there's no need for an extra flag
1496 pContext = new SvXMLNumFmtMapContext( GetImport(), nElement,
1497 *this, xAttrList );
1499 break;
1502 if( !pContext )
1504 SAL_WARN("xmloff.core", "No context for unknown-element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
1505 pContext = new SvXMLImportContext(GetImport());
1508 return pContext;
1511 sal_Int32 SvXMLNumFormatContext::GetKey()
1513 if (m_nKey > -1)
1515 if (m_bRemoveAfterUse)
1517 // format is used -> don't remove
1518 m_bRemoveAfterUse = false;
1519 if (m_pData)
1520 m_pData->SetUsed(m_nKey);
1522 // Add to import's list of keys now - CreateAndInsert didn't add
1523 // the style if bRemoveAfterUse was set.
1524 GetImport().AddNumberStyle( m_nKey, GetName() );
1526 return m_nKey;
1528 else
1530 // reset bRemoveAfterUse before CreateAndInsert, so AddKey is called without bRemoveAfterUse set
1531 m_bRemoveAfterUse = false;
1532 CreateAndInsert(true);
1533 return m_nKey;
1537 sal_Int32 SvXMLNumFormatContext::PrivateGetKey()
1539 // used for map elements in CreateAndInsert - don't reset bRemoveAfterUse flag
1541 if (m_nKey > -1)
1542 return m_nKey;
1543 else
1545 CreateAndInsert(true);
1546 return m_nKey;
1550 sal_Int32 SvXMLNumFormatContext::CreateAndInsert( css::uno::Reference< css::util::XNumberFormatsSupplier > const & xFormatsSupplier )
1552 if (m_nKey <= -1)
1554 SvNumberFormatter* pFormatter = nullptr;
1555 SvNumberFormatsSupplierObj* pObj =
1556 comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xFormatsSupplier );
1557 if (pObj)
1558 pFormatter = pObj->GetNumberFormatter();
1560 if ( pFormatter )
1561 return CreateAndInsert( pFormatter );
1562 else
1563 return -1;
1565 else
1566 return m_nKey;
1569 void SvXMLNumFormatContext::CreateAndInsert(bool /*bOverwrite*/)
1571 if (m_nKey <= -1)
1572 CreateAndInsert(m_pData->GetNumberFormatter());
1575 sal_Int32 SvXMLNumFormatContext::CreateAndInsert(SvNumberFormatter* pFormatter)
1577 if (!pFormatter)
1579 OSL_FAIL("no number formatter");
1580 return -1;
1583 sal_uInt32 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1585 for (size_t i = 0; i < m_aMyConditions.size(); i++)
1587 SvXMLNumFormatContext* pStyle = const_cast<SvXMLNumFormatContext*>( static_cast<const SvXMLNumFormatContext *>(m_pStyles->FindStyleChildContext(
1588 XmlStyleFamily::DATA_STYLE, m_aMyConditions[i].sMapName)));
1589 if (this == pStyle)
1591 SAL_INFO("xmloff.style", "invalid style:map references containing style");
1592 pStyle = nullptr;
1594 if (pStyle)
1596 if (pStyle->PrivateGetKey() > -1) // don't reset pStyle's bRemoveAfterUse flag
1597 AddCondition(i);
1601 sal_Int32 nBufLen;
1602 if ( m_aFormatCode.isEmpty() )
1604 // insert empty format as empty string (with quotes)
1605 // #93901# this check has to be done before inserting the conditions
1606 m_aFormatCode.append("\"\""); // ""
1608 else if (m_bHasTrailingEmptyText && (nBufLen = m_aFormatCode.getLength()) >= 3)
1610 // Remove a trailing empty text. Earlier this may had been written to
1611 // file, like in "General;General" written with elements for
1612 // 'General"";General""' (whyever); when reading, empty text was
1613 // ignored, which it isn't anymore, so get rid of those.
1614 if (m_aFormatCode[nBufLen-1] == '"' && m_aFormatCode[nBufLen-2] == '"')
1615 m_aFormatCode.truncate( nBufLen - 2);
1618 m_aFormatCode.insert( 0, m_aConditions );
1619 m_aConditions.setLength(0);
1620 OUString sFormat = m_aFormatCode.makeStringAndClear();
1622 // test special cases
1624 if ( m_bAutoDec ) // automatic decimal places
1626 // #99391# adjust only if the format contains no text elements, no conditions
1627 // and no color definition (detected by the '[' at the start)
1629 if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText &&
1630 m_aMyConditions.empty() && sFormat.toChar() != '[' )
1631 nIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1633 if ( m_bAutoInt ) // automatic integer digits
1635 //! only if two decimal places was set?
1637 if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText &&
1638 m_aMyConditions.empty() && sFormat.toChar() != '[' )
1639 nIndex = pFormatter->GetFormatIndex( NF_NUMBER_SYSTEM, m_nFormatLang );
1642 if ( m_nType == SvXMLStylesTokens::BOOLEAN_STYLE && !m_bHasExtraText &&
1643 m_aMyConditions.empty() && sFormat.toChar() != '[' )
1644 nIndex = pFormatter->GetFormatIndex( NF_BOOLEAN, m_nFormatLang );
1646 // check for default date formats
1647 if ( m_nType == SvXMLStylesTokens::DATE_STYLE && m_bAutoOrder && !m_bDateNoDefault )
1649 NfIndexTableOffset eFormat = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1650 m_eDateDOW, m_eDateDay, m_eDateMonth, m_eDateYear,
1651 m_eDateHours, m_eDateMins, m_eDateSecs, m_bFromSystem ));
1652 if ( eFormat < NF_INDEX_TABLE_RESERVED_START )
1654 // #109651# if a date format has the automatic-order attribute and
1655 // contains exactly the elements of one of the default date formats,
1656 // use that default format, with the element order and separators
1657 // from the current locale settings
1659 nIndex = pFormatter->GetFormatIndex( eFormat, m_nFormatLang );
1663 if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND && !sFormat.isEmpty() )
1665 // insert by format string
1667 OUString aFormatStr( sFormat );
1668 nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang );
1669 if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
1671 sal_Int32 nErrPos = 0;
1672 SvNumFormatType l_nType = SvNumFormatType::ALL;
1673 bool bOk = pFormatter->PutEntry( aFormatStr, nErrPos, l_nType, nIndex, m_nFormatLang );
1674 if ( !bOk && nErrPos == 0 && aFormatStr != sFormat )
1676 // if the string was modified by PutEntry, look for an existing format
1677 // with the modified string
1678 nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang );
1679 if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND )
1680 bOk = true;
1682 if (!bOk)
1683 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1687 //! I18N doesn't provide SYSTEM or extended date information yet
1688 if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND && !m_bAutoOrder )
1690 // use fixed-order formats instead of SYS... if bAutoOrder is false
1691 // (only if the format strings are equal for the locale)
1693 NfIndexTableOffset eOffset = pFormatter->GetIndexTableOffset( nIndex );
1694 if ( eOffset == NF_DATE_SYS_DMMMYYYY )
1696 sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMYYYY, m_nFormatLang );
1697 const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex );
1698 const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex );
1699 if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() )
1700 nIndex = nNewIndex;
1702 else if ( eOffset == NF_DATE_SYS_DMMMMYYYY )
1704 sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMMYYYY, m_nFormatLang );
1705 const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex );
1706 const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex );
1707 if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() )
1708 nIndex = nNewIndex;
1712 if ((nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND) && !m_sFormatTitle.isEmpty())
1714 SvNumberformat* pFormat = const_cast<SvNumberformat*>(pFormatter->GetEntry( nIndex ));
1715 if (pFormat)
1717 pFormat->SetComment(m_sFormatTitle);
1721 if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
1723 OSL_FAIL("invalid number format");
1724 nIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1727 m_pData->AddKey( nIndex, GetName(), m_bRemoveAfterUse );
1728 m_nKey = nIndex;
1730 // Add to import's list of keys (shared between styles and content import)
1731 // only if not volatile - formats are removed from NumberFormatter at the
1732 // end of each import (in SvXMLNumFmtHelper dtor).
1733 // If bRemoveAfterUse is reset later in GetKey, AddNumberStyle is called there.
1735 if (!m_bRemoveAfterUse)
1736 GetImport().AddNumberStyle( m_nKey, GetName() );
1738 return m_nKey;
1741 const LocaleDataWrapper& SvXMLNumFormatContext::GetLocaleData() const
1743 return m_pData->GetLocaleData( m_nFormatLang );
1746 void SvXMLNumFormatContext::AddToCode( sal_Unicode c )
1748 m_aFormatCode.append( c );
1749 m_bHasExtraText = true;
1752 void SvXMLNumFormatContext::AddToCode( std::u16string_view rString )
1754 m_aFormatCode.append( rString );
1755 m_bHasExtraText = true;
1756 m_bHasTrailingEmptyText = false; // is set by caller again if so
1759 void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo& rInfo )
1761 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1762 if (!pFormatter)
1763 return;
1765 // store special conditions
1766 m_bAutoDec = ( rInfo.nDecimals < 0 );
1767 m_bAutoInt = ( rInfo.nInteger < 0 );
1769 sal_uInt16 nPrec = 0;
1770 sal_uInt16 nLeading = 0;
1771 if ( rInfo.nDecimals >= 0 ) // < 0 : Default
1772 nPrec = static_cast<sal_uInt16>(rInfo.nDecimals);
1773 if ( rInfo.nInteger >= 0 ) // < 0 : Default
1774 nLeading = static_cast<sal_uInt16>(rInfo.nInteger);
1776 if ( m_bAutoDec )
1778 if ( m_nType == SvXMLStylesTokens::CURRENCY_STYLE )
1780 // for currency formats, "automatic decimals" is used for the automatic
1781 // currency format with (fixed) decimals from the locale settings
1783 const LocaleDataWrapper& rLoc = m_pData->GetLocaleData( m_nFormatLang );
1784 nPrec = rLoc.getCurrDigits();
1786 else
1788 // for other types, "automatic decimals" means dynamic determination of
1789 // decimals, as achieved with the "general" keyword
1791 m_aFormatCode.append( pFormatter->GetStandardName( m_nFormatLang ) );
1792 return;
1795 if ( m_bAutoInt )
1797 //!...
1800 sal_uInt16 nGenPrec = nPrec;
1801 if ( rInfo.nMinDecimalDigits >= 0 )
1802 nGenPrec = rInfo.nMinDecimalDigits;
1803 if ( rInfo.bDecReplace )
1804 nGenPrec = 0; // generate format without decimals...
1806 bool bGrouping = rInfo.bGrouping;
1807 size_t const nEmbeddedCount = rInfo.m_EmbeddedElements.size();
1808 if ( nEmbeddedCount && rInfo.m_EmbeddedElements.rbegin()->first > 0 )
1809 bGrouping = false; // grouping and embedded characters in integer part can't be used together
1811 sal_uInt32 nStdIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1812 OUStringBuffer aNumStr(pFormatter->GenerateFormat( nStdIndex, m_nFormatLang,
1813 bGrouping, false, nGenPrec, nLeading ));
1815 if ( rInfo.nExpDigits >= 0 && nLeading == 0 && !bGrouping && nEmbeddedCount == 0 )
1817 // #i43959# For scientific numbers, "#" in the integer part forces a digit,
1818 // so it has to be removed if nLeading is 0 (".00E+0", not "#.00E+0").
1820 aNumStr.stripStart('#');
1823 if ( rInfo.nBlankInteger > 0 )
1825 // Replace nBlankInteger '0' by '?'
1826 sal_Int32 nIndex = 0;
1827 sal_Int32 nBlanks = rInfo.nBlankInteger;
1828 sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() );
1829 if ( nIntegerEnd < 0 )
1830 nIntegerEnd = aNumStr.getLength();
1831 while ( nIndex < nIntegerEnd && nBlanks > 0 )
1833 if ( aNumStr[nIndex] == '0' )
1835 aNumStr[nIndex] = '?';
1836 nBlanks--;
1838 nIndex++;
1842 if ( bGrouping && rInfo.nExpInterval > rInfo.nInteger )
1844 sal_Int32 nIndex = 0;
1845 sal_Int32 nDigits = rInfo.nInteger;
1846 sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() );
1847 if ( nIntegerEnd < 0 )
1848 nIntegerEnd = aNumStr.getLength();
1849 while ( nIndex >= 0 && nIndex < nIntegerEnd )
1851 if ( ( nIndex = aNumStr.indexOf( '#', nIndex ) ) >= 0 )
1853 nDigits ++;
1854 nIndex ++;
1856 else
1857 nIndex = -1;
1859 while ( rInfo.nExpInterval > nDigits )
1861 nDigits++;
1862 aNumStr.insert( 0, '#' );
1866 if ( ( rInfo.bDecReplace || rInfo.nMinDecimalDigits < rInfo.nDecimals ) && nPrec ) // add decimal replacement (dashes)
1868 // add dashes for explicit decimal replacement, # or ? for variable decimals
1869 sal_Unicode cAdd = rInfo.bDecReplace ? '-' : ( rInfo.bDecAlign ? '?': '#' );
1871 if ( rInfo.nMinDecimalDigits == 0 )
1872 aNumStr.append( m_pData->GetLocaleData( m_nFormatLang ).getNumDecimalSep() );
1873 for ( sal_uInt16 i=rInfo.nMinDecimalDigits; i<nPrec; i++)
1874 aNumStr.append( cAdd );
1877 // Scientific number
1878 sal_Int32 nExpPos = -1;
1879 if ( rInfo.nExpDigits > 0 )
1881 nExpPos = aNumStr.getLength();
1882 aNumStr.append( rInfo.bExponentLowercase ? u"e" : u"E" );
1883 // exponent sign is required with embedded text in exponent
1884 if ( rInfo.bExpSign || ( nEmbeddedCount && ( rInfo.nDecimals + 1 < -rInfo.m_EmbeddedElements.begin()->first ) ) )
1886 aNumStr.append( u"+" );
1888 for (sal_Int32 i=0; i<rInfo.nExpDigits; i++)
1890 if ( i < rInfo.nBlankExp )
1891 aNumStr.append( '?' );
1892 else
1893 aNumStr.append( '0' );
1897 if ( nEmbeddedCount )
1899 // insert embedded strings into number string
1900 // support integer (position >=0) and decimal (position <0) part
1901 // nZeroPos is the string position where format position 0 is inserted
1903 sal_Int32 nZeroPos = aNumStr.indexOf( m_pData->GetLocaleData( m_nFormatLang ).getNumDecimalSep() );
1904 if ( nZeroPos < 0 )
1906 nZeroPos = aNumStr.getLength();
1909 // m_EmbeddedElements is sorted - last entry has the largest position (leftmost)
1910 sal_Int32 const nLastFormatPos = rInfo.m_EmbeddedElements.rbegin()->first;
1911 if ( nLastFormatPos >= nZeroPos )
1913 // add '#' characters so all embedded texts are really embedded in digits
1914 // (there always has to be a digit before the leftmost embedded text)
1916 sal_Int32 nAddCount = nLastFormatPos + 1 - nZeroPos;
1917 for(sal_Int32 index = 0; index < nAddCount; ++index)
1919 aNumStr.insert(0, '#');
1921 nZeroPos = nZeroPos + nAddCount;
1922 if ( nExpPos > 0 )
1923 nExpPos = nExpPos + nAddCount;
1926 // m_EmbeddedElements is sorted with ascending positions - loop is from right to left
1927 for (auto const& it : rInfo.m_EmbeddedElements)
1929 sal_Int32 const nFormatPos = it.first;
1930 sal_Int32 nInsertPos = nZeroPos - nFormatPos;
1931 if ( nExpPos > 0 && nInsertPos > nExpPos )
1932 nInsertPos ++;
1933 if ( 0 <= nInsertPos && nInsertPos <= aNumStr.getLength() )
1935 aNumStr.insert( nInsertPos, it.second );
1940 m_aFormatCode.append( aNumStr );
1942 // add extra thousands separators for display factor
1944 if (rInfo.fDisplayFactor == 1.0 || rInfo.fDisplayFactor <= 0.0)
1945 return;
1947 // test for 1.0 is just for optimization - nSepCount would be 0
1949 // one separator for each factor of 1000
1950 sal_Int32 nSepCount = static_cast<sal_Int32>(::rtl::math::round( log10(rInfo.fDisplayFactor) / 3.0 ));
1951 if ( nSepCount > 0 )
1953 OUString aSep = m_pData->GetLocaleData( m_nFormatLang ).getNumThousandSep();
1954 for ( sal_Int32 i=0; i<nSepCount; i++ )
1955 m_aFormatCode.append( aSep );
1959 void SvXMLNumFormatContext::AddCurrency( const OUString& rContent, LanguageType nLang )
1961 bool bAutomatic = false;
1962 OUString aSymbol = rContent;
1963 if ( aSymbol.isEmpty())
1965 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1966 if ( pFormatter )
1968 pFormatter->ChangeIntl( m_nFormatLang );
1969 OUString sCurString, sDummy;
1970 pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
1971 aSymbol = sCurString;
1973 bAutomatic = true;
1976 else if ( nLang == LANGUAGE_SYSTEM && aSymbol == "CCC" )
1978 // "CCC" is used for automatic long symbol
1979 bAutomatic = true;
1982 if ( bAutomatic )
1984 // remove unnecessary quotes before automatic symbol (formats like "-(0DM)")
1985 // otherwise the currency symbol isn't recognized (#94048#)
1987 sal_Int32 nLength = m_aFormatCode.getLength();
1988 if ( nLength > 1 && m_aFormatCode[nLength - 1] == '"' )
1990 // find start of quoted string
1991 // When SvXMLNumFmtElementContext::EndElement creates escaped quotes,
1992 // they must be handled here, too.
1994 sal_Int32 nFirst = nLength - 2;
1995 while ( nFirst >= 0 && m_aFormatCode[nFirst] != '"' )
1996 --nFirst;
1997 if ( nFirst >= 0 )
1999 // remove both quotes from aFormatCode
2000 OUString aOld = m_aFormatCode.makeStringAndClear();
2001 if ( nFirst > 0 )
2002 m_aFormatCode.append( aOld.subView( 0, nFirst ) );
2003 if ( nLength > nFirst + 2 )
2004 m_aFormatCode.append( aOld.subView( nFirst + 1, nLength - nFirst - 2 ) );
2009 if (!bAutomatic)
2010 m_aFormatCode.append( "[$" ); // intro for "new" currency symbols
2012 m_aFormatCode.append( aSymbol );
2014 if (!bAutomatic)
2016 if ( nLang != LANGUAGE_SYSTEM )
2018 // '-' sign and language code in hex:
2019 m_aFormatCode.append("-" + OUString(OUString::number(sal_uInt16(nLang), 16)).toAsciiUpperCase());
2022 m_aFormatCode.append( ']' ); // end of "new" currency symbol
2026 void SvXMLNumFormatContext::AddNfKeyword( sal_uInt16 nIndex )
2028 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2029 if (!pFormatter)
2030 return;
2032 if ( nIndex == NF_KEY_NNNN )
2034 nIndex = NF_KEY_NNN;
2035 m_bHasLongDoW = true; // to remove string constant with separator
2038 OUString sKeyword = pFormatter->GetKeyword( m_nFormatLang, nIndex );
2040 if ( nIndex == NF_KEY_H || nIndex == NF_KEY_HH ||
2041 nIndex == NF_KEY_MI || nIndex == NF_KEY_MMI ||
2042 nIndex == NF_KEY_S || nIndex == NF_KEY_SS )
2044 if ( !m_bTruncate && !m_bHasDateTime )
2046 // with truncate-on-overflow = false, add "[]" to first time part
2047 m_aFormatCode.append("[" + sKeyword + "]");
2049 else
2051 m_aFormatCode.append( sKeyword );
2053 m_bHasDateTime = true;
2055 else
2057 m_aFormatCode.append( sKeyword );
2059 // collect the date elements that the format contains, to recognize default date formats
2060 switch ( nIndex )
2062 case NF_KEY_NN: m_eDateDOW = XML_DEA_SHORT; break;
2063 case NF_KEY_NNN:
2064 case NF_KEY_NNNN: m_eDateDOW = XML_DEA_LONG; break;
2065 case NF_KEY_D: m_eDateDay = XML_DEA_SHORT; break;
2066 case NF_KEY_DD: m_eDateDay = XML_DEA_LONG; break;
2067 case NF_KEY_M: m_eDateMonth = XML_DEA_SHORT; break;
2068 case NF_KEY_MM: m_eDateMonth = XML_DEA_LONG; break;
2069 case NF_KEY_MMM: m_eDateMonth = XML_DEA_TEXTSHORT; break;
2070 case NF_KEY_MMMM: m_eDateMonth = XML_DEA_TEXTLONG; break;
2071 case NF_KEY_YY: m_eDateYear = XML_DEA_SHORT; break;
2072 case NF_KEY_YYYY: m_eDateYear = XML_DEA_LONG; break;
2073 case NF_KEY_H: m_eDateHours = XML_DEA_SHORT; break;
2074 case NF_KEY_HH: m_eDateHours = XML_DEA_LONG; break;
2075 case NF_KEY_MI: m_eDateMins = XML_DEA_SHORT; break;
2076 case NF_KEY_MMI: m_eDateMins = XML_DEA_LONG; break;
2077 case NF_KEY_S: m_eDateSecs = XML_DEA_SHORT; break;
2078 case NF_KEY_SS: m_eDateSecs = XML_DEA_LONG; break;
2079 case NF_KEY_AP:
2080 case NF_KEY_AMPM: break; // AM/PM may or may not be in date/time formats -> ignore by itself
2081 default:
2082 m_bDateNoDefault = true; // any other element -> no default format
2086 static bool lcl_IsAtEnd( OUStringBuffer& rBuffer, std::u16string_view rToken )
2088 sal_Int32 nBufLen = rBuffer.getLength();
2089 sal_Int32 nTokLen = rToken.size();
2091 if ( nTokLen > nBufLen )
2092 return false;
2094 sal_Int32 nStartPos = nBufLen - nTokLen;
2095 for ( sal_Int32 nTokPos = 0; nTokPos < nTokLen; nTokPos++ )
2096 if ( rToken[ nTokPos ] != rBuffer[nStartPos + nTokPos] )
2097 return false;
2099 return true;
2102 bool SvXMLNumFormatContext::ReplaceNfKeyword( sal_uInt16 nOld, sal_uInt16 nNew )
2104 // replaces one keyword with another if it is found at the end of the code
2106 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2107 if (!pFormatter)
2108 return false;
2110 OUString sOldStr = pFormatter->GetKeyword( m_nFormatLang, nOld );
2111 if ( lcl_IsAtEnd( m_aFormatCode, sOldStr ) )
2113 // remove old keyword
2114 m_aFormatCode.setLength( m_aFormatCode.getLength() - sOldStr.getLength() );
2116 // add new keyword
2117 OUString sNewStr = pFormatter->GetKeyword( m_nFormatLang, nNew );
2118 m_aFormatCode.append( sNewStr );
2120 return true; // changed
2122 return false; // not found
2125 void SvXMLNumFormatContext::AddCondition( const sal_Int32 nIndex )
2127 OUString rApplyName = m_aMyConditions[nIndex].sMapName;
2128 OUString rCondition = m_aMyConditions[nIndex].sCondition;
2129 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2130 sal_uInt32 l_nKey = m_pData->GetKeyForName( rApplyName );
2132 OUString sRealCond;
2133 if ( !(pFormatter && l_nKey != NUMBERFORMAT_ENTRY_NOT_FOUND &&
2134 rCondition.startsWith("value()", &sRealCond)) )
2135 return;
2137 //! test for valid conditions
2138 //! test for default conditions
2140 bool bDefaultCond = false;
2142 //! collect all conditions first and adjust default to >=0, >0 or <0 depending on count
2143 //! allow blanks in conditions
2144 if ( m_aConditions.isEmpty() && m_aMyConditions.size() == 1 && sRealCond == ">=0" )
2145 bDefaultCond = true;
2147 if ( m_nType == SvXMLStylesTokens::TEXT_STYLE && static_cast<size_t>(nIndex) == m_aMyConditions.size() - 1 )
2149 // The last condition in a number format with a text part can only
2150 // be "all other numbers", the condition string must be empty.
2151 bDefaultCond = true;
2154 if (!bDefaultCond)
2156 // Convert != to <>
2157 sal_Int32 nPos = sRealCond.indexOf( "!=" );
2158 if ( nPos >= 0 )
2160 sRealCond = sRealCond.replaceAt( nPos, 2, u"<>" );
2163 nPos = sRealCond.indexOf( '.' );
2164 if ( nPos >= 0 )
2166 // #i8026# #103991# localize decimal separator
2167 const OUString& rDecSep = GetLocaleData().getNumDecimalSep();
2168 if ( rDecSep.getLength() > 1 || rDecSep[0] != '.' )
2170 sRealCond = sRealCond.replaceAt( nPos, 1, rDecSep );
2173 m_aConditions.append("[" + sRealCond + "]");
2176 const SvNumberformat* pFormat = pFormatter->GetEntry(l_nKey);
2177 if ( pFormat )
2178 m_aConditions.append( pFormat->GetFormatstring() );
2180 m_aConditions.append( ';' );
2183 void SvXMLNumFormatContext::AddCondition( const OUString& rCondition, const OUString& rApplyName )
2185 MyCondition aCondition;
2186 aCondition.sCondition = rCondition;
2187 aCondition.sMapName = rApplyName;
2188 m_aMyConditions.push_back(aCondition);
2191 void SvXMLNumFormatContext::AddColor( Color const nColor )
2193 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2194 if (!pFormatter)
2195 return;
2197 OUStringBuffer aColName;
2198 for ( sal_uInt16 i=0; i<XML_NUMF_COLORCOUNT; i++ )
2199 if (nColor == aNumFmtStdColors[i])
2201 aColName = pFormatter->GetKeyword( m_nFormatLang, sal::static_int_cast< sal_uInt16 >(NF_KEY_FIRSTCOLOR + i) );
2202 break;
2205 if ( !aColName.isEmpty() )
2207 aColName.insert( 0, '[' );
2208 aColName.append( ']' );
2209 m_aFormatCode.insert( 0, aColName );
2213 void SvXMLNumFormatContext::UpdateCalendar( const OUString& rNewCalendar )
2215 if ( rNewCalendar == m_sCalendar )
2216 return;
2218 if (rNewCalendar.isEmpty() || rNewCalendar == m_aImplicitCalendar[0])
2220 m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2221 ImplicitCalendar::DEFAULT_FROM_OTHER : ImplicitCalendar::DEFAULT);
2223 else if (m_aImplicitCalendar[0].isEmpty() && rNewCalendar == GetLocaleData().getDefaultCalendar()->Name)
2225 m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2226 ImplicitCalendar::DEFAULT_FROM_OTHER : ImplicitCalendar::DEFAULT);
2227 m_aImplicitCalendar[0] = rNewCalendar;
2229 else if (rNewCalendar == m_aImplicitCalendar[1])
2231 m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2232 ImplicitCalendar::SECONDARY_FROM_OTHER : ImplicitCalendar::SECONDARY);
2234 else if (m_aImplicitCalendar[1].isEmpty() && GetLocaleData().doesSecondaryCalendarUseEC( rNewCalendar))
2236 m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2237 ImplicitCalendar::SECONDARY_FROM_OTHER : ImplicitCalendar::SECONDARY);
2238 m_aImplicitCalendar[1] = rNewCalendar;
2240 else
2242 m_eImplicitCalendar = ImplicitCalendar::OTHER;
2245 if (m_eImplicitCalendar != ImplicitCalendar::DEFAULT && m_eImplicitCalendar != ImplicitCalendar::SECONDARY)
2247 // A switch from empty default calendar to named default calendar or
2248 // vice versa is not a switch.
2249 bool bSameDefault = false;
2250 if (m_sCalendar.isEmpty() || rNewCalendar.isEmpty())
2252 // As both are not equal, only one can be empty here, the other
2253 // can not.
2254 const OUString& rDefaultCalendar = GetLocaleData().getDefaultCalendar()->Name;
2255 // So if one is the named default calendar the other is the
2256 // empty default calendar.
2257 bSameDefault = (rNewCalendar == rDefaultCalendar || m_sCalendar == rDefaultCalendar);
2259 if (!bSameDefault)
2261 m_aFormatCode.append( "[~" ); // intro for calendar code
2262 if (rNewCalendar.isEmpty())
2264 // Empty calendar name here means switching to default calendar
2265 // from a different calendar. Needs to be explicitly stated in
2266 // format code.
2267 m_aFormatCode.append( GetLocaleData().getDefaultCalendar()->Name );
2269 else
2271 m_aFormatCode.append( rNewCalendar );
2273 m_aFormatCode.append( ']' ); // end of calendar code
2276 m_sCalendar = rNewCalendar;
2279 bool SvXMLNumFormatContext::IsSystemLanguage() const
2281 return m_nFormatLang == LANGUAGE_SYSTEM;
2285 // SvXMLNumFmtHelper
2288 SvXMLNumFmtHelper::SvXMLNumFmtHelper(
2289 const uno::Reference<util::XNumberFormatsSupplier>& rSupp,
2290 const uno::Reference<uno::XComponentContext>& rxContext )
2292 SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
2294 SvNumberFormatter* pFormatter = nullptr;
2295 SvNumberFormatsSupplierObj* pObj =
2296 comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
2297 if (pObj)
2298 pFormatter = pObj->GetNumberFormatter();
2300 m_pData = std::make_unique<SvXMLNumImpData>( pFormatter, rxContext );
2303 SvXMLNumFmtHelper::SvXMLNumFmtHelper(
2304 SvNumberFormatter* pNumberFormatter,
2305 const uno::Reference<uno::XComponentContext>& rxContext )
2307 SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
2309 m_pData = std::make_unique<SvXMLNumImpData>( pNumberFormatter, rxContext );
2312 SvXMLNumFmtHelper::~SvXMLNumFmtHelper()
2314 // remove temporary (volatile) formats from NumberFormatter
2315 m_pData->RemoveVolatileFormats();
2319 SvXMLStyleContext* SvXMLNumFmtHelper::CreateChildContext( SvXMLImport& rImport,
2320 sal_Int32 nElement,
2321 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
2322 SvXMLStylesContext& rStyles )
2324 SvXMLStylesTokens nStyleToken;
2325 switch (nElement)
2327 case XML_ELEMENT(NUMBER, XML_NUMBER_STYLE):
2328 nStyleToken = SvXMLStylesTokens::NUMBER_STYLE;
2329 break;
2330 case XML_ELEMENT(NUMBER, XML_CURRENCY_STYLE):
2331 nStyleToken = SvXMLStylesTokens::CURRENCY_STYLE;
2332 break;
2333 case XML_ELEMENT(NUMBER, XML_PERCENTAGE_STYLE):
2334 nStyleToken = SvXMLStylesTokens::PERCENTAGE_STYLE;
2335 break;
2336 case XML_ELEMENT(NUMBER, XML_DATE_STYLE):
2337 nStyleToken = SvXMLStylesTokens::DATE_STYLE;
2338 break;
2339 case XML_ELEMENT(NUMBER, XML_TIME_STYLE):
2340 nStyleToken = SvXMLStylesTokens::TIME_STYLE;
2341 break;
2342 case XML_ELEMENT(NUMBER, XML_BOOLEAN_STYLE):
2343 nStyleToken = SvXMLStylesTokens::BOOLEAN_STYLE;
2344 break;
2345 case XML_ELEMENT(NUMBER, XML_TEXT_STYLE):
2346 nStyleToken = SvXMLStylesTokens::TEXT_STYLE;
2347 break;
2348 default:
2349 // return NULL if not a data style, caller must handle other elements
2350 return nullptr;
2352 return new SvXMLNumFormatContext( rImport, nElement,
2353 m_pData.get(), nStyleToken, xAttrList, rStyles );
2356 LanguageType SvXMLNumFmtHelper::GetLanguageForKey(sal_Int32 nKey) const
2358 if (m_pData->GetNumberFormatter())
2360 const SvNumberformat* pEntry = m_pData->GetNumberFormatter()->GetEntry(nKey);
2361 if (pEntry)
2362 return pEntry->GetLanguage();
2365 return LANGUAGE_SYSTEM;
2368 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */