bump product version to 7.6.3.2-android
[LibreOffice.git] / xmloff / source / style / xmlnumfi.cxx
blob2c6e58944c2504d13ff41db97854c7a2221578dc
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;
94 sal_Int32 nExpInterval = -1;
95 sal_Int32 nMinNumerDigits = -1;
96 sal_Int32 nMinDenomDigits = -1;
97 sal_Int32 nMaxNumerDigits = -1;
98 sal_Int32 nMaxDenomDigits = -1;
99 sal_Int32 nFracDenominator = -1;
100 sal_Int32 nMinDecimalDigits = -1;
101 sal_Int32 nZerosNumerDigits = -1;
102 sal_Int32 nZerosDenomDigits = -1;
103 bool bGrouping = false;
104 bool bDecReplace = false;
105 bool bExpSign = true;
106 bool bDecAlign = false;
107 double fDisplayFactor = 1.0;
108 OUString aIntegerFractionDelimiter;
109 std::map<sal_Int32, OUString> m_EmbeddedElements;
112 namespace {
114 enum class SvXMLStyleTokens;
116 class SvXMLNumFmtElementContext : public SvXMLImportContext
118 SvXMLNumFormatContext& rParent;
119 SvXMLStyleTokens nType;
120 OUStringBuffer aContent;
121 SvXMLNumberInfo aNumInfo;
122 LanguageType nElementLang;
123 bool bLong;
124 bool bTextual;
125 OUString sCalendar;
127 public:
128 SvXMLNumFmtElementContext( SvXMLImport& rImport, sal_Int32 nElement,
129 SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType,
130 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
132 virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
133 sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
134 virtual void SAL_CALL characters( const OUString& rChars ) override;
135 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
137 void AddEmbeddedElement( sal_Int32 nFormatPos, const OUString& rContent );
140 class SvXMLNumFmtEmbeddedTextContext : public SvXMLImportContext
142 SvXMLNumFmtElementContext& rParent;
143 OUStringBuffer aContent;
144 sal_Int32 nTextPosition;
146 public:
147 SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport, sal_Int32 nElement,
148 SvXMLNumFmtElementContext& rParentContext,
149 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
151 virtual void SAL_CALL characters( const OUString& rChars ) override;
152 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
155 class SvXMLNumFmtMapContext : public SvXMLImportContext
157 SvXMLNumFormatContext& rParent;
158 OUString sCondition;
159 OUString sName;
161 public:
162 SvXMLNumFmtMapContext( SvXMLImport& rImport, sal_Int32 nElement,
163 SvXMLNumFormatContext& rParentContext,
164 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
166 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
169 class SvXMLNumFmtPropContext : public SvXMLImportContext
171 SvXMLNumFormatContext& rParent;
172 Color m_nColor;
173 bool bColSet;
175 public:
176 SvXMLNumFmtPropContext( SvXMLImport& rImport, sal_Int32 nElement,
177 SvXMLNumFormatContext& rParentContext,
178 const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
180 virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
183 enum class SvXMLStyleTokens
185 Text,
186 FillCharacter,
187 Number,
188 ScientificNumber,
189 Fraction,
190 CurrencySymbol,
191 Day,
192 Month,
193 Year,
194 Era,
195 DayOfWeek,
196 WeekOfYear,
197 Quarter,
198 Hours,
199 AmPm,
200 Minutes,
201 Seconds,
202 Boolean,
203 TextContent
208 // standard colors
211 #define XML_NUMF_COLORCOUNT 10
213 const Color aNumFmtStdColors[XML_NUMF_COLORCOUNT] =
215 COL_BLACK,
216 COL_LIGHTBLUE,
217 COL_LIGHTGREEN,
218 COL_LIGHTCYAN,
219 COL_LIGHTRED,
220 COL_LIGHTMAGENTA,
221 COL_BROWN,
222 COL_GRAY,
223 COL_YELLOW,
224 COL_WHITE
228 // token maps
231 // maps for SvXMLUnitConverter::convertEnum
233 const SvXMLEnumMapEntry<bool> aStyleValueMap[] =
235 { XML_SHORT, false },
236 { XML_LONG, true },
237 { XML_TOKEN_INVALID, false }
240 const SvXMLEnumMapEntry<bool> aFormatSourceMap[] =
242 { XML_FIXED, false },
243 { XML_LANGUAGE, true },
244 { XML_TOKEN_INVALID, false }
247 namespace {
249 struct SvXMLDefaultDateFormat
251 NfIndexTableOffset eFormat;
252 SvXMLDateElementAttributes eDOW;
253 SvXMLDateElementAttributes eDay;
254 SvXMLDateElementAttributes eMonth;
255 SvXMLDateElementAttributes eYear;
256 SvXMLDateElementAttributes eHours;
257 SvXMLDateElementAttributes eMins;
258 SvXMLDateElementAttributes eSecs;
259 bool bSystem;
264 const SvXMLDefaultDateFormat aDefaultDateFormats[] =
266 // format day-of-week day month year hours minutes seconds format-source
268 { 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 },
269 { 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 },
270 { 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 },
271 { 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 },
272 { 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 },
273 { 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 },
274 { 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 },
275 { 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 },
276 { 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 },
277 { 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 },
278 { 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 },
279 { 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 },
280 { 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 },
281 { 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 },
282 { 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 }
286 // SvXMLNumImpData
289 SvXMLNumImpData::SvXMLNumImpData(
290 SvNumberFormatter* pFmt,
291 const uno::Reference<uno::XComponentContext>& rxContext )
292 : pFormatter(pFmt),
293 m_xContext(rxContext)
295 SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
298 sal_uInt32 SvXMLNumImpData::GetKeyForName( std::u16string_view rName )
300 for (const auto& rObj : m_NameEntries)
302 if (rObj.aName == rName)
303 return rObj.nKey; // found
305 return NUMBERFORMAT_ENTRY_NOT_FOUND;
308 void SvXMLNumImpData::AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse )
310 if ( bRemoveAfterUse )
312 // if there is already an entry for this key without the bRemoveAfterUse flag,
313 // clear the flag for this entry, too
315 for (const auto& rObj : m_NameEntries)
317 if (rObj.nKey == nKey && !rObj.bRemoveAfterUse)
319 bRemoveAfterUse = false; // clear flag for new entry
320 break;
324 else
326 // call SetUsed to clear the bRemoveAfterUse flag for other entries for this key
327 SetUsed( nKey );
330 m_NameEntries.emplace_back(rName, nKey, bRemoveAfterUse);
333 void SvXMLNumImpData::SetUsed( sal_uInt32 nKey )
335 for (auto& rObj : m_NameEntries)
337 if (rObj.nKey == nKey)
339 rObj.bRemoveAfterUse = false; // used -> don't remove
341 // continue searching - there may be several entries for the same key
342 // (with different names), the format must not be deleted if any one of
343 // them is used
348 void SvXMLNumImpData::RemoveVolatileFormats()
350 // remove temporary (volatile) formats from NumberFormatter
351 // called at the end of each import (styles and content), so volatile formats
352 // from styles can't be used in content
354 if ( !pFormatter )
355 return;
357 for (const auto& rObj : m_NameEntries)
359 if (rObj.bRemoveAfterUse )
361 const SvNumberformat* pFormat = pFormatter->GetEntry(rObj.nKey);
362 if (pFormat && (pFormat->GetType() & SvNumFormatType::DEFINED))
363 pFormatter->DeleteEntry(rObj.nKey);
368 const LocaleDataWrapper& SvXMLNumImpData::GetLocaleData( LanguageType nLang )
370 if ( !pLocaleData || pLocaleData->getLanguageTag() != LanguageTag(nLang) )
371 pLocaleData = std::make_unique<LocaleDataWrapper>(
372 pFormatter ? pFormatter->GetComponentContext() : m_xContext,
373 LanguageTag( nLang ) );
374 return *pLocaleData;
378 // SvXMLNumFmtMapContext
381 SvXMLNumFmtMapContext::SvXMLNumFmtMapContext( SvXMLImport& rImport,
382 sal_Int32 /*nElement*/,
383 SvXMLNumFormatContext& rParentContext,
384 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
385 SvXMLImportContext( rImport ),
386 rParent( rParentContext )
388 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
390 OUString sValue = aIter.toString();
391 switch(aIter.getToken())
393 case XML_ELEMENT(STYLE, XML_CONDITION):
394 sCondition = sValue;
395 break;
396 case XML_ELEMENT(STYLE, XML_APPLY_STYLE_NAME):
397 sName = sValue;
398 break;
399 default:
400 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
405 void SvXMLNumFmtMapContext::endFastElement(sal_Int32 )
407 rParent.AddCondition( sCondition, sName );
411 // SvXMLNumFmtPropContext
414 SvXMLNumFmtPropContext::SvXMLNumFmtPropContext( SvXMLImport& rImport,
415 sal_Int32 /*nElement*/,
416 SvXMLNumFormatContext& rParentContext,
417 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
418 SvXMLImportContext( rImport ),
419 rParent( rParentContext ),
420 m_nColor( 0 ),
421 bColSet( false )
423 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
425 switch ( aIter.getToken())
427 case XML_ELEMENT(FO, XML_COLOR):
428 case XML_ELEMENT(FO_COMPAT, XML_COLOR):
429 bColSet = ::sax::Converter::convertColor( m_nColor, aIter.toView() );
430 break;
431 default:
432 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
437 void SvXMLNumFmtPropContext::endFastElement(sal_Int32 )
439 if (bColSet)
440 rParent.AddColor( m_nColor );
444 // SvXMLNumFmtEmbeddedTextContext
447 SvXMLNumFmtEmbeddedTextContext::SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport,
448 sal_Int32 /*nElement*/,
449 SvXMLNumFmtElementContext& rParentContext,
450 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
451 SvXMLImportContext( rImport ),
452 rParent( rParentContext ),
453 nTextPosition( 0 )
455 sal_Int32 nAttrVal;
457 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
459 if ( aIter.getToken() == XML_ELEMENT(NUMBER, XML_POSITION) )
461 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView() ))
462 nTextPosition = nAttrVal;
464 else
465 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
469 void SvXMLNumFmtEmbeddedTextContext::characters( const OUString& rChars )
471 aContent.append( rChars );
474 void SvXMLNumFmtEmbeddedTextContext::endFastElement(sal_Int32 )
476 rParent.AddEmbeddedElement( nTextPosition, aContent.makeStringAndClear() );
479 static bool lcl_ValidChar( sal_Unicode cChar, const SvXMLNumFormatContext& rParent )
481 SvXMLStylesTokens nFormatType = rParent.GetType();
483 // Treat space equal to non-breaking space separator.
484 const sal_Unicode cNBSP = 0x00A0;
485 sal_Unicode cTS;
486 if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
487 nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
488 nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
489 (cChar == (cTS = rParent.GetLocaleData().getNumThousandSep()[0]) ||
490 (cChar == ' ' && cTS == cNBSP)) )
492 // #i22394# Extra occurrences of thousands separator must be quoted, so they
493 // aren't mis-interpreted as display-factor.
494 // This must be limited to the format types that can contain a number element,
495 // because the same character can be a date separator that should not be quoted
496 // in date formats.
498 return false; // force quotes
501 // see ImpSvNumberformatScan::Next_Symbol
503 // All format types except BOOLEAN may contain minus sign or delimiter.
504 if ( cChar == '-' )
505 return nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE;
507 if ( ( cChar == ' ' ||
508 cChar == '/' ||
509 cChar == '.' ||
510 cChar == ',' ||
511 cChar == ':' ||
512 cChar == '\'' ) &&
513 ( nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
514 nFormatType == SvXMLStylesTokens::DATE_STYLE ||
515 nFormatType == SvXMLStylesTokens::TIME_STYLE ) ) // other formats do not require delimiter tdf#97837
516 return true;
518 // percent sign must be used without quotes for percentage styles only
519 if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && cChar == '%' )
520 return true;
522 // don't put quotes around single parentheses (often used for negative numbers)
523 if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
524 nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
525 nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
526 ( cChar == '(' || cChar == ')' ) )
527 return true;
529 return false;
532 static void lcl_EnquoteIfNecessary( OUStringBuffer& rContent, const SvXMLNumFormatContext& rParent )
534 bool bQuote = true;
535 sal_Int32 nLength = rContent.getLength();
536 const SvXMLStylesTokens nFormatType = rParent.GetType();
538 if (nFormatType != SvXMLStylesTokens::BOOLEAN_STYLE &&
539 ((nLength == 1 && lcl_ValidChar( rContent[0], rParent)) ||
540 (nLength == 2 &&
541 ((rContent[0] == ' ' && rContent[1] == '-') ||
542 (rContent[1] == ' ' && lcl_ValidChar( rContent[0], rParent))))))
544 // Don't quote single separator characters like space or percent,
545 // or separator characters followed by space (used in date formats).
546 // Or space followed by minus (used in currency formats) that would
547 // lead to almost duplicated formats with built-in formats just with
548 // the difference of quotes.
549 bQuote = false;
551 else if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && nLength > 1 )
553 // the percent character in percentage styles must be left out of quoting
554 // (one occurrence is enough even if there are several percent characters in the string)
556 sal_Int32 nPos = rContent.indexOf( '%' );
557 if ( nPos >= 0 )
559 if ( nPos + 1 < nLength )
561 if ( nPos + 2 == nLength && lcl_ValidChar( rContent[nPos + 1], rParent ) )
563 // single character that doesn't need quoting
565 else
567 // quote text behind percent character
568 rContent.insert( nPos + 1, '"' );
569 rContent.append( '"' );
572 if ( nPos > 0 )
574 if ( nPos == 1 && lcl_ValidChar( rContent[0], rParent ) )
576 // single character that doesn't need quoting
578 else
580 // quote text before percent character
581 rContent.insert( nPos, '"' );
582 rContent.insert( 0, '"' );
585 bQuote = false;
587 // else: normal quoting (below)
590 if ( !bQuote )
591 return;
593 // #i55469# quotes in the string itself have to be escaped
594 bool bEscape = ( rContent.indexOf( '"' ) >= 0 );
595 if ( bEscape )
597 // A quote is turned into "\"" - a quote to end quoted text, an escaped quote,
598 // and a quote to resume quoting.
599 OUString aInsert( "\"\\\"" );
601 sal_Int32 nPos = 0;
602 while ( nPos < rContent.getLength() )
604 if ( rContent[nPos] == '"' )
606 rContent.insert( nPos, aInsert );
607 nPos += aInsert.getLength();
609 ++nPos;
613 // quote string literals
614 rContent.insert( 0, '"' );
615 rContent.append( '"' );
617 // remove redundant double quotes at start or end
618 if ( !bEscape )
619 return;
621 if ( rContent.getLength() > 2 &&
622 rContent[0] == '"' &&
623 rContent[1] == '"' )
625 rContent.remove(0, 2);
628 sal_Int32 nLen = rContent.getLength();
629 if ( nLen > 2 &&
630 rContent[nLen - 1] == '"' &&
631 rContent[nLen - 2] == '"' )
633 rContent.truncate(nLen - 2);
638 // SvXMLNumFmtElementContext
641 SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( SvXMLImport& rImport,
642 sal_Int32 /*nElement*/,
643 SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType,
644 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
645 SvXMLImportContext( rImport ),
646 rParent( rParentContext ),
647 nType( nNewType ),
648 nElementLang( LANGUAGE_SYSTEM ),
649 bLong( false ),
650 bTextual( false )
652 LanguageTagODF aLanguageTagODF;
653 sal_Int32 nAttrVal;
654 bool bAttrBool(false);
655 bool bVarDecimals = false;
656 bool bIsMaxDenominator = false;
657 double fAttrDouble;
659 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
661 switch (aIter.getToken())
663 case XML_ELEMENT(NUMBER, XML_DECIMAL_PLACES):
664 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
666 // fdo#58539 & gnome#627420: limit number of digits during import
667 aNumInfo.nDecimals = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
669 break;
670 case XML_ELEMENT(LO_EXT, XML_MIN_DECIMAL_PLACES):
671 case XML_ELEMENT(NUMBER, XML_MIN_DECIMAL_PLACES):
672 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
673 aNumInfo.nMinDecimalDigits = nAttrVal;
674 break;
675 case XML_ELEMENT(NUMBER, XML_MIN_INTEGER_DIGITS):
676 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
677 aNumInfo.nInteger = nAttrVal;
678 break;
679 case XML_ELEMENT(LO_EXT, XML_MAX_BLANK_INTEGER_DIGITS):
680 case XML_ELEMENT(NUMBER, XML_MAX_BLANK_INTEGER_DIGITS):
681 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
682 aNumInfo.nBlankInteger = nAttrVal;
683 break;
684 case XML_ELEMENT(NUMBER, XML_GROUPING):
685 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
686 aNumInfo.bGrouping = bAttrBool;
687 break;
688 case XML_ELEMENT(NUMBER, XML_DISPLAY_FACTOR):
689 if (::sax::Converter::convertDouble( fAttrDouble, aIter.toView() ))
690 aNumInfo.fDisplayFactor = fAttrDouble;
691 break;
692 case XML_ELEMENT(NUMBER, XML_DECIMAL_REPLACEMENT):
693 if ( aIter.toView() == " " )
695 aNumInfo.bDecAlign = true; // space replacement for "?"
696 bVarDecimals = true;
698 else
699 if ( aIter.isEmpty() )
700 bVarDecimals = true; // empty replacement string: variable decimals
701 else // all other strings
702 aNumInfo.bDecReplace = true; // decimal replacement with dashes
703 break;
704 case XML_ELEMENT(NUMBER, XML_MIN_EXPONENT_DIGITS):
705 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
706 aNumInfo.nExpDigits = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
707 break;
708 case XML_ELEMENT(NUMBER, XML_EXPONENT_INTERVAL):
709 case XML_ELEMENT(LO_EXT, XML_EXPONENT_INTERVAL):
710 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
711 aNumInfo.nExpInterval = nAttrVal;
712 break;
713 case XML_ELEMENT(NUMBER, XML_FORCED_EXPONENT_SIGN):
714 case XML_ELEMENT(LO_EXT, XML_FORCED_EXPONENT_SIGN):
715 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
716 aNumInfo.bExpSign = bAttrBool;
717 break;
718 case XML_ELEMENT(NUMBER, XML_MIN_NUMERATOR_DIGITS):
719 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
720 aNumInfo.nMinNumerDigits = nAttrVal;
721 break;
722 case XML_ELEMENT(NUMBER, XML_MIN_DENOMINATOR_DIGITS):
723 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
724 aNumInfo.nMinDenomDigits = nAttrVal;
725 break;
726 case XML_ELEMENT(LO_EXT, XML_MAX_NUMERATOR_DIGITS):
727 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // at least one '#'
728 aNumInfo.nMaxNumerDigits = nAttrVal;
729 break;
730 case XML_ELEMENT(NUMBER, XML_DENOMINATOR_VALUE):
731 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // 0 is not valid
733 aNumInfo.nFracDenominator = nAttrVal;
734 bIsMaxDenominator = false;
736 break;
737 case XML_ELEMENT(NUMBER, XML_MAX_DENOMINATOR_VALUE): // part of ODF 1.3
738 case XML_ELEMENT(LO_EXT, XML_MAX_DENOMINATOR_VALUE):
739 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 ) && aNumInfo.nFracDenominator <= 0)
740 { // if denominator value not yet defined
741 aNumInfo.nFracDenominator = nAttrVal;
742 bIsMaxDenominator = true;
744 break;
745 case XML_ELEMENT(LO_EXT, XML_ZEROS_NUMERATOR_DIGITS):
746 case XML_ELEMENT(NUMBER, XML_ZEROS_NUMERATOR_DIGITS):
747 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
748 aNumInfo.nZerosNumerDigits = nAttrVal;
749 break;
750 case XML_ELEMENT(NUMBER, XML_ZEROS_DENOMINATOR_DIGITS):
751 case XML_ELEMENT(LO_EXT, XML_ZEROS_DENOMINATOR_DIGITS):
752 if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
753 aNumInfo.nZerosDenomDigits = nAttrVal;
754 break;
755 case XML_ELEMENT(NUMBER, XML_INTEGER_FRACTION_DELIMITER):
756 case XML_ELEMENT(LO_EXT, XML_INTEGER_FRACTION_DELIMITER):
757 aNumInfo.aIntegerFractionDelimiter = aIter.toString();
758 break;
759 case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG):
760 aLanguageTagODF.maRfcLanguageTag = aIter.toString();
761 break;
762 case XML_ELEMENT(NUMBER, XML_LANGUAGE):
763 aLanguageTagODF.maLanguage = aIter.toString();
764 break;
765 case XML_ELEMENT(NUMBER, XML_SCRIPT):
766 aLanguageTagODF.maScript = aIter.toString();
767 break;
768 case XML_ELEMENT(NUMBER, XML_COUNTRY):
769 aLanguageTagODF.maCountry = aIter.toString();
770 break;
771 case XML_ELEMENT(NUMBER, XML_STYLE):
772 SvXMLUnitConverter::convertEnum( bLong, aIter.toView(), aStyleValueMap );
773 break;
774 case XML_ELEMENT(NUMBER, XML_TEXTUAL):
775 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
776 bTextual = bAttrBool;
777 break;
778 case XML_ELEMENT(NUMBER, XML_CALENDAR):
779 sCalendar = aIter.toString();
780 break;
781 default:
782 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
785 if ( aNumInfo.nBlankInteger > aNumInfo.nInteger )
786 aNumInfo.nInteger = aNumInfo.nBlankInteger;
787 if ( aNumInfo.nMinDecimalDigits == -1)
789 if ( bVarDecimals || aNumInfo.bDecReplace )
790 aNumInfo.nMinDecimalDigits = 0;
791 else
792 aNumInfo.nMinDecimalDigits = aNumInfo.nDecimals;
794 if ( aNumInfo.nZerosDenomDigits > 0 )
795 { // nMin = count of '0' and '?'
796 if ( aNumInfo.nMinDenomDigits < aNumInfo.nZerosDenomDigits )
797 aNumInfo.nMinDenomDigits = aNumInfo.nZerosDenomDigits;
799 else
800 aNumInfo.nZerosDenomDigits = 0;
801 if ( aNumInfo.nMinDenomDigits >= 0 )
802 if ( aNumInfo.nMaxDenomDigits < aNumInfo.nMinDenomDigits )
803 aNumInfo.nMaxDenomDigits = ( aNumInfo.nMinDenomDigits ? aNumInfo.nMinDenomDigits : 1 );
804 if ( aNumInfo.nZerosNumerDigits > 0 )
806 if ( aNumInfo.nMinNumerDigits < aNumInfo.nZerosNumerDigits )
807 aNumInfo.nMinNumerDigits = aNumInfo.nZerosNumerDigits;
809 else
810 aNumInfo.nZerosNumerDigits = 0;
811 if ( aNumInfo.nMinNumerDigits >= 0 )
812 if ( aNumInfo.nMaxNumerDigits < aNumInfo.nMinNumerDigits )
813 aNumInfo.nMaxNumerDigits = ( aNumInfo.nMinNumerDigits ? aNumInfo.nMinNumerDigits : 1 );
814 if ( bIsMaxDenominator && aNumInfo.nFracDenominator > 0 )
816 aNumInfo.nMaxDenomDigits = floor( log10( aNumInfo.nFracDenominator ) ) + 1;
817 aNumInfo.nFracDenominator = -1; // Max denominator value only gives number of digits at denominator
819 if ( aNumInfo.nMaxDenomDigits > 0 )
821 if ( aNumInfo.nMinDenomDigits < 0 )
822 aNumInfo.nMinDenomDigits = 0;
823 else if ( aNumInfo.nMinDenomDigits > aNumInfo.nMaxDenomDigits )
824 aNumInfo.nMinDenomDigits = aNumInfo.nMaxDenomDigits;
827 if ( !aLanguageTagODF.isEmpty() )
829 nElementLang = aLanguageTagODF.getLanguageTag().getLanguageType( false);
830 if ( nElementLang == LANGUAGE_DONTKNOW )
831 nElementLang = LANGUAGE_SYSTEM; //! error handling for unknown locales?
834 if ( aNumInfo.aIntegerFractionDelimiter.isEmpty() )
835 aNumInfo.aIntegerFractionDelimiter = " ";
838 css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFmtElementContext::createFastChildContext(
839 sal_Int32 nElement,
840 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
842 // only number:number supports number:embedded-text child element
844 if ( nType == SvXMLStyleTokens::Number &&
845 nElement == XML_ELEMENT(NUMBER, XML_EMBEDDED_TEXT) )
847 return new SvXMLNumFmtEmbeddedTextContext( GetImport(), nElement, *this, xAttrList );
849 else
850 XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
851 return nullptr;
854 void SvXMLNumFmtElementContext::characters( const OUString& rChars )
856 aContent.append( rChars );
859 void SvXMLNumFmtElementContext::AddEmbeddedElement( sal_Int32 nFormatPos, const OUString& rContent )
861 if (rContent.isEmpty())
862 return;
864 auto iterPair = aNumInfo.m_EmbeddedElements.emplace(nFormatPos, rContent);
865 if (!iterPair.second)
866 // there's already an element at this position - append text to existing element
867 iterPair.first->second += rContent;
870 void SvXMLNumFmtElementContext::endFastElement(sal_Int32 )
872 bool bEffLong = bLong;
873 switch (nType)
875 case SvXMLStyleTokens::Text:
876 if ( rParent.HasLongDoW() &&
877 std::u16string_view(aContent) == rParent.GetLocaleData().getLongDateDayOfWeekSep() )
879 // skip separator constant after long day of week
880 // (NF_KEY_NNNN contains the separator)
882 if ( rParent.ReplaceNfKeyword( NF_KEY_NNN, NF_KEY_NNNN ) )
884 aContent.truncate();
887 rParent.SetHasLongDoW( false ); // only once
889 if ( !aContent.isEmpty() )
891 lcl_EnquoteIfNecessary( aContent, rParent );
892 rParent.AddToCode( aContent );
893 aContent.setLength(0);
895 else
897 // Quoted empty text may be significant to separate.
898 aContent.append("\"\"");
899 rParent.AddToCode( aContent );
900 aContent.setLength(0);
901 rParent.SetHasTrailingEmptyText(true); // *after* AddToCode()
903 break;
905 case SvXMLStyleTokens::Number:
906 rParent.AddNumber( aNumInfo );
907 break;
909 case SvXMLStyleTokens::CurrencySymbol:
910 rParent.AddCurrency( aContent.makeStringAndClear(), nElementLang );
911 break;
913 case SvXMLStyleTokens::TextContent:
914 rParent.AddToCode( '@');
915 break;
916 case SvXMLStyleTokens::FillCharacter:
917 if ( !aContent.isEmpty() )
919 rParent.AddToCode( '*' );
920 rParent.AddToCode( aContent[0] );
922 break;
923 case SvXMLStyleTokens::Boolean:
924 rParent.AddNfKeyword( NF_KEY_BOOLEAN );
925 break;
927 case SvXMLStyleTokens::Day:
928 rParent.UpdateCalendar( sCalendar );
929 //! I18N doesn't provide SYSTEM or extended date information yet
931 rParent.AddNfKeyword(
932 sal::static_int_cast< sal_uInt16 >(
933 bEffLong ? NF_KEY_DD : NF_KEY_D ) );
934 break;
935 case SvXMLStyleTokens::Month:
936 rParent.UpdateCalendar( sCalendar );
937 //! I18N doesn't provide SYSTEM or extended date information yet
939 rParent.AddNfKeyword(
940 sal::static_int_cast< sal_uInt16 >(
941 bTextual
942 ? ( bEffLong ? NF_KEY_MMMM : NF_KEY_MMM )
943 : ( bEffLong ? NF_KEY_MM : NF_KEY_M ) ) );
944 break;
945 case SvXMLStyleTokens::Year:
946 //! I18N doesn't provide SYSTEM or extended date information yet
948 // Y after G (era) is replaced by E for a secondary calendar.
949 // Do not replace for default calendar.
950 // Also replace Y by E if we're switching to the secondary
951 // calendar of a locale if it is known to implicitly use E.
952 rParent.UpdateCalendar( sCalendar);
953 const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState();
954 if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
955 || eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER)
957 rParent.AddNfKeyword(
958 sal::static_int_cast< sal_uInt16 >(
959 bEffLong ? NF_KEY_EEC : NF_KEY_EC ) );
961 else
963 rParent.AddNfKeyword(
964 sal::static_int_cast< sal_uInt16 >(
965 bEffLong ? NF_KEY_YYYY : NF_KEY_YY ) );
968 break;
969 case SvXMLStyleTokens::Era:
970 rParent.UpdateCalendar( sCalendar );
971 //! I18N doesn't provide SYSTEM or extended date information yet
972 rParent.AddNfKeyword(
973 sal::static_int_cast< sal_uInt16 >(
974 bEffLong ? NF_KEY_GGG : NF_KEY_G ) );
975 // HasEra flag is set
976 break;
977 case SvXMLStyleTokens::DayOfWeek:
978 //! I18N doesn't provide SYSTEM or extended date information yet
980 // Implicit secondary calendar uses A keyword, default and
981 // explicit calendar N keyword.
982 rParent.UpdateCalendar( sCalendar);
983 const SvXMLNumFormatContext::ImplicitCalendar eCal = rParent.GetImplicitCalendarState();
984 if (eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY
985 || eCal == SvXMLNumFormatContext::ImplicitCalendar::SECONDARY_FROM_OTHER)
987 rParent.AddNfKeyword(
988 sal::static_int_cast< sal_uInt16 >(
989 bEffLong ? NF_KEY_AAAA : NF_KEY_AAA ) );
991 else
993 rParent.AddNfKeyword(
994 sal::static_int_cast< sal_uInt16 >(
995 bEffLong ? NF_KEY_NNNN : NF_KEY_NN ) );
998 break;
999 case SvXMLStyleTokens::WeekOfYear:
1000 rParent.UpdateCalendar( sCalendar );
1001 rParent.AddNfKeyword( NF_KEY_WW );
1002 break;
1003 case SvXMLStyleTokens::Quarter:
1004 rParent.UpdateCalendar( sCalendar );
1005 rParent.AddNfKeyword(
1006 sal::static_int_cast< sal_uInt16 >(
1007 bEffLong ? NF_KEY_QQ : NF_KEY_Q ) );
1008 break;
1009 case SvXMLStyleTokens::Hours:
1010 rParent.AddNfKeyword(
1011 sal::static_int_cast< sal_uInt16 >(
1012 bEffLong ? NF_KEY_HH : NF_KEY_H ) );
1013 break;
1014 case SvXMLStyleTokens::AmPm:
1015 //! short/long?
1016 rParent.AddNfKeyword( NF_KEY_AMPM );
1017 break;
1018 case SvXMLStyleTokens::Minutes:
1019 rParent.AddNfKeyword(
1020 sal::static_int_cast< sal_uInt16 >(
1021 bEffLong ? NF_KEY_MMI : NF_KEY_MI ) );
1022 break;
1023 case SvXMLStyleTokens::Seconds:
1024 rParent.AddNfKeyword(
1025 sal::static_int_cast< sal_uInt16 >(
1026 bEffLong ? NF_KEY_SS : NF_KEY_S ) );
1027 if ( aNumInfo.nDecimals > 0 )
1029 // manually add the decimal places
1030 rParent.AddToCode(rParent.GetLocaleData().getNumDecimalSep());
1031 for (sal_Int32 i=0; i<aNumInfo.nDecimals; i++)
1033 rParent.AddToCode( '0');
1036 break;
1038 case SvXMLStyleTokens::Fraction:
1040 if ( aNumInfo.nInteger >= 0 )
1042 // add integer part only if min-integer-digits attribute is there
1043 aNumInfo.nDecimals = 0;
1044 rParent.AddNumber( aNumInfo ); // number without decimals
1045 OUStringBuffer sIntegerFractionDelimiter(aNumInfo.aIntegerFractionDelimiter);
1046 lcl_EnquoteIfNecessary( sIntegerFractionDelimiter, rParent );
1047 rParent.AddToCode( sIntegerFractionDelimiter ); // default is ' '
1050 //! build string and add at once
1052 sal_Int32 i;
1053 for (i=aNumInfo.nMaxNumerDigits; i > 0; i--)
1055 if ( i > aNumInfo.nMinNumerDigits )
1056 rParent.AddToCode( '#' );
1057 else if ( i > aNumInfo.nZerosNumerDigits )
1058 rParent.AddToCode( '?' );
1059 else
1060 rParent.AddToCode( '0' );
1062 rParent.AddToCode( '/' );
1063 if ( aNumInfo.nFracDenominator > 0 )
1065 rParent.AddToCode( OUString::number( aNumInfo.nFracDenominator ) );
1067 else
1069 for (i=aNumInfo.nMaxDenomDigits; i > 0 ; i--)
1071 if ( i > aNumInfo.nMinDenomDigits )
1072 rParent.AddToCode( '#' );
1073 else if ( i > aNumInfo.nZerosDenomDigits )
1074 rParent.AddToCode( '?' );
1075 else
1076 rParent.AddToCode( '0' );
1080 break;
1082 case SvXMLStyleTokens::ScientificNumber:
1084 // exponential interval for engineering notation
1085 if( !aNumInfo.bGrouping && aNumInfo.nExpInterval > aNumInfo.nInteger )
1087 for (sal_Int32 i=aNumInfo.nInteger; i<aNumInfo.nExpInterval; i++)
1089 rParent.AddToCode( '#' );
1092 rParent.AddNumber( aNumInfo ); // simple number
1094 if ( aNumInfo.bExpSign )
1095 rParent.AddToCode( u"E+" );
1096 else
1097 rParent.AddToCode( u"E" );
1098 for (sal_Int32 i=0; i<aNumInfo.nExpDigits; i++)
1100 rParent.AddToCode( '0' );
1103 break;
1105 default:
1106 assert(false && "invalid element ID");
1110 sal_uInt16 SvXMLNumFmtDefaults::GetDefaultDateFormat( SvXMLDateElementAttributes eDOW,
1111 SvXMLDateElementAttributes eDay, SvXMLDateElementAttributes eMonth,
1112 SvXMLDateElementAttributes eYear, SvXMLDateElementAttributes eHours,
1113 SvXMLDateElementAttributes eMins, SvXMLDateElementAttributes eSecs,
1114 bool bSystem )
1116 for (const auto & rEntry : aDefaultDateFormats)
1118 if ( bSystem == rEntry.bSystem &&
1119 ( eDOW == rEntry.eDOW || ( rEntry.eDOW == XML_DEA_ANY && eDOW != XML_DEA_NONE ) ) &&
1120 ( eDay == rEntry.eDay || ( rEntry.eDay == XML_DEA_ANY && eDay != XML_DEA_NONE ) ) &&
1121 ( eMonth == rEntry.eMonth || ( rEntry.eMonth == XML_DEA_ANY && eMonth != XML_DEA_NONE ) ) &&
1122 ( eYear == rEntry.eYear || ( rEntry.eYear == XML_DEA_ANY && eYear != XML_DEA_NONE ) ) &&
1123 ( eHours == rEntry.eHours || ( rEntry.eHours == XML_DEA_ANY && eHours != XML_DEA_NONE ) ) &&
1124 ( eMins == rEntry.eMins || ( rEntry.eMins == XML_DEA_ANY && eMins != XML_DEA_NONE ) ) &&
1125 ( eSecs == rEntry.eSecs || ( rEntry.eSecs == XML_DEA_ANY && eSecs != XML_DEA_NONE ) ) )
1127 return sal::static_int_cast< sal_uInt16 >(rEntry.eFormat);
1131 return NF_INDEX_TABLE_ENTRIES; // invalid
1135 // SvXMLNumFormatContext
1137 SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport& rImport,
1138 sal_Int32 /*nElement*/,
1139 SvXMLNumImpData* pNewData, SvXMLStylesTokens nNewType,
1140 const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
1141 SvXMLStylesContext& rStyles ) :
1142 SvXMLStyleContext( rImport ),
1143 m_pData( pNewData ),
1144 m_pStyles( &rStyles ),
1145 m_nType( nNewType ),
1146 m_nKey(-1),
1147 m_eImplicitCalendar(ImplicitCalendar::DEFAULT),
1148 m_nFormatLang( LANGUAGE_SYSTEM ),
1149 m_bAutoOrder( false ),
1150 m_bFromSystem( false ),
1151 m_bTruncate( true ),
1152 m_bAutoDec( false ),
1153 m_bAutoInt( false ),
1154 m_bHasExtraText( false ),
1155 m_bHasTrailingEmptyText( false ),
1156 m_bHasLongDoW( false ),
1157 m_bHasDateTime( false ),
1158 m_bRemoveAfterUse( false ),
1159 m_eDateDOW( XML_DEA_NONE ),
1160 m_eDateDay( XML_DEA_NONE ),
1161 m_eDateMonth( XML_DEA_NONE ),
1162 m_eDateYear( XML_DEA_NONE ),
1163 m_eDateHours( XML_DEA_NONE ),
1164 m_eDateMins( XML_DEA_NONE ),
1165 m_eDateSecs( XML_DEA_NONE ),
1166 m_bDateNoDefault( false )
1168 LanguageTagODF aLanguageTagODF;
1169 css::i18n::NativeNumberXmlAttributes aNatNumAttr;
1170 OUString aSpellout;
1171 bool bAttrBool(false);
1173 for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
1175 switch (aIter.getToken())
1177 // attributes for a style
1178 case XML_ELEMENT(STYLE, XML_NAME):
1179 break;
1180 case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG):
1181 aLanguageTagODF.maRfcLanguageTag = aIter.toString();
1182 break;
1183 case XML_ELEMENT(NUMBER, XML_LANGUAGE):
1184 aLanguageTagODF.maLanguage = aIter.toString();
1185 break;
1186 case XML_ELEMENT(NUMBER, XML_SCRIPT):
1187 aLanguageTagODF.maScript = aIter.toString();
1188 break;
1189 case XML_ELEMENT(NUMBER, XML_COUNTRY):
1190 aLanguageTagODF.maCountry = aIter.toString();
1191 break;
1192 case XML_ELEMENT(NUMBER, XML_TITLE):
1193 m_sFormatTitle = aIter.toString();
1194 break;
1195 case XML_ELEMENT(NUMBER, XML_AUTOMATIC_ORDER):
1196 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1197 m_bAutoOrder = bAttrBool;
1198 break;
1199 case XML_ELEMENT(NUMBER, XML_FORMAT_SOURCE):
1200 SvXMLUnitConverter::convertEnum( m_bFromSystem, aIter.toView(), aFormatSourceMap );
1201 break;
1202 case XML_ELEMENT(NUMBER, XML_TRUNCATE_ON_OVERFLOW):
1203 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1204 m_bTruncate = bAttrBool;
1205 break;
1206 case XML_ELEMENT(STYLE, XML_VOLATILE):
1207 // volatile formats can be removed after importing
1208 // if not used in other styles
1209 if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1210 m_bRemoveAfterUse = bAttrBool;
1211 break;
1212 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_FORMAT):
1213 aNatNumAttr.Format = aIter.toString();
1214 break;
1215 case XML_ELEMENT(LO_EXT, XML_TRANSLITERATION_SPELLOUT):
1216 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_SPELLOUT):
1217 aSpellout = aIter.toString();
1218 break;
1219 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_LANGUAGE):
1220 aNatNumAttr.Locale.Language = aIter.toString();
1221 break;
1222 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_COUNTRY):
1223 aNatNumAttr.Locale.Country = aIter.toString();
1224 break;
1225 case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_STYLE):
1226 aNatNumAttr.Style = aIter.toString();
1227 break;
1228 default:
1229 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
1233 if (!aLanguageTagODF.isEmpty())
1235 m_nFormatLang = aLanguageTagODF.getLanguageTag().getLanguageType( false);
1236 if ( m_nFormatLang == LANGUAGE_DONTKNOW )
1237 m_nFormatLang = LANGUAGE_SYSTEM; //! error handling for unknown locales?
1240 if (aNatNumAttr.Format.isEmpty() && aSpellout.isEmpty())
1241 return;
1243 LanguageTag aLanguageTag( OUString(), aNatNumAttr.Locale.Language,
1244 std::u16string_view(), aNatNumAttr.Locale.Country);
1245 aNatNumAttr.Locale = aLanguageTag.getLocale( false);
1247 // NatNum12 spell out formula (cardinal, ordinal, ordinal-feminine etc.)
1248 if ( !aSpellout.isEmpty() )
1250 m_aFormatCode.append( "[NatNum12 " );
1251 m_aFormatCode.append( aSpellout );
1252 } else {
1253 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1254 if ( !pFormatter ) return;
1256 sal_Int32 nNatNum = pFormatter->GetNatNum()->convertFromXmlAttributes( aNatNumAttr );
1257 m_aFormatCode.append( "[NatNum" );
1258 m_aFormatCode.append( nNatNum );
1261 LanguageType eLang = aLanguageTag.getLanguageType( false );
1262 if ( eLang == LANGUAGE_DONTKNOW )
1263 eLang = LANGUAGE_SYSTEM; //! error handling for unknown locales?
1264 if ( eLang != m_nFormatLang && eLang != LANGUAGE_SYSTEM )
1266 m_aFormatCode.append( "][$-" );
1267 // language code in upper hex:
1268 m_aFormatCode.append(OUString::number(static_cast<sal_uInt16>(eLang), 16).toAsciiUpperCase());
1270 m_aFormatCode.append( ']' );
1273 SvXMLNumFormatContext::SvXMLNumFormatContext( SvXMLImport& rImport,
1274 const OUString& rName,
1275 const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/,
1276 const sal_Int32 nTempKey, LanguageType nLang,
1277 SvXMLStylesContext& rStyles ) :
1278 SvXMLStyleContext( rImport, XmlStyleFamily::DATA_STYLE ),
1279 m_pData( nullptr ),
1280 m_pStyles( &rStyles ),
1281 m_nType( SvXMLStylesTokens::NUMBER_STYLE ),
1282 m_nKey(nTempKey),
1283 m_eImplicitCalendar(ImplicitCalendar::DEFAULT),
1284 m_nFormatLang( nLang ),
1285 m_bAutoOrder( false ),
1286 m_bFromSystem( false ),
1287 m_bTruncate( true ),
1288 m_bAutoDec( false ),
1289 m_bAutoInt( false ),
1290 m_bHasExtraText( false ),
1291 m_bHasTrailingEmptyText( false ),
1292 m_bHasLongDoW( false ),
1293 m_bHasDateTime( false ),
1294 m_bRemoveAfterUse( false ),
1295 m_eDateDOW( XML_DEA_NONE ),
1296 m_eDateDay( XML_DEA_NONE ),
1297 m_eDateMonth( XML_DEA_NONE ),
1298 m_eDateYear( XML_DEA_NONE ),
1299 m_eDateHours( XML_DEA_NONE ),
1300 m_eDateMins( XML_DEA_NONE ),
1301 m_eDateSecs( XML_DEA_NONE ),
1302 m_bDateNoDefault( false )
1304 SetAttribute(XML_ELEMENT(STYLE, XML_NAME), rName);
1307 SvXMLNumFormatContext::~SvXMLNumFormatContext()
1311 css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFormatContext::createFastChildContext(
1312 sal_Int32 nElement,
1313 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
1315 SvXMLImportContext* pContext = nullptr;
1317 switch (nElement)
1319 case XML_ELEMENT(LO_EXT, XML_TEXT):
1320 case XML_ELEMENT(NUMBER, XML_TEXT):
1321 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1322 *this, SvXMLStyleTokens::Text, xAttrList );
1323 break;
1324 case XML_ELEMENT(LO_EXT, XML_FILL_CHARACTER):
1325 case XML_ELEMENT(NUMBER, XML_FILL_CHARACTER):
1326 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1327 *this, SvXMLStyleTokens::FillCharacter, xAttrList );
1328 break;
1329 case XML_ELEMENT(NUMBER, XML_NUMBER):
1330 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1331 *this, SvXMLStyleTokens::Number, xAttrList );
1332 break;
1333 case XML_ELEMENT(NUMBER, XML_SCIENTIFIC_NUMBER):
1334 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1335 *this, SvXMLStyleTokens::ScientificNumber, xAttrList );
1336 break;
1337 case XML_ELEMENT(NUMBER, XML_FRACTION):
1338 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1339 *this, SvXMLStyleTokens::Fraction, xAttrList );
1340 break;
1341 case XML_ELEMENT(NUMBER, XML_CURRENCY_SYMBOL):
1342 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1343 *this, SvXMLStyleTokens::CurrencySymbol, xAttrList );
1344 break;
1345 case XML_ELEMENT(NUMBER, XML_DAY):
1346 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1347 *this, SvXMLStyleTokens::Day, xAttrList );
1348 break;
1349 case XML_ELEMENT(NUMBER, XML_MONTH):
1350 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1351 *this, SvXMLStyleTokens::Month, xAttrList );
1352 break;
1353 case XML_ELEMENT(NUMBER, XML_YEAR):
1354 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1355 *this, SvXMLStyleTokens::Year, xAttrList );
1356 break;
1357 case XML_ELEMENT(NUMBER, XML_ERA):
1358 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1359 *this, SvXMLStyleTokens::Era, xAttrList );
1360 break;
1361 case XML_ELEMENT(NUMBER, XML_DAY_OF_WEEK):
1362 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1363 *this, SvXMLStyleTokens::DayOfWeek, xAttrList );
1364 break;
1365 case XML_ELEMENT(NUMBER, XML_WEEK_OF_YEAR):
1366 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1367 *this, SvXMLStyleTokens::WeekOfYear, xAttrList );
1368 break;
1369 case XML_ELEMENT(NUMBER, XML_QUARTER):
1370 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1371 *this, SvXMLStyleTokens::Quarter, xAttrList );
1372 break;
1373 case XML_ELEMENT(NUMBER, XML_HOURS):
1374 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1375 *this, SvXMLStyleTokens::Hours, xAttrList );
1376 break;
1377 case XML_ELEMENT(NUMBER, XML_AM_PM):
1378 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1379 *this, SvXMLStyleTokens::AmPm, xAttrList );
1380 break;
1381 case XML_ELEMENT(NUMBER, XML_MINUTES):
1382 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1383 *this, SvXMLStyleTokens::Minutes, xAttrList );
1384 break;
1385 case XML_ELEMENT(NUMBER, XML_SECONDS):
1386 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1387 *this, SvXMLStyleTokens::Seconds, xAttrList );
1388 break;
1389 case XML_ELEMENT(NUMBER, XML_BOOLEAN):
1390 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1391 *this, SvXMLStyleTokens::Boolean, xAttrList );
1392 break;
1393 case XML_ELEMENT(NUMBER, XML_TEXT_CONTENT):
1394 pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1395 *this, SvXMLStyleTokens::TextContent, xAttrList );
1396 break;
1398 case XML_ELEMENT(STYLE, XML_TEXT_PROPERTIES):
1399 pContext = new SvXMLNumFmtPropContext( GetImport(), nElement,
1400 *this, xAttrList );
1401 break;
1402 case XML_ELEMENT(STYLE, XML_MAP):
1404 // SvXMLNumFmtMapContext::EndElement adds to aMyConditions,
1405 // so there's no need for an extra flag
1406 pContext = new SvXMLNumFmtMapContext( GetImport(), nElement,
1407 *this, xAttrList );
1409 break;
1412 if( !pContext )
1414 SAL_WARN("xmloff.core", "No context for unknown-element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
1415 pContext = new SvXMLImportContext(GetImport());
1418 return pContext;
1421 sal_Int32 SvXMLNumFormatContext::GetKey()
1423 if (m_nKey > -1)
1425 if (m_bRemoveAfterUse)
1427 // format is used -> don't remove
1428 m_bRemoveAfterUse = false;
1429 if (m_pData)
1430 m_pData->SetUsed(m_nKey);
1432 // Add to import's list of keys now - CreateAndInsert didn't add
1433 // the style if bRemoveAfterUse was set.
1434 GetImport().AddNumberStyle( m_nKey, GetName() );
1436 return m_nKey;
1438 else
1440 // reset bRemoveAfterUse before CreateAndInsert, so AddKey is called without bRemoveAfterUse set
1441 m_bRemoveAfterUse = false;
1442 CreateAndInsert(true);
1443 return m_nKey;
1447 sal_Int32 SvXMLNumFormatContext::PrivateGetKey()
1449 // used for map elements in CreateAndInsert - don't reset bRemoveAfterUse flag
1451 if (m_nKey > -1)
1452 return m_nKey;
1453 else
1455 CreateAndInsert(true);
1456 return m_nKey;
1460 sal_Int32 SvXMLNumFormatContext::CreateAndInsert( css::uno::Reference< css::util::XNumberFormatsSupplier > const & xFormatsSupplier )
1462 if (m_nKey <= -1)
1464 SvNumberFormatter* pFormatter = nullptr;
1465 SvNumberFormatsSupplierObj* pObj =
1466 comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( xFormatsSupplier );
1467 if (pObj)
1468 pFormatter = pObj->GetNumberFormatter();
1470 if ( pFormatter )
1471 return CreateAndInsert( pFormatter );
1472 else
1473 return -1;
1475 else
1476 return m_nKey;
1479 void SvXMLNumFormatContext::CreateAndInsert(bool /*bOverwrite*/)
1481 if (m_nKey <= -1)
1482 CreateAndInsert(m_pData->GetNumberFormatter());
1485 sal_Int32 SvXMLNumFormatContext::CreateAndInsert(SvNumberFormatter* pFormatter)
1487 if (!pFormatter)
1489 OSL_FAIL("no number formatter");
1490 return -1;
1493 sal_uInt32 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1495 for (size_t i = 0; i < m_aMyConditions.size(); i++)
1497 SvXMLNumFormatContext* pStyle = const_cast<SvXMLNumFormatContext*>( static_cast<const SvXMLNumFormatContext *>(m_pStyles->FindStyleChildContext(
1498 XmlStyleFamily::DATA_STYLE, m_aMyConditions[i].sMapName)));
1499 if (this == pStyle)
1501 SAL_INFO("xmloff.style", "invalid style:map references containing style");
1502 pStyle = nullptr;
1504 if (pStyle)
1506 if (pStyle->PrivateGetKey() > -1) // don't reset pStyle's bRemoveAfterUse flag
1507 AddCondition(i);
1511 sal_Int32 nBufLen;
1512 if ( m_aFormatCode.isEmpty() )
1514 // insert empty format as empty string (with quotes)
1515 // #93901# this check has to be done before inserting the conditions
1516 m_aFormatCode.append("\"\""); // ""
1518 else if (m_bHasTrailingEmptyText && (nBufLen = m_aFormatCode.getLength()) >= 3)
1520 // Remove a trailing empty text. Earlier this may had been written to
1521 // file, like in "General;General" written with elements for
1522 // 'General"";General""' (whyever); when reading, empty text was
1523 // ignored, which it isn't anymore, so get rid of those.
1524 if (m_aFormatCode[nBufLen-1] == '"' && m_aFormatCode[nBufLen-2] == '"')
1525 m_aFormatCode.truncate( nBufLen - 2);
1528 m_aFormatCode.insert( 0, m_aConditions );
1529 m_aConditions.setLength(0);
1530 OUString sFormat = m_aFormatCode.makeStringAndClear();
1532 // test special cases
1534 if ( m_bAutoDec ) // automatic decimal places
1536 // #99391# adjust only if the format contains no text elements, no conditions
1537 // and no color definition (detected by the '[' at the start)
1539 if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText &&
1540 m_aMyConditions.empty() && sFormat.toChar() != '[' )
1541 nIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1543 if ( m_bAutoInt ) // automatic integer digits
1545 //! only if two decimal places was set?
1547 if ( m_nType == SvXMLStylesTokens::NUMBER_STYLE && !m_bHasExtraText &&
1548 m_aMyConditions.empty() && sFormat.toChar() != '[' )
1549 nIndex = pFormatter->GetFormatIndex( NF_NUMBER_SYSTEM, m_nFormatLang );
1552 if ( m_nType == SvXMLStylesTokens::BOOLEAN_STYLE && !m_bHasExtraText &&
1553 m_aMyConditions.empty() && sFormat.toChar() != '[' )
1554 nIndex = pFormatter->GetFormatIndex( NF_BOOLEAN, m_nFormatLang );
1556 // check for default date formats
1557 if ( m_nType == SvXMLStylesTokens::DATE_STYLE && m_bAutoOrder && !m_bDateNoDefault )
1559 NfIndexTableOffset eFormat = static_cast<NfIndexTableOffset>(SvXMLNumFmtDefaults::GetDefaultDateFormat(
1560 m_eDateDOW, m_eDateDay, m_eDateMonth, m_eDateYear,
1561 m_eDateHours, m_eDateMins, m_eDateSecs, m_bFromSystem ));
1562 if ( eFormat < NF_INDEX_TABLE_RESERVED_START )
1564 // #109651# if a date format has the automatic-order attribute and
1565 // contains exactly the elements of one of the default date formats,
1566 // use that default format, with the element order and separators
1567 // from the current locale settings
1569 nIndex = pFormatter->GetFormatIndex( eFormat, m_nFormatLang );
1573 if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND && !sFormat.isEmpty() )
1575 // insert by format string
1577 OUString aFormatStr( sFormat );
1578 nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang );
1579 if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
1581 sal_Int32 nErrPos = 0;
1582 SvNumFormatType l_nType = SvNumFormatType::ALL;
1583 bool bOk = pFormatter->PutEntry( aFormatStr, nErrPos, l_nType, nIndex, m_nFormatLang );
1584 if ( !bOk && nErrPos == 0 && aFormatStr != sFormat )
1586 // if the string was modified by PutEntry, look for an existing format
1587 // with the modified string
1588 nIndex = pFormatter->GetEntryKey( aFormatStr, m_nFormatLang );
1589 if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND )
1590 bOk = true;
1592 if (!bOk)
1593 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1597 //! I18N doesn't provide SYSTEM or extended date information yet
1598 if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND && !m_bAutoOrder )
1600 // use fixed-order formats instead of SYS... if bAutoOrder is false
1601 // (only if the format strings are equal for the locale)
1603 NfIndexTableOffset eOffset = pFormatter->GetIndexTableOffset( nIndex );
1604 if ( eOffset == NF_DATE_SYS_DMMMYYYY )
1606 sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMYYYY, m_nFormatLang );
1607 const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex );
1608 const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex );
1609 if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() )
1610 nIndex = nNewIndex;
1612 else if ( eOffset == NF_DATE_SYS_DMMMMYYYY )
1614 sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMMYYYY, m_nFormatLang );
1615 const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex );
1616 const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex );
1617 if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() )
1618 nIndex = nNewIndex;
1622 if ((nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND) && !m_sFormatTitle.isEmpty())
1624 SvNumberformat* pFormat = const_cast<SvNumberformat*>(pFormatter->GetEntry( nIndex ));
1625 if (pFormat)
1627 pFormat->SetComment(m_sFormatTitle);
1631 if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
1633 OSL_FAIL("invalid number format");
1634 nIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1637 m_pData->AddKey( nIndex, GetName(), m_bRemoveAfterUse );
1638 m_nKey = nIndex;
1640 // Add to import's list of keys (shared between styles and content import)
1641 // only if not volatile - formats are removed from NumberFormatter at the
1642 // end of each import (in SvXMLNumFmtHelper dtor).
1643 // If bRemoveAfterUse is reset later in GetKey, AddNumberStyle is called there.
1645 if (!m_bRemoveAfterUse)
1646 GetImport().AddNumberStyle( m_nKey, GetName() );
1648 return m_nKey;
1651 const LocaleDataWrapper& SvXMLNumFormatContext::GetLocaleData() const
1653 return m_pData->GetLocaleData( m_nFormatLang );
1656 void SvXMLNumFormatContext::AddToCode( sal_Unicode c )
1658 m_aFormatCode.append( c );
1659 m_bHasExtraText = true;
1662 void SvXMLNumFormatContext::AddToCode( std::u16string_view rString )
1664 m_aFormatCode.append( rString );
1665 m_bHasExtraText = true;
1666 m_bHasTrailingEmptyText = false; // is set by caller again if so
1669 void SvXMLNumFormatContext::AddNumber( const SvXMLNumberInfo& rInfo )
1671 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1672 if (!pFormatter)
1673 return;
1675 // store special conditions
1676 m_bAutoDec = ( rInfo.nDecimals < 0 );
1677 m_bAutoInt = ( rInfo.nInteger < 0 );
1679 sal_uInt16 nPrec = 0;
1680 sal_uInt16 nLeading = 0;
1681 if ( rInfo.nDecimals >= 0 ) // < 0 : Default
1682 nPrec = static_cast<sal_uInt16>(rInfo.nDecimals);
1683 if ( rInfo.nInteger >= 0 ) // < 0 : Default
1684 nLeading = static_cast<sal_uInt16>(rInfo.nInteger);
1686 if ( m_bAutoDec )
1688 if ( m_nType == SvXMLStylesTokens::CURRENCY_STYLE )
1690 // for currency formats, "automatic decimals" is used for the automatic
1691 // currency format with (fixed) decimals from the locale settings
1693 const LocaleDataWrapper& rLoc = m_pData->GetLocaleData( m_nFormatLang );
1694 nPrec = rLoc.getCurrDigits();
1696 else
1698 // for other types, "automatic decimals" means dynamic determination of
1699 // decimals, as achieved with the "general" keyword
1701 m_aFormatCode.append( pFormatter->GetStandardName( m_nFormatLang ) );
1702 return;
1705 if ( m_bAutoInt )
1707 //!...
1710 sal_uInt16 nGenPrec = nPrec;
1711 if ( rInfo.nMinDecimalDigits >= 0 )
1712 nGenPrec = rInfo.nMinDecimalDigits;
1713 if ( rInfo.bDecReplace )
1714 nGenPrec = 0; // generate format without decimals...
1716 bool bGrouping = rInfo.bGrouping;
1717 size_t const nEmbeddedCount = rInfo.m_EmbeddedElements.size();
1718 if ( nEmbeddedCount && rInfo.m_EmbeddedElements.rbegin()->first > 0 )
1719 bGrouping = false; // grouping and embedded characters in integer part can't be used together
1721 sal_uInt32 nStdIndex = pFormatter->GetStandardIndex( m_nFormatLang );
1722 OUStringBuffer aNumStr(pFormatter->GenerateFormat( nStdIndex, m_nFormatLang,
1723 bGrouping, false, nGenPrec, nLeading ));
1725 if ( rInfo.nExpDigits >= 0 && nLeading == 0 && !bGrouping && nEmbeddedCount == 0 )
1727 // #i43959# For scientific numbers, "#" in the integer part forces a digit,
1728 // so it has to be removed if nLeading is 0 (".00E+0", not "#.00E+0").
1730 aNumStr.stripStart('#');
1733 if ( rInfo.nBlankInteger > 0 )
1735 // Replace nBlankInteger '0' by '?'
1736 sal_Int32 nIndex = 0;
1737 sal_Int32 nBlanks = rInfo.nBlankInteger;
1738 sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() );
1739 if ( nIntegerEnd < 0 )
1740 nIntegerEnd = aNumStr.getLength();
1741 while ( nIndex < nIntegerEnd && nBlanks > 0 )
1743 if ( aNumStr[nIndex] == '0' )
1745 aNumStr[nIndex] = '?';
1746 nBlanks--;
1748 nIndex++;
1752 if ( bGrouping && rInfo.nExpInterval > rInfo.nInteger )
1754 sal_Int32 nIndex = 0;
1755 sal_Int32 nDigits = rInfo.nInteger;
1756 sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() );
1757 if ( nIntegerEnd < 0 )
1758 nIntegerEnd = aNumStr.getLength();
1759 while ( nIndex >= 0 && nIndex < nIntegerEnd )
1761 if ( ( nIndex = aNumStr.indexOf( '#', nIndex ) ) >= 0 )
1763 nDigits ++;
1764 nIndex ++;
1766 else
1767 nIndex = -1;
1769 while ( rInfo.nExpInterval > nDigits )
1771 nDigits++;
1772 aNumStr.insert( 0, '#' );
1776 if ( ( rInfo.bDecReplace || rInfo.nMinDecimalDigits < rInfo.nDecimals ) && nPrec ) // add decimal replacement (dashes)
1778 // add dashes for explicit decimal replacement, # or ? for variable decimals
1779 sal_Unicode cAdd = rInfo.bDecReplace ? '-' : ( rInfo.bDecAlign ? '?': '#' );
1781 if ( rInfo.nMinDecimalDigits == 0 )
1782 aNumStr.append( m_pData->GetLocaleData( m_nFormatLang ).getNumDecimalSep() );
1783 for ( sal_uInt16 i=rInfo.nMinDecimalDigits; i<nPrec; i++)
1784 aNumStr.append( cAdd );
1787 if ( nEmbeddedCount )
1789 // insert embedded strings into number string
1790 // support integer (position >=0) and decimal (position <0) part
1791 // nZeroPos is the string position where format position 0 is inserted
1793 sal_Int32 nZeroPos = aNumStr.indexOf( m_pData->GetLocaleData( m_nFormatLang ).getNumDecimalSep() );
1794 if ( nZeroPos < 0 )
1796 nZeroPos = aNumStr.getLength();
1799 // m_EmbeddedElements is sorted - last entry has the largest position (leftmost)
1800 sal_Int32 const nLastFormatPos = rInfo.m_EmbeddedElements.rbegin()->first;
1801 if ( nLastFormatPos >= nZeroPos )
1803 // add '#' characters so all embedded texts are really embedded in digits
1804 // (there always has to be a digit before the leftmost embedded text)
1806 sal_Int32 nAddCount = nLastFormatPos + 1 - nZeroPos;
1807 for(sal_Int32 index = 0; index < nAddCount; ++index)
1809 aNumStr.insert(0, '#');
1811 nZeroPos = nZeroPos + nAddCount;
1814 // m_EmbeddedElements is sorted with ascending positions - loop is from right to left
1815 for (auto const& it : rInfo.m_EmbeddedElements)
1817 sal_Int32 const nFormatPos = it.first;
1818 sal_Int32 nInsertPos = nZeroPos - nFormatPos;
1819 if ( nInsertPos >= 0 )
1821 // #107805# always quote embedded strings - even space would otherwise
1822 // be recognized as thousands separator in French.
1824 aNumStr.insert(nInsertPos, OUString::Concat("\"") + it.second + "\"");
1829 m_aFormatCode.append( aNumStr );
1831 // add extra thousands separators for display factor
1833 if (rInfo.fDisplayFactor == 1.0 || rInfo.fDisplayFactor <= 0.0)
1834 return;
1836 // test for 1.0 is just for optimization - nSepCount would be 0
1838 // one separator for each factor of 1000
1839 sal_Int32 nSepCount = static_cast<sal_Int32>(::rtl::math::round( log10(rInfo.fDisplayFactor) / 3.0 ));
1840 if ( nSepCount > 0 )
1842 OUString aSep = m_pData->GetLocaleData( m_nFormatLang ).getNumThousandSep();
1843 for ( sal_Int32 i=0; i<nSepCount; i++ )
1844 m_aFormatCode.append( aSep );
1848 void SvXMLNumFormatContext::AddCurrency( const OUString& rContent, LanguageType nLang )
1850 bool bAutomatic = false;
1851 OUString aSymbol = rContent;
1852 if ( aSymbol.isEmpty())
1854 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1855 if ( pFormatter )
1857 pFormatter->ChangeIntl( m_nFormatLang );
1858 OUString sCurString, sDummy;
1859 pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
1860 aSymbol = sCurString;
1862 bAutomatic = true;
1865 else if ( nLang == LANGUAGE_SYSTEM && aSymbol == "CCC" )
1867 // "CCC" is used for automatic long symbol
1868 bAutomatic = true;
1871 if ( bAutomatic )
1873 // remove unnecessary quotes before automatic symbol (formats like "-(0DM)")
1874 // otherwise the currency symbol isn't recognized (#94048#)
1876 sal_Int32 nLength = m_aFormatCode.getLength();
1877 if ( nLength > 1 && m_aFormatCode[nLength - 1] == '"' )
1879 // find start of quoted string
1880 // When SvXMLNumFmtElementContext::EndElement creates escaped quotes,
1881 // they must be handled here, too.
1883 sal_Int32 nFirst = nLength - 2;
1884 while ( nFirst >= 0 && m_aFormatCode[nFirst] != '"' )
1885 --nFirst;
1886 if ( nFirst >= 0 )
1888 // remove both quotes from aFormatCode
1889 OUString aOld = m_aFormatCode.makeStringAndClear();
1890 if ( nFirst > 0 )
1891 m_aFormatCode.append( aOld.subView( 0, nFirst ) );
1892 if ( nLength > nFirst + 2 )
1893 m_aFormatCode.append( aOld.subView( nFirst + 1, nLength - nFirst - 2 ) );
1898 if (!bAutomatic)
1899 m_aFormatCode.append( "[$" ); // intro for "new" currency symbols
1901 m_aFormatCode.append( aSymbol );
1903 if (!bAutomatic)
1905 if ( nLang != LANGUAGE_SYSTEM )
1907 // '-' sign and language code in hex:
1908 m_aFormatCode.append("-" + OUString(OUString::number(sal_uInt16(nLang), 16)).toAsciiUpperCase());
1911 m_aFormatCode.append( ']' ); // end of "new" currency symbol
1915 void SvXMLNumFormatContext::AddNfKeyword( sal_uInt16 nIndex )
1917 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1918 if (!pFormatter)
1919 return;
1921 if ( nIndex == NF_KEY_NNNN )
1923 nIndex = NF_KEY_NNN;
1924 m_bHasLongDoW = true; // to remove string constant with separator
1927 OUString sKeyword = pFormatter->GetKeyword( m_nFormatLang, nIndex );
1929 if ( nIndex == NF_KEY_H || nIndex == NF_KEY_HH ||
1930 nIndex == NF_KEY_MI || nIndex == NF_KEY_MMI ||
1931 nIndex == NF_KEY_S || nIndex == NF_KEY_SS )
1933 if ( !m_bTruncate && !m_bHasDateTime )
1935 // with truncate-on-overflow = false, add "[]" to first time part
1936 m_aFormatCode.append("[" + sKeyword + "]");
1938 else
1940 m_aFormatCode.append( sKeyword );
1942 m_bHasDateTime = true;
1944 else
1946 m_aFormatCode.append( sKeyword );
1948 // collect the date elements that the format contains, to recognize default date formats
1949 switch ( nIndex )
1951 case NF_KEY_NN: m_eDateDOW = XML_DEA_SHORT; break;
1952 case NF_KEY_NNN:
1953 case NF_KEY_NNNN: m_eDateDOW = XML_DEA_LONG; break;
1954 case NF_KEY_D: m_eDateDay = XML_DEA_SHORT; break;
1955 case NF_KEY_DD: m_eDateDay = XML_DEA_LONG; break;
1956 case NF_KEY_M: m_eDateMonth = XML_DEA_SHORT; break;
1957 case NF_KEY_MM: m_eDateMonth = XML_DEA_LONG; break;
1958 case NF_KEY_MMM: m_eDateMonth = XML_DEA_TEXTSHORT; break;
1959 case NF_KEY_MMMM: m_eDateMonth = XML_DEA_TEXTLONG; break;
1960 case NF_KEY_YY: m_eDateYear = XML_DEA_SHORT; break;
1961 case NF_KEY_YYYY: m_eDateYear = XML_DEA_LONG; break;
1962 case NF_KEY_H: m_eDateHours = XML_DEA_SHORT; break;
1963 case NF_KEY_HH: m_eDateHours = XML_DEA_LONG; break;
1964 case NF_KEY_MI: m_eDateMins = XML_DEA_SHORT; break;
1965 case NF_KEY_MMI: m_eDateMins = XML_DEA_LONG; break;
1966 case NF_KEY_S: m_eDateSecs = XML_DEA_SHORT; break;
1967 case NF_KEY_SS: m_eDateSecs = XML_DEA_LONG; break;
1968 case NF_KEY_AP:
1969 case NF_KEY_AMPM: break; // AM/PM may or may not be in date/time formats -> ignore by itself
1970 default:
1971 m_bDateNoDefault = true; // any other element -> no default format
1975 static bool lcl_IsAtEnd( OUStringBuffer& rBuffer, std::u16string_view rToken )
1977 sal_Int32 nBufLen = rBuffer.getLength();
1978 sal_Int32 nTokLen = rToken.size();
1980 if ( nTokLen > nBufLen )
1981 return false;
1983 sal_Int32 nStartPos = nBufLen - nTokLen;
1984 for ( sal_Int32 nTokPos = 0; nTokPos < nTokLen; nTokPos++ )
1985 if ( rToken[ nTokPos ] != rBuffer[nStartPos + nTokPos] )
1986 return false;
1988 return true;
1991 bool SvXMLNumFormatContext::ReplaceNfKeyword( sal_uInt16 nOld, sal_uInt16 nNew )
1993 // replaces one keyword with another if it is found at the end of the code
1995 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
1996 if (!pFormatter)
1997 return false;
1999 OUString sOldStr = pFormatter->GetKeyword( m_nFormatLang, nOld );
2000 if ( lcl_IsAtEnd( m_aFormatCode, sOldStr ) )
2002 // remove old keyword
2003 m_aFormatCode.setLength( m_aFormatCode.getLength() - sOldStr.getLength() );
2005 // add new keyword
2006 OUString sNewStr = pFormatter->GetKeyword( m_nFormatLang, nNew );
2007 m_aFormatCode.append( sNewStr );
2009 return true; // changed
2011 return false; // not found
2014 void SvXMLNumFormatContext::AddCondition( const sal_Int32 nIndex )
2016 OUString rApplyName = m_aMyConditions[nIndex].sMapName;
2017 OUString rCondition = m_aMyConditions[nIndex].sCondition;
2018 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2019 sal_uInt32 l_nKey = m_pData->GetKeyForName( rApplyName );
2021 OUString sRealCond;
2022 if ( !(pFormatter && l_nKey != NUMBERFORMAT_ENTRY_NOT_FOUND &&
2023 rCondition.startsWith("value()", &sRealCond)) )
2024 return;
2026 //! test for valid conditions
2027 //! test for default conditions
2029 bool bDefaultCond = false;
2031 //! collect all conditions first and adjust default to >=0, >0 or <0 depending on count
2032 //! allow blanks in conditions
2033 if ( m_aConditions.isEmpty() && m_aMyConditions.size() == 1 && sRealCond == ">=0" )
2034 bDefaultCond = true;
2036 if ( m_nType == SvXMLStylesTokens::TEXT_STYLE && static_cast<size_t>(nIndex) == m_aMyConditions.size() - 1 )
2038 // The last condition in a number format with a text part can only
2039 // be "all other numbers", the condition string must be empty.
2040 bDefaultCond = true;
2043 if (!bDefaultCond)
2045 // Convert != to <>
2046 sal_Int32 nPos = sRealCond.indexOf( "!=" );
2047 if ( nPos >= 0 )
2049 sRealCond = sRealCond.replaceAt( nPos, 2, u"<>" );
2052 nPos = sRealCond.indexOf( '.' );
2053 if ( nPos >= 0 )
2055 // #i8026# #103991# localize decimal separator
2056 const OUString& rDecSep = GetLocaleData().getNumDecimalSep();
2057 if ( rDecSep.getLength() > 1 || rDecSep[0] != '.' )
2059 sRealCond = sRealCond.replaceAt( nPos, 1, rDecSep );
2062 m_aConditions.append("[" + sRealCond + "]");
2065 const SvNumberformat* pFormat = pFormatter->GetEntry(l_nKey);
2066 if ( pFormat )
2067 m_aConditions.append( pFormat->GetFormatstring() );
2069 m_aConditions.append( ';' );
2072 void SvXMLNumFormatContext::AddCondition( const OUString& rCondition, const OUString& rApplyName )
2074 MyCondition aCondition;
2075 aCondition.sCondition = rCondition;
2076 aCondition.sMapName = rApplyName;
2077 m_aMyConditions.push_back(aCondition);
2080 void SvXMLNumFormatContext::AddColor( Color const nColor )
2082 SvNumberFormatter* pFormatter = m_pData->GetNumberFormatter();
2083 if (!pFormatter)
2084 return;
2086 OUStringBuffer aColName;
2087 for ( sal_uInt16 i=0; i<XML_NUMF_COLORCOUNT; i++ )
2088 if (nColor == aNumFmtStdColors[i])
2090 aColName = pFormatter->GetKeyword( m_nFormatLang, sal::static_int_cast< sal_uInt16 >(NF_KEY_FIRSTCOLOR + i) );
2091 break;
2094 if ( !aColName.isEmpty() )
2096 aColName.insert( 0, '[' );
2097 aColName.append( ']' );
2098 m_aFormatCode.insert( 0, aColName );
2102 void SvXMLNumFormatContext::UpdateCalendar( const OUString& rNewCalendar )
2104 if ( rNewCalendar == m_sCalendar )
2105 return;
2107 if (rNewCalendar.isEmpty() || rNewCalendar == m_aImplicitCalendar[0])
2109 m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2110 ImplicitCalendar::DEFAULT_FROM_OTHER : ImplicitCalendar::DEFAULT);
2112 else if (m_aImplicitCalendar[0].isEmpty() && rNewCalendar == GetLocaleData().getDefaultCalendar()->Name)
2114 m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2115 ImplicitCalendar::DEFAULT_FROM_OTHER : ImplicitCalendar::DEFAULT);
2116 m_aImplicitCalendar[0] = rNewCalendar;
2118 else if (rNewCalendar == m_aImplicitCalendar[1])
2120 m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2121 ImplicitCalendar::SECONDARY_FROM_OTHER : ImplicitCalendar::SECONDARY);
2123 else if (m_aImplicitCalendar[1].isEmpty() && GetLocaleData().doesSecondaryCalendarUseEC( rNewCalendar))
2125 m_eImplicitCalendar = (m_eImplicitCalendar == ImplicitCalendar::OTHER ?
2126 ImplicitCalendar::SECONDARY_FROM_OTHER : ImplicitCalendar::SECONDARY);
2127 m_aImplicitCalendar[1] = rNewCalendar;
2129 else
2131 m_eImplicitCalendar = ImplicitCalendar::OTHER;
2134 if (m_eImplicitCalendar != ImplicitCalendar::DEFAULT && m_eImplicitCalendar != ImplicitCalendar::SECONDARY)
2136 // A switch from empty default calendar to named default calendar or
2137 // vice versa is not a switch.
2138 bool bSameDefault = false;
2139 if (m_sCalendar.isEmpty() || rNewCalendar.isEmpty())
2141 // As both are not equal, only one can be empty here, the other
2142 // can not.
2143 const OUString& rDefaultCalendar = GetLocaleData().getDefaultCalendar()->Name;
2144 // So if one is the named default calendar the other is the
2145 // empty default calendar.
2146 bSameDefault = (rNewCalendar == rDefaultCalendar || m_sCalendar == rDefaultCalendar);
2148 if (!bSameDefault)
2150 m_aFormatCode.append( "[~" ); // intro for calendar code
2151 if (rNewCalendar.isEmpty())
2153 // Empty calendar name here means switching to default calendar
2154 // from a different calendar. Needs to be explicitly stated in
2155 // format code.
2156 m_aFormatCode.append( GetLocaleData().getDefaultCalendar()->Name );
2158 else
2160 m_aFormatCode.append( rNewCalendar );
2162 m_aFormatCode.append( ']' ); // end of calendar code
2165 m_sCalendar = rNewCalendar;
2168 bool SvXMLNumFormatContext::IsSystemLanguage() const
2170 return m_nFormatLang == LANGUAGE_SYSTEM;
2174 // SvXMLNumFmtHelper
2177 SvXMLNumFmtHelper::SvXMLNumFmtHelper(
2178 const uno::Reference<util::XNumberFormatsSupplier>& rSupp,
2179 const uno::Reference<uno::XComponentContext>& rxContext )
2181 SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
2183 SvNumberFormatter* pFormatter = nullptr;
2184 SvNumberFormatsSupplierObj* pObj =
2185 comphelper::getFromUnoTunnel<SvNumberFormatsSupplierObj>( rSupp );
2186 if (pObj)
2187 pFormatter = pObj->GetNumberFormatter();
2189 m_pData = std::make_unique<SvXMLNumImpData>( pFormatter, rxContext );
2192 SvXMLNumFmtHelper::SvXMLNumFmtHelper(
2193 SvNumberFormatter* pNumberFormatter,
2194 const uno::Reference<uno::XComponentContext>& rxContext )
2196 SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
2198 m_pData = std::make_unique<SvXMLNumImpData>( pNumberFormatter, rxContext );
2201 SvXMLNumFmtHelper::~SvXMLNumFmtHelper()
2203 // remove temporary (volatile) formats from NumberFormatter
2204 m_pData->RemoveVolatileFormats();
2208 SvXMLStyleContext* SvXMLNumFmtHelper::CreateChildContext( SvXMLImport& rImport,
2209 sal_Int32 nElement,
2210 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
2211 SvXMLStylesContext& rStyles )
2213 SvXMLStylesTokens nStyleToken;
2214 switch (nElement)
2216 case XML_ELEMENT(NUMBER, XML_NUMBER_STYLE):
2217 nStyleToken = SvXMLStylesTokens::NUMBER_STYLE;
2218 break;
2219 case XML_ELEMENT(NUMBER, XML_CURRENCY_STYLE):
2220 nStyleToken = SvXMLStylesTokens::CURRENCY_STYLE;
2221 break;
2222 case XML_ELEMENT(NUMBER, XML_PERCENTAGE_STYLE):
2223 nStyleToken = SvXMLStylesTokens::PERCENTAGE_STYLE;
2224 break;
2225 case XML_ELEMENT(NUMBER, XML_DATE_STYLE):
2226 nStyleToken = SvXMLStylesTokens::DATE_STYLE;
2227 break;
2228 case XML_ELEMENT(NUMBER, XML_TIME_STYLE):
2229 nStyleToken = SvXMLStylesTokens::TIME_STYLE;
2230 break;
2231 case XML_ELEMENT(NUMBER, XML_BOOLEAN_STYLE):
2232 nStyleToken = SvXMLStylesTokens::BOOLEAN_STYLE;
2233 break;
2234 case XML_ELEMENT(NUMBER, XML_TEXT_STYLE):
2235 nStyleToken = SvXMLStylesTokens::TEXT_STYLE;
2236 break;
2237 default:
2238 // return NULL if not a data style, caller must handle other elements
2239 return nullptr;
2241 return new SvXMLNumFormatContext( rImport, nElement,
2242 m_pData.get(), nStyleToken, xAttrList, rStyles );
2245 LanguageType SvXMLNumFmtHelper::GetLanguageForKey(sal_Int32 nKey) const
2247 if (m_pData->GetNumberFormatter())
2249 const SvNumberformat* pEntry = m_pData->GetNumberFormatter()->GetEntry(nKey);
2250 if (pEntry)
2251 return pEntry->GetLanguage();
2254 return LANGUAGE_SYSTEM;
2257 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */