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