LibreOffice Module xmloff (master)  1
xmlnumfi.cxx
Go to the documentation of this file.
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 
22 #include <svl/zformat.hxx>
23 #include <svl/numuno.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 <xmloff/xmlement.hxx>
34 #include <xmloff/xmlnumfi.hxx>
35 #include <xmloff/xmltkmap.hxx>
36 #include <xmloff/xmlnamespace.hxx>
37 #include <xmloff/xmlictxt.hxx>
38 #include <xmloff/xmlimp.hxx>
39 #include <xmloff/xmluconv.hxx>
40 #include <xmloff/namespacemap.hxx>
41 #include <xmloff/families.hxx>
42 #include <xmloff/xmltoken.hxx>
44 
45 #include <memory>
46 #include <string_view>
47 #include <vector>
48 
49 using namespace ::com::sun::star;
50 using namespace ::xmloff::token;
51 
52 namespace {
53 
54 struct SvXMLNumFmtEntry
55 {
56  OUString aName;
57  sal_uInt32 nKey;
58  bool bRemoveAfterUse;
59 
60  SvXMLNumFmtEntry( const OUString& rN, sal_uInt32 nK, bool bR ) :
61  aName(rN), nKey(nK), bRemoveAfterUse(bR) {}
62 };
63 
64 }
65 
67 {
69  std::unique_ptr<LocaleDataWrapper> pLocaleData;
70  std::vector<SvXMLNumFmtEntry> m_NameEntries;
71 
72  uno::Reference< uno::XComponentContext > m_xContext;
73 
74 public:
76  SvNumberFormatter* pFmt,
77  const uno::Reference<uno::XComponentContext>& rxContext );
78 
81  sal_uInt32 GetKeyForName( std::u16string_view rName );
82  void AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse );
83  void SetUsed( sal_uInt32 nKey );
84  void RemoveVolatileFormats();
85 };
86 
88 {
89  sal_Int32 nDecimals = -1;
90  sal_Int32 nInteger = -1;
91  sal_Int32 nExpDigits = -1;
92  sal_Int32 nExpInterval = -1;
93  sal_Int32 nMinNumerDigits = -1;
94  sal_Int32 nMinDenomDigits = -1;
95  sal_Int32 nMaxNumerDigits = -1;
96  sal_Int32 nMaxDenomDigits = -1;
97  sal_Int32 nFracDenominator = -1;
98  sal_Int32 nMinDecimalDigits = -1;
99  sal_Int32 nZerosNumerDigits = -1;
100  sal_Int32 nZerosDenomDigits = -1;
101  bool bGrouping = false;
102  bool bDecReplace = false;
103  bool bExpSign = true;
104  bool bDecAlign = false;
105  double fDisplayFactor = 1.0;
107  std::map<sal_Int32, OUString> m_EmbeddedElements;
108 };
109 
110 namespace {
111 
112 enum class SvXMLStyleTokens;
113 
114 class SvXMLNumFmtElementContext : public SvXMLImportContext
115 {
116  SvXMLNumFormatContext& rParent;
118  OUStringBuffer aContent;
119  SvXMLNumberInfo aNumInfo;
120  LanguageType nElementLang;
121  bool bLong;
122  bool bTextual;
123  OUString sCalendar;
124 
125 public:
126  SvXMLNumFmtElementContext( SvXMLImport& rImport, sal_Int32 nElement,
127  SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType,
128  const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
129 
130  virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(
131  sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& AttrList ) override;
132  virtual void SAL_CALL characters( const OUString& rChars ) override;
133  virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
134 
135  void AddEmbeddedElement( sal_Int32 nFormatPos, const OUString& rContent );
136 };
137 
138 class SvXMLNumFmtEmbeddedTextContext : public SvXMLImportContext
139 {
140  SvXMLNumFmtElementContext& rParent;
141  OUStringBuffer aContent;
142  sal_Int32 nTextPosition;
143 
144 public:
145  SvXMLNumFmtEmbeddedTextContext( SvXMLImport& rImport, sal_Int32 nElement,
146  SvXMLNumFmtElementContext& rParentContext,
147  const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
148 
149  virtual void SAL_CALL characters( const OUString& rChars ) override;
150  virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
151 };
152 
153 class SvXMLNumFmtMapContext : public SvXMLImportContext
154 {
155  SvXMLNumFormatContext& rParent;
156  OUString sCondition;
157  OUString sName;
158 
159 public:
160  SvXMLNumFmtMapContext( SvXMLImport& rImport, sal_Int32 nElement,
161  SvXMLNumFormatContext& rParentContext,
162  const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
163 
164  virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
165 };
166 
167 class SvXMLNumFmtPropContext : public SvXMLImportContext
168 {
169  SvXMLNumFormatContext& rParent;
170  Color m_nColor;
171  bool bColSet;
172 
173 public:
174  SvXMLNumFmtPropContext( SvXMLImport& rImport, sal_Int32 nElement,
175  SvXMLNumFormatContext& rParentContext,
176  const css::uno::Reference< css::xml::sax::XFastAttributeList>& xAttrList );
177 
178  virtual void SAL_CALL endFastElement(sal_Int32 nElement) override;
179 };
180 
182 {
183  Text,
184  FillCharacter,
185  Number,
186  ScientificNumber,
187  Fraction,
188  CurrencySymbol,
189  Day,
190  Month,
191  Year,
192  Era,
193  DayOfWeek,
194  WeekOfYear,
195  Quarter,
196  Hours,
197  AmPm,
198  Minutes,
199  Seconds,
200  Boolean,
201  TextContent
202 };
203 
204 }
205 
206 // standard colors
207 
208 
209 #define XML_NUMF_COLORCOUNT 10
210 
212 {
213  COL_BLACK,
217  COL_LIGHTRED,
219  COL_BROWN,
220  COL_GRAY,
221  COL_YELLOW,
222  COL_WHITE
223 };
224 
225 
226 // token maps
227 
228 
229 // maps for SvXMLUnitConverter::convertEnum
230 
232 {
233  { XML_SHORT, false },
234  { XML_LONG, true },
235  { XML_TOKEN_INVALID, false }
236 };
237 
239 {
240  { XML_FIXED, false },
241  { XML_LANGUAGE, true },
242  { XML_TOKEN_INVALID, false }
243 };
244 
245 namespace {
246 
247 struct SvXMLDefaultDateFormat
248 {
249  NfIndexTableOffset eFormat;
257  bool bSystem;
258 };
259 
260 }
261 
262 const SvXMLDefaultDateFormat aDefaultDateFormats[] =
263 {
264  // format day-of-week day month year hours minutes seconds format-source
265 
281 };
282 
283 
284 // SvXMLNumImpData
285 
286 
288  SvNumberFormatter* pFmt,
289  const uno::Reference<uno::XComponentContext>& rxContext )
290 : pFormatter(pFmt),
291  m_xContext(rxContext)
292 {
293  SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
294 }
295 
296 sal_uInt32 SvXMLNumImpData::GetKeyForName( std::u16string_view rName )
297 {
298  for (const auto& rObj : m_NameEntries)
299  {
300  if (rObj.aName == rName)
301  return rObj.nKey; // found
302  }
304 }
305 
306 void SvXMLNumImpData::AddKey( sal_uInt32 nKey, const OUString& rName, bool bRemoveAfterUse )
307 {
308  if ( bRemoveAfterUse )
309  {
310  // if there is already an entry for this key without the bRemoveAfterUse flag,
311  // clear the flag for this entry, too
312 
313  for (const auto& rObj : m_NameEntries)
314  {
315  if (rObj.nKey == nKey && !rObj.bRemoveAfterUse)
316  {
317  bRemoveAfterUse = false; // clear flag for new entry
318  break;
319  }
320  }
321  }
322  else
323  {
324  // call SetUsed to clear the bRemoveAfterUse flag for other entries for this key
325  SetUsed( nKey );
326  }
327 
328  m_NameEntries.emplace_back(rName, nKey, bRemoveAfterUse);
329 }
330 
331 void SvXMLNumImpData::SetUsed( sal_uInt32 nKey )
332 {
333  for (auto& rObj : m_NameEntries)
334  {
335  if (rObj.nKey == nKey)
336  {
337  rObj.bRemoveAfterUse = false; // used -> don't remove
338 
339  // continue searching - there may be several entries for the same key
340  // (with different names), the format must not be deleted if any one of
341  // them is used
342  }
343  }
344 }
345 
347 {
348  // remove temporary (volatile) formats from NumberFormatter
349  // called at the end of each import (styles and content), so volatile formats
350  // from styles can't be used in content
351 
352  if ( !pFormatter )
353  return;
354 
355  for (const auto& rObj : m_NameEntries)
356  {
357  if (rObj.bRemoveAfterUse )
358  {
359  const SvNumberformat* pFormat = pFormatter->GetEntry(rObj.nKey);
360  if (pFormat && (pFormat->GetType() & SvNumFormatType::DEFINED))
361  pFormatter->DeleteEntry(rObj.nKey);
362  }
363  }
364 }
365 
367 {
368  if ( !pLocaleData )
369  pLocaleData = std::make_unique<LocaleDataWrapper>(
371  LanguageTag( nLang ) );
372  else
373  pLocaleData->setLanguageTag( LanguageTag( nLang ) );
374  return *pLocaleData;
375 }
376 
377 
378 // SvXMLNumFmtMapContext
379 
380 
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 )
387 {
388  for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
389  {
390  OUString sValue = aIter.toString();
391  switch(aIter.getToken())
392  {
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);
401  }
402  }
403 }
404 
405 void SvXMLNumFmtMapContext::endFastElement(sal_Int32 )
406 {
407  rParent.AddCondition( sCondition, sName );
408 }
409 
410 
411 // SvXMLNumFmtPropContext
412 
413 
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 )
422 {
423  for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
424  {
425  switch ( aIter.getToken())
426  {
427  case XML_ELEMENT(FO, XML_COLOR):
428  case XML_ELEMENT(FO_COMPAT, XML_COLOR):
429  bColSet = ::sax::Converter::convertColor( m_nColor, aIter.toString() );
430  break;
431  default:
432  XMLOFF_WARN_UNKNOWN("xmloff", aIter);
433  }
434  }
435 }
436 
437 void SvXMLNumFmtPropContext::endFastElement(sal_Int32 )
438 {
439  if (bColSet)
440  rParent.AddColor( m_nColor );
441 }
442 
443 
444 // SvXMLNumFmtEmbeddedTextContext
445 
446 
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 )
454 {
455  sal_Int32 nAttrVal;
456 
457  for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
458  {
459  if ( aIter.getToken() == XML_ELEMENT(NUMBER, XML_POSITION) )
460  {
461  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
462  nTextPosition = nAttrVal;
463  }
464  else
465  XMLOFF_WARN_UNKNOWN("xmloff", aIter);
466  }
467 }
468 
469 void SvXMLNumFmtEmbeddedTextContext::characters( const OUString& rChars )
470 {
471  aContent.append( rChars );
472 }
473 
474 void SvXMLNumFmtEmbeddedTextContext::endFastElement(sal_Int32 )
475 {
476  rParent.AddEmbeddedElement( nTextPosition, aContent.makeStringAndClear() );
477 }
478 
479 static bool lcl_ValidChar( sal_Unicode cChar, const SvXMLNumFormatContext& rParent )
480 {
481  SvXMLStylesTokens nFormatType = rParent.GetType();
482 
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)) )
491  {
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.
497 
498  return false; // force quotes
499  }
500 
501  // see ImpSvNumberformatScan::Next_Symbol
502  if ( cChar == '-' )
503  return true; // all format types may content minus sign or delimiter
504  if ( ( cChar == ' ' ||
505  cChar == '/' ||
506  cChar == '.' ||
507  cChar == ',' ||
508  cChar == ':' ||
509  cChar == '\'' ) &&
510  ( nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
511  nFormatType == SvXMLStylesTokens::DATE_STYLE ||
512  nFormatType == SvXMLStylesTokens::TIME_STYLE ) ) // other formats do not require delimiter tdf#97837
513  return true;
514 
515  // percent sign must be used without quotes for percentage styles only
516  if ( nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE && cChar == '%' )
517  return true;
518 
519  // don't put quotes around single parentheses (often used for negative numbers)
520  if ( ( nFormatType == SvXMLStylesTokens::NUMBER_STYLE ||
521  nFormatType == SvXMLStylesTokens::CURRENCY_STYLE ||
522  nFormatType == SvXMLStylesTokens::PERCENTAGE_STYLE ) &&
523  ( cChar == '(' || cChar == ')' ) )
524  return true;
525 
526  return false;
527 }
528 
529 static void lcl_EnquoteIfNecessary( OUStringBuffer& rContent, const SvXMLNumFormatContext& rParent )
530 {
531  bool bQuote = true;
532  sal_Int32 nLength = rContent.getLength();
533 
534  if ((nLength == 1 && lcl_ValidChar( rContent[0], rParent)) ||
535  (nLength == 2 &&
536  ((rContent[0] == ' ' && rContent[1] == '-') ||
537  (rContent[1] == ' ' && lcl_ValidChar( rContent[0], rParent)))))
538  {
539  // Don't quote single separator characters like space or percent,
540  // or separator characters followed by space (used in date formats).
541  // Or space followed by minus (used in currency formats) that would
542  // lead to almost duplicated formats with built-in formats just with
543  // the difference of quotes.
544  bQuote = false;
545  }
546  else if ( rParent.GetType() == SvXMLStylesTokens::PERCENTAGE_STYLE && nLength > 1 )
547  {
548  // the percent character in percentage styles must be left out of quoting
549  // (one occurrence is enough even if there are several percent characters in the string)
550 
551  OUString aString( rContent.toString() );
552  sal_Int32 nPos = aString.indexOf( '%' );
553  if ( nPos >= 0 )
554  {
555  if ( nPos + 1 < nLength )
556  {
557  if ( nPos + 2 == nLength && lcl_ValidChar( rContent[nPos + 1], rParent ) )
558  {
559  // single character that doesn't need quoting
560  }
561  else
562  {
563  // quote text behind percent character
564  rContent.insert( nPos + 1, '"' );
565  rContent.append( '"' );
566  }
567  }
568  if ( nPos > 0 )
569  {
570  if ( nPos == 1 && lcl_ValidChar( rContent[0], rParent ) )
571  {
572  // single character that doesn't need quoting
573  }
574  else
575  {
576  // quote text before percent character
577  rContent.insert( nPos, '"' );
578  rContent.insert( 0, '"' );
579  }
580  }
581  bQuote = false;
582  }
583  // else: normal quoting (below)
584  }
585 
586  if ( !bQuote )
587  return;
588 
589  // #i55469# quotes in the string itself have to be escaped
590  bool bEscape = ( rContent.indexOf( '"' ) >= 0 );
591  if ( bEscape )
592  {
593  // A quote is turned into "\"" - a quote to end quoted text, an escaped quote,
594  // and a quote to resume quoting.
595  OUString aInsert( "\"\\\"" );
596 
597  sal_Int32 nPos = 0;
598  while ( nPos < rContent.getLength() )
599  {
600  if ( rContent[nPos] == '"' )
601  {
602  rContent.insert( nPos, aInsert );
603  nPos += aInsert.getLength();
604  }
605  ++nPos;
606  }
607  }
608 
609  // quote string literals
610  rContent.insert( 0, '"' );
611  rContent.append( '"' );
612 
613  // remove redundant double quotes at start or end
614  if ( !bEscape )
615  return;
616 
617  if ( rContent.getLength() > 2 &&
618  rContent[0] == '"' &&
619  rContent[1] == '"' )
620  {
621  rContent.remove(0, 2);
622  }
623 
624  sal_Int32 nLen = rContent.getLength();
625  if ( nLen > 2 &&
626  rContent[nLen - 1] == '"' &&
627  rContent[nLen - 2] == '"' )
628  {
629  rContent.truncate(nLen - 2);
630  }
631 }
632 
633 
634 // SvXMLNumFmtElementContext
635 
636 
637 SvXMLNumFmtElementContext::SvXMLNumFmtElementContext( SvXMLImport& rImport,
638  sal_Int32 /*nElement*/,
639  SvXMLNumFormatContext& rParentContext, SvXMLStyleTokens nNewType,
640  const uno::Reference<xml::sax::XFastAttributeList>& xAttrList ) :
641  SvXMLImportContext( rImport ),
642  rParent( rParentContext ),
643  nType( nNewType ),
644  nElementLang( LANGUAGE_SYSTEM ),
645  bLong( false ),
646  bTextual( false )
647 {
648  LanguageTagODF aLanguageTagODF;
649  sal_Int32 nAttrVal;
650  bool bAttrBool(false);
651  bool bVarDecimals = false;
652  bool bIsMaxDenominator = false;
653  double fAttrDouble;
654 
655  for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
656  {
657  switch (aIter.getToken())
658  {
659  case XML_ELEMENT(NUMBER, XML_DECIMAL_PLACES):
660  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
661  {
662  // fdo#58539 & gnome#627420: limit number of digits during import
663  aNumInfo.nDecimals = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
664  }
665  break;
666  case XML_ELEMENT(LO_EXT, XML_MIN_DECIMAL_PLACES):
668  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
669  aNumInfo.nMinDecimalDigits = nAttrVal;
670  break;
671  case XML_ELEMENT(NUMBER, XML_MIN_INTEGER_DIGITS):
672  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
673  aNumInfo.nInteger = nAttrVal;
674  break;
675  case XML_ELEMENT(NUMBER, XML_GROUPING):
676  if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
677  aNumInfo.bGrouping = bAttrBool;
678  break;
679  case XML_ELEMENT(NUMBER, XML_DISPLAY_FACTOR):
680  if (::sax::Converter::convertDouble( fAttrDouble, aIter.toView() ))
681  aNumInfo.fDisplayFactor = fAttrDouble;
682  break;
683  case XML_ELEMENT(NUMBER, XML_DECIMAL_REPLACEMENT):
684  if ( aIter.toView() == " " )
685  {
686  aNumInfo.bDecAlign = true; // space replacement for "?"
687  bVarDecimals = true;
688  }
689  else
690  if ( aIter.isEmpty() )
691  bVarDecimals = true; // empty replacement string: variable decimals
692  else // all other strings
693  aNumInfo.bDecReplace = true; // decimal replacement with dashes
694  break;
695  case XML_ELEMENT(NUMBER, XML_MIN_EXPONENT_DIGITS):
696  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
697  aNumInfo.nExpDigits = std::min<sal_Int32>(nAttrVal, NF_MAX_FORMAT_SYMBOLS);
698  break;
699  case XML_ELEMENT(NUMBER, XML_EXPONENT_INTERVAL):
700  case XML_ELEMENT(LO_EXT, XML_EXPONENT_INTERVAL):
701  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
702  aNumInfo.nExpInterval = nAttrVal;
703  break;
706  if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
707  aNumInfo.bExpSign = bAttrBool;
708  break;
710  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
711  aNumInfo.nMinNumerDigits = nAttrVal;
712  break;
714  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
715  aNumInfo.nMinDenomDigits = nAttrVal;
716  break;
718  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // at least one '#'
719  aNumInfo.nMaxNumerDigits = nAttrVal;
720  break;
721  case XML_ELEMENT(NUMBER, XML_DENOMINATOR_VALUE):
722  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 )) // 0 is not valid
723  {
724  aNumInfo.nFracDenominator = nAttrVal;
725  bIsMaxDenominator = false;
726  }
727  break;
728  case XML_ELEMENT(NUMBER, XML_MAX_DENOMINATOR_VALUE): // part of ODF 1.3
730  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 1 ) && aNumInfo.nFracDenominator <= 0)
731  { // if denominator value not yet defined
732  aNumInfo.nFracDenominator = nAttrVal;
733  bIsMaxDenominator = true;
734  }
735  break;
738  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
739  aNumInfo.nZerosNumerDigits = nAttrVal;
740  break;
743  if (::sax::Converter::convertNumber( nAttrVal, aIter.toView(), 0 ))
744  aNumInfo.nZerosDenomDigits = nAttrVal;
745  break;
748  aNumInfo.aIntegerFractionDelimiter = aIter.toString();
749  break;
750  case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG):
751  aLanguageTagODF.maRfcLanguageTag = aIter.toString();
752  break;
753  case XML_ELEMENT(NUMBER, XML_LANGUAGE):
754  aLanguageTagODF.maLanguage = aIter.toString();
755  break;
756  case XML_ELEMENT(NUMBER, XML_SCRIPT):
757  aLanguageTagODF.maScript = aIter.toString();
758  break;
759  case XML_ELEMENT(NUMBER, XML_COUNTRY):
760  aLanguageTagODF.maCountry = aIter.toString();
761  break;
762  case XML_ELEMENT(NUMBER, XML_STYLE):
763  SvXMLUnitConverter::convertEnum( bLong, aIter.toView(), aStyleValueMap );
764  break;
765  case XML_ELEMENT(NUMBER, XML_TEXTUAL):
766  if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
767  bTextual = bAttrBool;
768  break;
769  case XML_ELEMENT(NUMBER, XML_CALENDAR):
770  sCalendar = aIter.toString();
771  break;
772  default:
773  XMLOFF_WARN_UNKNOWN("xmloff", aIter);
774  }
775  }
776  if ( aNumInfo.nMinDecimalDigits == -1)
777  {
778  if ( bVarDecimals || aNumInfo.bDecReplace )
779  aNumInfo.nMinDecimalDigits = 0;
780  else
781  aNumInfo.nMinDecimalDigits = aNumInfo.nDecimals;
782  }
783  if ( aNumInfo.nZerosDenomDigits > 0 )
784  { // nMin = count of '0' and '?'
785  if ( aNumInfo.nMinDenomDigits < aNumInfo.nZerosDenomDigits )
786  aNumInfo.nMinDenomDigits = aNumInfo.nZerosDenomDigits;
787  }
788  else
789  aNumInfo.nZerosDenomDigits = 0;
790  if ( aNumInfo.nMinDenomDigits >= 0 )
791  if ( aNumInfo.nMaxDenomDigits < aNumInfo.nMinDenomDigits )
792  aNumInfo.nMaxDenomDigits = ( aNumInfo.nMinDenomDigits ? aNumInfo.nMinDenomDigits : 1 );
793  if ( aNumInfo.nZerosNumerDigits > 0 )
794  {
795  if ( aNumInfo.nMinNumerDigits < aNumInfo.nZerosNumerDigits )
796  aNumInfo.nMinNumerDigits = aNumInfo.nZerosNumerDigits;
797  }
798  else
799  aNumInfo.nZerosNumerDigits = 0;
800  if ( aNumInfo.nMinNumerDigits >= 0 )
801  if ( aNumInfo.nMaxNumerDigits < aNumInfo.nMinNumerDigits )
802  aNumInfo.nMaxNumerDigits = ( aNumInfo.nMinNumerDigits ? aNumInfo.nMinNumerDigits : 1 );
803  if ( bIsMaxDenominator && aNumInfo.nFracDenominator > 0 )
804  {
805  aNumInfo.nMaxDenomDigits = floor( log10( aNumInfo.nFracDenominator ) ) + 1;
806  aNumInfo.nFracDenominator = -1; // Max denominator value only gives number of digits at denominator
807  }
808  if ( aNumInfo.nMaxDenomDigits > 0 )
809  {
810  if ( aNumInfo.nMinDenomDigits < 0 )
811  aNumInfo.nMinDenomDigits = 0;
812  else if ( aNumInfo.nMinDenomDigits > aNumInfo.nMaxDenomDigits )
813  aNumInfo.nMinDenomDigits = aNumInfo.nMaxDenomDigits;
814  }
815 
816  if ( !aLanguageTagODF.isEmpty() )
817  {
818  nElementLang = aLanguageTagODF.getLanguageTag().getLanguageType( false);
819  if ( nElementLang == LANGUAGE_DONTKNOW )
820  nElementLang = LANGUAGE_SYSTEM;
821  }
822 
823  if ( aNumInfo.aIntegerFractionDelimiter.isEmpty() )
824  aNumInfo.aIntegerFractionDelimiter = " ";
825 }
826 
827 css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFmtElementContext::createFastChildContext(
828  sal_Int32 nElement,
829  const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
830 {
831  // only number:number supports number:embedded-text child element
832 
833  if ( nType == SvXMLStyleTokens::Number &&
834  nElement == XML_ELEMENT(NUMBER, XML_EMBEDDED_TEXT) )
835  {
836  return new SvXMLNumFmtEmbeddedTextContext( GetImport(), nElement, *this, xAttrList );
837  }
838  else
839  XMLOFF_WARN_UNKNOWN_ELEMENT("xmloff", nElement);
840  return nullptr;
841 }
842 
843 void SvXMLNumFmtElementContext::characters( const OUString& rChars )
844 {
845  aContent.append( rChars );
846 }
847 
848 void SvXMLNumFmtElementContext::AddEmbeddedElement( sal_Int32 nFormatPos, const OUString& rContent )
849 {
850  if (rContent.isEmpty())
851  return;
852 
853  auto iterPair = aNumInfo.m_EmbeddedElements.emplace(nFormatPos, rContent);
854  if (!iterPair.second)
855  // there's already an element at this position - append text to existing element
856  iterPair.first->second += rContent;
857 }
858 
859 void SvXMLNumFmtElementContext::endFastElement(sal_Int32 )
860 {
861  bool bEffLong = bLong;
862  switch (nType)
863  {
864  case SvXMLStyleTokens::Text:
865  if ( rParent.HasLongDoW() &&
866  aContent.toString() == rParent.GetLocaleData().getLongDateDayOfWeekSep() )
867  {
868  // skip separator constant after long day of week
869  // (NF_KEY_NNNN contains the separator)
870 
871  if ( rParent.ReplaceNfKeyword( NF_KEY_NNN, NF_KEY_NNNN ) )
872  {
873  aContent.truncate();
874  }
875 
876  rParent.SetHasLongDoW( false ); // only once
877  }
878  if ( !aContent.isEmpty() )
879  {
880  lcl_EnquoteIfNecessary( aContent, rParent );
881  rParent.AddToCode( aContent.makeStringAndClear() );
882  }
883  break;
884 
885  case SvXMLStyleTokens::Number:
886  rParent.AddNumber( aNumInfo );
887  break;
888 
889  case SvXMLStyleTokens::CurrencySymbol:
890  rParent.AddCurrency( aContent.makeStringAndClear(), nElementLang );
891  break;
892 
893  case SvXMLStyleTokens::TextContent:
894  rParent.AddToCode( '@');
895  break;
896  case SvXMLStyleTokens::FillCharacter:
897  if ( !aContent.isEmpty() )
898  {
899  rParent.AddToCode( '*' );
900  rParent.AddToCode( aContent[0] );
901  }
902  break;
903  case SvXMLStyleTokens::Boolean:
904  // ignored - only default boolean format is supported
905  break;
906 
907  case SvXMLStyleTokens::Day:
908  rParent.UpdateCalendar( sCalendar );
910 
911  rParent.AddNfKeyword(
912  sal::static_int_cast< sal_uInt16 >(
913  bEffLong ? NF_KEY_DD : NF_KEY_D ) );
914  break;
915  case SvXMLStyleTokens::Month:
916  rParent.UpdateCalendar( sCalendar );
918 
919  rParent.AddNfKeyword(
920  sal::static_int_cast< sal_uInt16 >(
921  bTextual
922  ? ( bEffLong ? NF_KEY_MMMM : NF_KEY_MMM )
923  : ( bEffLong ? NF_KEY_MM : NF_KEY_M ) ) );
924  break;
925  case SvXMLStyleTokens::Year:
927  {
928  // Y after G (era) is replaced by E for a secondary calendar.
929  // Do not replace for default calendar.
930  // Also replace Y by E if we're switching to the secondary
931  // calendar of a locale if it is known to implicitly use E.
932  bool bImplicitEC = (!sCalendar.isEmpty() &&
933  rParent.GetLocaleData().doesSecondaryCalendarUseEC( sCalendar));
934  if (bImplicitEC || (!sCalendar.isEmpty() && rParent.HasEra()))
935  {
936  // If E or EE is the first format keyword, passing
937  // bImplicitEC=true suppresses the superfluous calendar
938  // modifier for this format. This does not help for
939  // something like [~cal]DD/MM/EE but so far only YMD order
940  // is used with such calendars. Live with the modifier if
941  // other keywords precede this.
942  rParent.UpdateCalendar( sCalendar, bImplicitEC);
943  rParent.AddNfKeyword(
944  sal::static_int_cast< sal_uInt16 >(
945  bEffLong ? NF_KEY_EEC : NF_KEY_EC ) );
946  }
947  else
948  {
949  rParent.UpdateCalendar( sCalendar );
950  rParent.AddNfKeyword(
951  sal::static_int_cast< sal_uInt16 >(
952  bEffLong ? NF_KEY_YYYY : NF_KEY_YY ) );
953  }
954  }
955  break;
956  case SvXMLStyleTokens::Era:
957  rParent.UpdateCalendar( sCalendar );
959  rParent.AddNfKeyword(
960  sal::static_int_cast< sal_uInt16 >(
961  bEffLong ? NF_KEY_GGG : NF_KEY_G ) );
962  // HasEra flag is set
963  break;
964  case SvXMLStyleTokens::DayOfWeek:
965  rParent.UpdateCalendar( sCalendar );
967  rParent.AddNfKeyword(
968  sal::static_int_cast< sal_uInt16 >(
969  bEffLong ? NF_KEY_NNNN : NF_KEY_NN ) );
970  break;
971  case SvXMLStyleTokens::WeekOfYear:
972  rParent.UpdateCalendar( sCalendar );
973  rParent.AddNfKeyword( NF_KEY_WW );
974  break;
975  case SvXMLStyleTokens::Quarter:
976  rParent.UpdateCalendar( sCalendar );
977  rParent.AddNfKeyword(
978  sal::static_int_cast< sal_uInt16 >(
979  bEffLong ? NF_KEY_QQ : NF_KEY_Q ) );
980  break;
981  case SvXMLStyleTokens::Hours:
982  rParent.AddNfKeyword(
983  sal::static_int_cast< sal_uInt16 >(
984  bEffLong ? NF_KEY_HH : NF_KEY_H ) );
985  break;
986  case SvXMLStyleTokens::AmPm:
988  rParent.AddNfKeyword( NF_KEY_AMPM );
989  break;
990  case SvXMLStyleTokens::Minutes:
991  rParent.AddNfKeyword(
992  sal::static_int_cast< sal_uInt16 >(
993  bEffLong ? NF_KEY_MMI : NF_KEY_MI ) );
994  break;
995  case SvXMLStyleTokens::Seconds:
996  rParent.AddNfKeyword(
997  sal::static_int_cast< sal_uInt16 >(
998  bEffLong ? NF_KEY_SS : NF_KEY_S ) );
999  if ( aNumInfo.nDecimals > 0 )
1000  {
1001  // manually add the decimal places
1002  rParent.AddToCode(rParent.GetLocaleData().getNumDecimalSep());
1003  for (sal_Int32 i=0; i<aNumInfo.nDecimals; i++)
1004  {
1005  rParent.AddToCode( '0');
1006  }
1007  }
1008  break;
1009 
1010  case SvXMLStyleTokens::Fraction:
1011  {
1012  if ( aNumInfo.nInteger >= 0 )
1013  {
1014  // add integer part only if min-integer-digits attribute is there
1015  aNumInfo.nDecimals = 0;
1016  rParent.AddNumber( aNumInfo ); // number without decimals
1017  OUStringBuffer sIntegerFractionDelimiter = aNumInfo.aIntegerFractionDelimiter;
1018  lcl_EnquoteIfNecessary( sIntegerFractionDelimiter, rParent );
1019  rParent.AddToCode( sIntegerFractionDelimiter.makeStringAndClear() ); // default is ' '
1020  }
1021 
1023 
1024  sal_Int32 i;
1025  for (i=aNumInfo.nMaxNumerDigits; i > 0; i--)
1026  {
1027  if ( i > aNumInfo.nMinNumerDigits )
1028  rParent.AddToCode( '#' );
1029  else if ( i > aNumInfo.nZerosNumerDigits )
1030  rParent.AddToCode( '?' );
1031  else
1032  rParent.AddToCode( '0' );
1033  }
1034  rParent.AddToCode( '/' );
1035  if ( aNumInfo.nFracDenominator > 0 )
1036  {
1037  rParent.AddToCode( OUString::number( aNumInfo.nFracDenominator ) );
1038  }
1039  else
1040  {
1041  for (i=aNumInfo.nMaxDenomDigits; i > 0 ; i--)
1042  {
1043  if ( i > aNumInfo.nMinDenomDigits )
1044  rParent.AddToCode( '#' );
1045  else if ( i > aNumInfo.nZerosDenomDigits )
1046  rParent.AddToCode( '?' );
1047  else
1048  rParent.AddToCode( '0' );
1049  }
1050  }
1051  }
1052  break;
1053 
1054  case SvXMLStyleTokens::ScientificNumber:
1055  {
1056  // exponential interval for engineering notation
1057  if( !aNumInfo.bGrouping && aNumInfo.nExpInterval > aNumInfo.nInteger )
1058  {
1059  for (sal_Int32 i=aNumInfo.nInteger; i<aNumInfo.nExpInterval; i++)
1060  {
1061  rParent.AddToCode( '#' );
1062  }
1063  }
1064  rParent.AddNumber( aNumInfo ); // simple number
1065 
1066  if ( aNumInfo.bExpSign )
1067  rParent.AddToCode( "E+" );
1068  else
1069  rParent.AddToCode( "E" );
1070  for (sal_Int32 i=0; i<aNumInfo.nExpDigits; i++)
1071  {
1072  rParent.AddToCode( '0' );
1073  }
1074  }
1075  break;
1076 
1077  default:
1078  assert(false && "invalid element ID");
1079  }
1080 }
1081 
1086  bool bSystem )
1087 {
1088  for (const auto & rEntry : aDefaultDateFormats)
1089  {
1090  if ( bSystem == rEntry.bSystem &&
1091  ( eDOW == rEntry.eDOW || ( rEntry.eDOW == XML_DEA_ANY && eDOW != XML_DEA_NONE ) ) &&
1092  ( eDay == rEntry.eDay || ( rEntry.eDay == XML_DEA_ANY && eDay != XML_DEA_NONE ) ) &&
1093  ( eMonth == rEntry.eMonth || ( rEntry.eMonth == XML_DEA_ANY && eMonth != XML_DEA_NONE ) ) &&
1094  ( eYear == rEntry.eYear || ( rEntry.eYear == XML_DEA_ANY && eYear != XML_DEA_NONE ) ) &&
1095  ( eHours == rEntry.eHours || ( rEntry.eHours == XML_DEA_ANY && eHours != XML_DEA_NONE ) ) &&
1096  ( eMins == rEntry.eMins || ( rEntry.eMins == XML_DEA_ANY && eMins != XML_DEA_NONE ) ) &&
1097  ( eSecs == rEntry.eSecs || ( rEntry.eSecs == XML_DEA_ANY && eSecs != XML_DEA_NONE ) ) )
1098  {
1099  return sal::static_int_cast< sal_uInt16 >(rEntry.eFormat);
1100  }
1101  }
1102 
1103  return NF_INDEX_TABLE_ENTRIES; // invalid
1104 }
1105 
1106 
1107 // SvXMLNumFormatContext
1108 
1110  sal_Int32 /*nElement*/,
1111  SvXMLNumImpData* pNewData, SvXMLStylesTokens nNewType,
1112  const uno::Reference<xml::sax::XFastAttributeList>& xAttrList,
1113  SvXMLStylesContext& rStyles ) :
1114  SvXMLStyleContext( rImport ),
1115  pData( pNewData ),
1116  pStyles( &rStyles ),
1117  aMyConditions(),
1118  nType( nNewType ),
1119  nKey(-1),
1120  nFormatLang( LANGUAGE_SYSTEM ),
1121  bAutoOrder( false ),
1122  bFromSystem( false ),
1123  bTruncate( true ),
1124  bAutoDec( false ),
1125  bAutoInt( false ),
1126  bHasExtraText( false ),
1127  bHasLongDoW( false ),
1128  bHasEra( false ),
1129  bHasDateTime( false ),
1130  bRemoveAfterUse( false ),
1131  eDateDOW( XML_DEA_NONE ),
1132  eDateDay( XML_DEA_NONE ),
1133  eDateMonth( XML_DEA_NONE ),
1134  eDateYear( XML_DEA_NONE ),
1135  eDateHours( XML_DEA_NONE ),
1136  eDateMins( XML_DEA_NONE ),
1137  eDateSecs( XML_DEA_NONE ),
1138  bDateNoDefault( false )
1139 {
1140  LanguageTagODF aLanguageTagODF;
1141  css::i18n::NativeNumberXmlAttributes aNatNumAttr;
1142  OUString aSpellout;
1143  bool bAttrBool(false);
1144 
1145  for( auto &aIter : sax_fastparser::castToFastAttributeList( xAttrList ) )
1146  {
1147  switch (aIter.getToken())
1148  {
1149  // attributes for a style
1150  case XML_ELEMENT(STYLE, XML_NAME):
1151  break;
1152  case XML_ELEMENT(NUMBER, XML_RFC_LANGUAGE_TAG):
1153  aLanguageTagODF.maRfcLanguageTag = aIter.toString();
1154  break;
1155  case XML_ELEMENT(NUMBER, XML_LANGUAGE):
1156  aLanguageTagODF.maLanguage = aIter.toString();
1157  break;
1158  case XML_ELEMENT(NUMBER, XML_SCRIPT):
1159  aLanguageTagODF.maScript = aIter.toString();
1160  break;
1161  case XML_ELEMENT(NUMBER, XML_COUNTRY):
1162  aLanguageTagODF.maCountry = aIter.toString();
1163  break;
1164  case XML_ELEMENT(NUMBER, XML_TITLE):
1165  sFormatTitle = aIter.toString();
1166  break;
1167  case XML_ELEMENT(NUMBER, XML_AUTOMATIC_ORDER):
1168  if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1169  bAutoOrder = bAttrBool;
1170  break;
1171  case XML_ELEMENT(NUMBER, XML_FORMAT_SOURCE):
1172  SvXMLUnitConverter::convertEnum( bFromSystem, aIter.toView(), aFormatSourceMap );
1173  break;
1174  case XML_ELEMENT(NUMBER, XML_TRUNCATE_ON_OVERFLOW):
1175  if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1176  bTruncate = bAttrBool;
1177  break;
1178  case XML_ELEMENT(STYLE, XML_VOLATILE):
1179  // volatile formats can be removed after importing
1180  // if not used in other styles
1181  if (::sax::Converter::convertBool( bAttrBool, aIter.toView() ))
1182  bRemoveAfterUse = bAttrBool;
1183  break;
1185  aNatNumAttr.Format = aIter.toString();
1186  break;
1189  aSpellout = aIter.toString();
1190  break;
1192  aNatNumAttr.Locale.Language = aIter.toString();
1193  break;
1195  aNatNumAttr.Locale.Country = aIter.toString();
1196  break;
1197  case XML_ELEMENT(NUMBER, XML_TRANSLITERATION_STYLE):
1198  aNatNumAttr.Style = aIter.toString();
1199  break;
1200  default:
1201  XMLOFF_WARN_UNKNOWN("xmloff", aIter);
1202  }
1203  }
1204 
1205  if (!aLanguageTagODF.isEmpty())
1206  {
1207  nFormatLang = aLanguageTagODF.getLanguageTag().getLanguageType( false);
1208  if ( nFormatLang == LANGUAGE_DONTKNOW )
1209  nFormatLang = LANGUAGE_SYSTEM;
1210  }
1211 
1212  if (aNatNumAttr.Format.isEmpty() && aSpellout.isEmpty())
1213  return;
1214 
1215  LanguageTag aLanguageTag( OUString(), aNatNumAttr.Locale.Language,
1216  std::u16string_view(), aNatNumAttr.Locale.Country);
1217  aNatNumAttr.Locale = aLanguageTag.getLocale( false);
1218 
1219  // NatNum12 spell out formula (cardinal, ordinal, ordinal-feminine etc.)
1220  if ( !aSpellout.isEmpty() )
1221  {
1222  aFormatCode.append( "[NatNum12 " );
1223  aFormatCode.append( aSpellout );
1224  } else {
1225  SvNumberFormatter* pFormatter = pData->GetNumberFormatter();
1226  if ( !pFormatter ) return;
1227 
1228  sal_Int32 nNatNum = pFormatter->GetNatNum()->convertFromXmlAttributes( aNatNumAttr );
1229  aFormatCode.append( "[NatNum" );
1230  aFormatCode.append( nNatNum );
1231  }
1232 
1233  LanguageType eLang = aLanguageTag.getLanguageType( false );
1234  if ( eLang == LANGUAGE_DONTKNOW )
1235  eLang = LANGUAGE_SYSTEM;
1236  if ( eLang != nFormatLang && eLang != LANGUAGE_SYSTEM )
1237  {
1238  aFormatCode.append( "][$-" );
1239  // language code in upper hex:
1240  aFormatCode.append(OUString::number(static_cast<sal_uInt16>(eLang), 16).toAsciiUpperCase());
1241  }
1242  aFormatCode.append( ']' );
1243 }
1244 
1246  const OUString& rName,
1247  const uno::Reference<xml::sax::XFastAttributeList>& /*xAttrList*/,
1248  const sal_Int32 nTempKey, LanguageType nLang,
1249  SvXMLStylesContext& rStyles ) :
1251  pData( nullptr ),
1252  pStyles( &rStyles ),
1253  aMyConditions(),
1255  nKey(nTempKey),
1256  nFormatLang( nLang ),
1257  bAutoOrder( false ),
1258  bFromSystem( false ),
1259  bTruncate( true ),
1260  bAutoDec( false ),
1261  bAutoInt( false ),
1262  bHasExtraText( false ),
1263  bHasLongDoW( false ),
1264  bHasEra( false ),
1265  bHasDateTime( false ),
1266  bRemoveAfterUse( false ),
1267  eDateDOW( XML_DEA_NONE ),
1268  eDateDay( XML_DEA_NONE ),
1269  eDateMonth( XML_DEA_NONE ),
1270  eDateYear( XML_DEA_NONE ),
1271  eDateHours( XML_DEA_NONE ),
1272  eDateMins( XML_DEA_NONE ),
1273  eDateSecs( XML_DEA_NONE ),
1274  bDateNoDefault( false )
1275 {
1276  SetAttribute(XML_ELEMENT(STYLE, XML_NAME), rName);
1277 }
1278 
1280 {
1281 }
1282 
1283 css::uno::Reference< css::xml::sax::XFastContextHandler > SvXMLNumFormatContext::createFastChildContext(
1284  sal_Int32 nElement,
1285  const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
1286 {
1287  SvXMLImportContext* pContext = nullptr;
1288 
1289  switch (nElement)
1290  {
1291  case XML_ELEMENT(LO_EXT, XML_TEXT):
1292  case XML_ELEMENT(NUMBER, XML_TEXT):
1293  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1294  *this, SvXMLStyleTokens::Text, xAttrList );
1295  break;
1296  case XML_ELEMENT(LO_EXT, XML_FILL_CHARACTER):
1298  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1299  *this, SvXMLStyleTokens::FillCharacter, xAttrList );
1300  break;
1301  case XML_ELEMENT(NUMBER, XML_NUMBER):
1302  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1303  *this, SvXMLStyleTokens::Number, xAttrList );
1304  break;
1306  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1307  *this, SvXMLStyleTokens::ScientificNumber, xAttrList );
1308  break;
1310  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1311  *this, SvXMLStyleTokens::Fraction, xAttrList );
1312  break;
1314  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1315  *this, SvXMLStyleTokens::CurrencySymbol, xAttrList );
1316  break;
1317  case XML_ELEMENT(NUMBER, XML_DAY):
1318  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1319  *this, SvXMLStyleTokens::Day, xAttrList );
1320  break;
1321  case XML_ELEMENT(NUMBER, XML_MONTH):
1322  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1323  *this, SvXMLStyleTokens::Month, xAttrList );
1324  break;
1325  case XML_ELEMENT(NUMBER, XML_YEAR):
1326  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1327  *this, SvXMLStyleTokens::Year, xAttrList );
1328  break;
1329  case XML_ELEMENT(NUMBER, XML_ERA):
1330  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1331  *this, SvXMLStyleTokens::Era, xAttrList );
1332  break;
1334  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1335  *this, SvXMLStyleTokens::DayOfWeek, xAttrList );
1336  break;
1338  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1339  *this, SvXMLStyleTokens::WeekOfYear, xAttrList );
1340  break;
1342  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1343  *this, SvXMLStyleTokens::Quarter, xAttrList );
1344  break;
1345  case XML_ELEMENT(NUMBER, XML_HOURS):
1346  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1347  *this, SvXMLStyleTokens::Hours, xAttrList );
1348  break;
1349  case XML_ELEMENT(NUMBER, XML_AM_PM):
1350  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1351  *this, SvXMLStyleTokens::AmPm, xAttrList );
1352  break;
1354  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1355  *this, SvXMLStyleTokens::Minutes, xAttrList );
1356  break;
1358  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1359  *this, SvXMLStyleTokens::Seconds, xAttrList );
1360  break;
1362  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1363  *this, SvXMLStyleTokens::Boolean, xAttrList );
1364  break;
1366  pContext = new SvXMLNumFmtElementContext( GetImport(), nElement,
1367  *this, SvXMLStyleTokens::TextContent, xAttrList );
1368  break;
1369 
1371  pContext = new SvXMLNumFmtPropContext( GetImport(), nElement,
1372  *this, xAttrList );
1373  break;
1374  case XML_ELEMENT(STYLE, XML_MAP):
1375  {
1376  // SvXMLNumFmtMapContext::EndElement adds to aMyConditions,
1377  // so there's no need for an extra flag
1378  pContext = new SvXMLNumFmtMapContext( GetImport(), nElement,
1379  *this, xAttrList );
1380  }
1381  break;
1382  }
1383 
1384  if( !pContext )
1385  {
1386  SAL_WARN("xmloff.core", "No context for unknown-element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
1387  pContext = new SvXMLImportContext(GetImport());
1388  }
1389 
1390  return pContext;
1391 }
1392 
1394 {
1395  if (nKey > -1)
1396  {
1397  if (bRemoveAfterUse)
1398  {
1399  // format is used -> don't remove
1400  bRemoveAfterUse = false;
1401  if (pData)
1402  pData->SetUsed(nKey);
1403 
1404  // Add to import's list of keys now - CreateAndInsert didn't add
1405  // the style if bRemoveAfterUse was set.
1407  }
1408  return nKey;
1409  }
1410  else
1411  {
1412  // reset bRemoveAfterUse before CreateAndInsert, so AddKey is called without bRemoveAfterUse set
1413  bRemoveAfterUse = false;
1414  CreateAndInsert(true);
1415  return nKey;
1416  }
1417 }
1418 
1420 {
1421  // used for map elements in CreateAndInsert - don't reset bRemoveAfterUse flag
1422 
1423  if (nKey > -1)
1424  return nKey;
1425  else
1426  {
1427  CreateAndInsert(true);
1428  return nKey;
1429  }
1430 }
1431 
1432 sal_Int32 SvXMLNumFormatContext::CreateAndInsert( css::uno::Reference< css::util::XNumberFormatsSupplier > const & xFormatsSupplier )
1433 {
1434  if (nKey <= -1)
1435  {
1436  SvNumberFormatter* pFormatter = nullptr;
1438  comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( xFormatsSupplier );
1439  if (pObj)
1440  pFormatter = pObj->GetNumberFormatter();
1441 
1442  if ( pFormatter )
1443  return CreateAndInsert( pFormatter );
1444  else
1445  return -1;
1446  }
1447  else
1448  return nKey;
1449 }
1450 
1452 {
1453  if (nKey <= -1)
1455 }
1456 
1458 {
1459  if (!pFormatter)
1460  {
1461  OSL_FAIL("no number formatter");
1462  return -1;
1463  }
1464 
1465  sal_uInt32 nIndex = NUMBERFORMAT_ENTRY_NOT_FOUND;
1466 
1467  for (size_t i = 0; i < aMyConditions.size(); i++)
1468  {
1470  XmlStyleFamily::DATA_STYLE, aMyConditions[i].sMapName)));
1471  if (this == pStyle)
1472  {
1473  SAL_INFO("xmloff.style", "invalid style:map references containing style");
1474  pStyle = nullptr;
1475  }
1476  if (pStyle)
1477  {
1478  if (pStyle->PrivateGetKey() > -1) // don't reset pStyle's bRemoveAfterUse flag
1479  AddCondition(i);
1480  }
1481  }
1482 
1483  if ( aFormatCode.isEmpty() )
1484  {
1485  // insert empty format as empty string (with quotes)
1486  // #93901# this check has to be done before inserting the conditions
1487  aFormatCode.append("\"\""); // ""
1488  }
1489 
1490  aFormatCode.insert( 0, aConditions.makeStringAndClear() );
1491  OUString sFormat = aFormatCode.makeStringAndClear();
1492 
1493  // test special cases
1494 
1495  if ( bAutoDec ) // automatic decimal places
1496  {
1497  // #99391# adjust only if the format contains no text elements, no conditions
1498  // and no color definition (detected by the '[' at the start)
1499 
1501  aMyConditions.empty() && sFormat.toChar() != '[' )
1502  nIndex = pFormatter->GetStandardIndex( nFormatLang );
1503  }
1504  if ( bAutoInt ) // automatic integer digits
1505  {
1507 
1509  aMyConditions.empty() && sFormat.toChar() != '[' )
1510  nIndex = pFormatter->GetFormatIndex( NF_NUMBER_SYSTEM, nFormatLang );
1511  }
1512 
1513  // boolean is always the builtin boolean format
1514  // (no other boolean formats are implemented)
1516  nIndex = pFormatter->GetFormatIndex( NF_BOOLEAN, nFormatLang );
1517 
1518  // check for default date formats
1520  {
1524  if ( eFormat < NF_INDEX_TABLE_RESERVED_START )
1525  {
1526  // #109651# if a date format has the automatic-order attribute and
1527  // contains exactly the elements of one of the default date formats,
1528  // use that default format, with the element order and separators
1529  // from the current locale settings
1530 
1531  nIndex = pFormatter->GetFormatIndex( eFormat, nFormatLang );
1532  }
1533  }
1534 
1535  if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND && !sFormat.isEmpty() )
1536  {
1537  // insert by format string
1538 
1539  OUString aFormatStr( sFormat );
1540  nIndex = pFormatter->GetEntryKey( aFormatStr, nFormatLang );
1541  if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
1542  {
1543  sal_Int32 nErrPos = 0;
1544  SvNumFormatType l_nType = SvNumFormatType::ALL;
1545  bool bOk = pFormatter->PutEntry( aFormatStr, nErrPos, l_nType, nIndex, nFormatLang );
1546  if ( !bOk && nErrPos == 0 && aFormatStr != sFormat )
1547  {
1548  // if the string was modified by PutEntry, look for an existing format
1549  // with the modified string
1550  nIndex = pFormatter->GetEntryKey( aFormatStr, nFormatLang );
1551  if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND )
1552  bOk = true;
1553  }
1554  if (!bOk)
1556  }
1557  }
1558 
1560  if ( nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND && !bAutoOrder )
1561  {
1562  // use fixed-order formats instead of SYS... if bAutoOrder is false
1563  // (only if the format strings are equal for the locale)
1564 
1565  NfIndexTableOffset eOffset = pFormatter->GetIndexTableOffset( nIndex );
1566  if ( eOffset == NF_DATE_SYS_DMMMYYYY )
1567  {
1568  sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMYYYY, nFormatLang );
1569  const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex );
1570  const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex );
1571  if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() )
1572  nIndex = nNewIndex;
1573  }
1574  else if ( eOffset == NF_DATE_SYS_DMMMMYYYY )
1575  {
1576  sal_uInt32 nNewIndex = pFormatter->GetFormatIndex( NF_DATE_DIN_DMMMMYYYY, nFormatLang );
1577  const SvNumberformat* pOldEntry = pFormatter->GetEntry( nIndex );
1578  const SvNumberformat* pNewEntry = pFormatter->GetEntry( nNewIndex );
1579  if ( pOldEntry && pNewEntry && pOldEntry->GetFormatstring() == pNewEntry->GetFormatstring() )
1580  nIndex = nNewIndex;
1581  }
1582  }
1583 
1584  if ((nIndex != NUMBERFORMAT_ENTRY_NOT_FOUND) && !sFormatTitle.isEmpty())
1585  {
1586  SvNumberformat* pFormat = const_cast<SvNumberformat*>(pFormatter->GetEntry( nIndex ));
1587  if (pFormat)
1588  {
1589  pFormat->SetComment(sFormatTitle);
1590  }
1591  }
1592 
1593  if ( nIndex == NUMBERFORMAT_ENTRY_NOT_FOUND )
1594  {
1595  OSL_FAIL("invalid number format");
1596  nIndex = pFormatter->GetStandardIndex( nFormatLang );
1597  }
1598 
1599  pData->AddKey( nIndex, GetName(), bRemoveAfterUse );
1600  nKey = nIndex;
1601 
1602  // Add to import's list of keys (shared between styles and content import)
1603  // only if not volatile - formats are removed from NumberFormatter at the
1604  // end of each import (in SvXMLNumFmtHelper dtor).
1605  // If bRemoveAfterUse is reset later in GetKey, AddNumberStyle is called there.
1606 
1607  if (!bRemoveAfterUse)
1609 
1610  return nKey;
1611 }
1612 
1614 {
1615  return pData->GetLocaleData( nFormatLang );
1616 }
1617 
1619 {
1620  aFormatCode.append( c );
1621  bHasExtraText = true;
1622 }
1623 
1624 void SvXMLNumFormatContext::AddToCode( const OUString& rString )
1625 {
1626  aFormatCode.append( rString );
1627  bHasExtraText = true;
1628 }
1629 
1631 {
1632  SvNumberFormatter* pFormatter = pData->GetNumberFormatter();
1633  if (!pFormatter)
1634  return;
1635 
1636  // store special conditions
1637  bAutoDec = ( rInfo.nDecimals < 0 );
1638  bAutoInt = ( rInfo.nInteger < 0 );
1639 
1640  sal_uInt16 nPrec = 0;
1641  sal_uInt16 nLeading = 0;
1642  if ( rInfo.nDecimals >= 0 ) // < 0 : Default
1643  nPrec = static_cast<sal_uInt16>(rInfo.nDecimals);
1644  if ( rInfo.nInteger >= 0 ) // < 0 : Default
1645  nLeading = static_cast<sal_uInt16>(rInfo.nInteger);
1646 
1647  if ( bAutoDec )
1648  {
1650  {
1651  // for currency formats, "automatic decimals" is used for the automatic
1652  // currency format with (fixed) decimals from the locale settings
1653 
1655  nPrec = rLoc.getCurrDigits();
1656  }
1657  else
1658  {
1659  // for other types, "automatic decimals" means dynamic determination of
1660  // decimals, as achieved with the "general" keyword
1661 
1662  aFormatCode.append( pFormatter->GetStandardName( nFormatLang ) );
1663  return;
1664  }
1665  }
1666  if ( bAutoInt )
1667  {
1669  }
1670 
1671  sal_uInt16 nGenPrec = nPrec;
1672  if ( rInfo.nMinDecimalDigits >= 0 )
1673  nGenPrec = rInfo.nMinDecimalDigits;
1674  if ( rInfo.bDecReplace )
1675  nGenPrec = 0; // generate format without decimals...
1676 
1677  bool bGrouping = rInfo.bGrouping;
1678  size_t const nEmbeddedCount = rInfo.m_EmbeddedElements.size();
1679  if ( nEmbeddedCount )
1680  bGrouping = false; // grouping and embedded characters can't be used together
1681 
1682  sal_uInt32 nStdIndex = pFormatter->GetStandardIndex( nFormatLang );
1683  OUStringBuffer aNumStr = pFormatter->GenerateFormat( nStdIndex, nFormatLang,
1684  bGrouping, false, nGenPrec, nLeading );
1685 
1686  if ( rInfo.nExpDigits >= 0 && nLeading == 0 && !bGrouping && nEmbeddedCount == 0 )
1687  {
1688  // #i43959# For scientific numbers, "#" in the integer part forces a digit,
1689  // so it has to be removed if nLeading is 0 (".00E+0", not "#.00E+0").
1690 
1691  aNumStr.stripStart('#');
1692  }
1693 
1694  if ( bGrouping && rInfo.nExpInterval > rInfo.nInteger )
1695  {
1696  sal_Int32 nIndex = 0;
1697  sal_Int32 nDigits = rInfo.nInteger;
1698  sal_Int32 nIntegerEnd = aNumStr.indexOf( pFormatter->GetNumDecimalSep() );
1699  if ( nIntegerEnd < 0 )
1700  nIntegerEnd = aNumStr.getLength();
1701  while ( nIndex >= 0 && nIndex < nIntegerEnd )
1702  {
1703  if ( ( nIndex = aNumStr.indexOf( '#', nIndex ) ) >= 0 )
1704  {
1705  nDigits ++;
1706  nIndex ++;
1707  }
1708  else
1709  nIndex = -1;
1710  }
1711  while ( rInfo.nExpInterval > nDigits )
1712  {
1713  nDigits++;
1714  aNumStr.insert( 0, '#' );
1715  }
1716  }
1717 
1718  if ( nEmbeddedCount )
1719  {
1720  // insert embedded strings into number string
1721  // only the integer part is supported
1722  // nZeroPos is the string position where format position 0 is inserted
1723 
1724  sal_Int32 nZeroPos = aNumStr.indexOf( pData->GetLocaleData( nFormatLang ).getNumDecimalSep() );
1725  if ( nZeroPos < 0 )
1726  {
1727  nZeroPos = aNumStr.getLength();
1728  }
1729 
1730  // m_EmbeddedElements is sorted - last entry has the largest position (leftmost)
1731  sal_Int32 const nLastFormatPos = rInfo.m_EmbeddedElements.rbegin()->first;
1732  if ( nLastFormatPos >= nZeroPos )
1733  {
1734  // add '#' characters so all embedded texts are really embedded in digits
1735  // (there always has to be a digit before the leftmost embedded text)
1736 
1737  sal_Int32 nAddCount = nLastFormatPos + 1 - nZeroPos;
1738  for(sal_Int32 index = 0; index < nAddCount; ++index)
1739  {
1740  aNumStr.insert(0, '#');
1741  }
1742  nZeroPos = nZeroPos + nAddCount;
1743  }
1744 
1745  // m_EmbeddedElements is sorted with ascending positions - loop is from right to left
1746  for (auto const& it : rInfo.m_EmbeddedElements)
1747  {
1748  sal_Int32 const nFormatPos = it.first;
1749  sal_Int32 nInsertPos = nZeroPos - nFormatPos;
1750  if ( nFormatPos >= 0 && nInsertPos >= 0 )
1751  {
1752  // #107805# always quote embedded strings - even space would otherwise
1753  // be recognized as thousands separator in French.
1754 
1755  aNumStr.insert(nInsertPos, '"');
1756  aNumStr.insert(nInsertPos, it.second);
1757  aNumStr.insert(nInsertPos, '"');
1758  }
1759  }
1760  }
1761 
1762  aFormatCode.append( aNumStr.makeStringAndClear() );
1763 
1764  if ( ( rInfo.bDecReplace || rInfo.nMinDecimalDigits < rInfo.nDecimals ) && nPrec ) // add decimal replacement (dashes)
1765  {
1766  // add dashes for explicit decimal replacement, # or ? for variable decimals
1767  sal_Unicode cAdd = rInfo.bDecReplace ? '-' : ( rInfo.bDecAlign ? '?': '#' );
1768 
1769  if ( rInfo.nMinDecimalDigits == 0 )
1771  for ( sal_uInt16 i=rInfo.nMinDecimalDigits; i<nPrec; i++)
1772  aFormatCode.append( cAdd );
1773  }
1774 
1775  // add extra thousands separators for display factor
1776 
1777  if (rInfo.fDisplayFactor == 1.0 || rInfo.fDisplayFactor <= 0.0)
1778  return;
1779 
1780  // test for 1.0 is just for optimization - nSepCount would be 0
1781 
1782  // one separator for each factor of 1000
1783  sal_Int32 nSepCount = static_cast<sal_Int32>(::rtl::math::round( log10(rInfo.fDisplayFactor) / 3.0 ));
1784  if ( nSepCount > 0 )
1785  {
1786  OUString aSep = pData->GetLocaleData( nFormatLang ).getNumThousandSep();
1787  for ( sal_Int32 i=0; i<nSepCount; i++ )
1788  aFormatCode.append( aSep );
1789  }
1790 }
1791 
1792 void SvXMLNumFormatContext::AddCurrency( const OUString& rContent, LanguageType nLang )
1793 {
1794  bool bAutomatic = false;
1795  OUString aSymbol = rContent;
1796  if ( aSymbol.isEmpty())
1797  {
1798  SvNumberFormatter* pFormatter = pData->GetNumberFormatter();
1799  if ( pFormatter )
1800  {
1801  pFormatter->ChangeIntl( nFormatLang );
1802  OUString sCurString, sDummy;
1803  pFormatter->GetCompatibilityCurrency( sCurString, sDummy );
1804  aSymbol = sCurString;
1805 
1806  bAutomatic = true;
1807  }
1808  }
1809  else if ( nLang == LANGUAGE_SYSTEM && aSymbol == "CCC" )
1810  {
1811  // "CCC" is used for automatic long symbol
1812  bAutomatic = true;
1813  }
1814 
1815  if ( bAutomatic )
1816  {
1817  // remove unnecessary quotes before automatic symbol (formats like "-(0DM)")
1818  // otherwise the currency symbol isn't recognized (#94048#)
1819 
1820  sal_Int32 nLength = aFormatCode.getLength();
1821  if ( nLength > 1 && aFormatCode[nLength - 1] == '"' )
1822  {
1823  // find start of quoted string
1824  // When SvXMLNumFmtElementContext::EndElement creates escaped quotes,
1825  // they must be handled here, too.
1826 
1827  sal_Int32 nFirst = nLength - 2;
1828  while ( nFirst >= 0 && aFormatCode[nFirst] != '"' )
1829  --nFirst;
1830  if ( nFirst >= 0 )
1831  {
1832  // remove both quotes from aFormatCode
1833  OUString aOld = aFormatCode.makeStringAndClear();
1834  if ( nFirst > 0 )
1835  aFormatCode.append( aOld.copy( 0, nFirst ) );
1836  if ( nLength > nFirst + 2 )
1837  aFormatCode.append( aOld.copy( nFirst + 1, nLength - nFirst - 2 ) );
1838  }
1839  }
1840  }
1841 
1842  if (!bAutomatic)
1843  aFormatCode.append( "[$" ); // intro for "new" currency symbols
1844 
1845  aFormatCode.append( aSymbol );
1846 
1847  if (!bAutomatic)
1848  {
1849  if ( nLang != LANGUAGE_SYSTEM )
1850  {
1851  // '-' sign and language code in hex:
1852  aFormatCode.append("-").append(OUString(OUString::number(sal_uInt16(nLang), 16)).toAsciiUpperCase());
1853  }
1854 
1855  aFormatCode.append( ']' ); // end of "new" currency symbol
1856  }
1857 }
1858 
1859 void SvXMLNumFormatContext::AddNfKeyword( sal_uInt16 nIndex )
1860 {
1861  SvNumberFormatter* pFormatter = pData->GetNumberFormatter();
1862  if (!pFormatter)
1863  return;
1864 
1865  if ( nIndex == NF_KEY_G || nIndex == NF_KEY_GG || nIndex == NF_KEY_GGG )
1866  bHasEra = true;
1867 
1868  if ( nIndex == NF_KEY_NNNN )
1869  {
1870  nIndex = NF_KEY_NNN;
1871  bHasLongDoW = true; // to remove string constant with separator
1872  }
1873 
1874  OUString sKeyword = pFormatter->GetKeyword( nFormatLang, nIndex );
1875 
1876  if ( nIndex == NF_KEY_H || nIndex == NF_KEY_HH ||
1877  nIndex == NF_KEY_MI || nIndex == NF_KEY_MMI ||
1878  nIndex == NF_KEY_S || nIndex == NF_KEY_SS )
1879  {
1880  if ( !bTruncate && !bHasDateTime )
1881  {
1882  // with truncate-on-overflow = false, add "[]" to first time part
1883  aFormatCode.append("[").append(sKeyword).append("]");
1884  }
1885  else
1886  {
1887  aFormatCode.append( sKeyword );
1888  }
1889  bHasDateTime = true;
1890  }
1891  else
1892  {
1893  aFormatCode.append( sKeyword );
1894  }
1895  // collect the date elements that the format contains, to recognize default date formats
1896  switch ( nIndex )
1897  {
1898  case NF_KEY_NN: eDateDOW = XML_DEA_SHORT; break;
1899  case NF_KEY_NNN:
1900  case NF_KEY_NNNN: eDateDOW = XML_DEA_LONG; break;
1901  case NF_KEY_D: eDateDay = XML_DEA_SHORT; break;
1902  case NF_KEY_DD: eDateDay = XML_DEA_LONG; break;
1903  case NF_KEY_M: eDateMonth = XML_DEA_SHORT; break;
1904  case NF_KEY_MM: eDateMonth = XML_DEA_LONG; break;
1905  case NF_KEY_MMM: eDateMonth = XML_DEA_TEXTSHORT; break;
1906  case NF_KEY_MMMM: eDateMonth = XML_DEA_TEXTLONG; break;
1907  case NF_KEY_YY: eDateYear = XML_DEA_SHORT; break;
1908  case NF_KEY_YYYY: eDateYear = XML_DEA_LONG; break;
1909  case NF_KEY_H: eDateHours = XML_DEA_SHORT; break;
1910  case NF_KEY_HH: eDateHours = XML_DEA_LONG; break;
1911  case NF_KEY_MI: eDateMins = XML_DEA_SHORT; break;
1912  case NF_KEY_MMI: eDateMins = XML_DEA_LONG; break;
1913  case NF_KEY_S: eDateSecs = XML_DEA_SHORT; break;
1914  case NF_KEY_SS: eDateSecs = XML_DEA_LONG; break;
1915  case NF_KEY_AP:
1916  case NF_KEY_AMPM: break; // AM/PM may or may not be in date/time formats -> ignore by itself
1917  default:
1918  bDateNoDefault = true; // any other element -> no default format
1919  }
1920 }
1921 
1922 static bool lcl_IsAtEnd( OUStringBuffer& rBuffer, const OUString& rToken )
1923 {
1924  sal_Int32 nBufLen = rBuffer.getLength();
1925  sal_Int32 nTokLen = rToken.getLength();
1926 
1927  if ( nTokLen > nBufLen )
1928  return false;
1929 
1930  sal_Int32 nStartPos = nBufLen - nTokLen;
1931  for ( sal_Int32 nTokPos = 0; nTokPos < nTokLen; nTokPos++ )
1932  if ( rToken[ nTokPos ] != rBuffer[nStartPos + nTokPos] )
1933  return false;
1934 
1935  return true;
1936 }
1937 
1938 bool SvXMLNumFormatContext::ReplaceNfKeyword( sal_uInt16 nOld, sal_uInt16 nNew )
1939 {
1940  // replaces one keyword with another if it is found at the end of the code
1941 
1942  SvNumberFormatter* pFormatter = pData->GetNumberFormatter();
1943  if (!pFormatter)
1944  return false;
1945 
1946  OUString sOldStr = pFormatter->GetKeyword( nFormatLang, nOld );
1947  if ( lcl_IsAtEnd( aFormatCode, sOldStr ) )
1948  {
1949  // remove old keyword
1950  aFormatCode.setLength( aFormatCode.getLength() - sOldStr.getLength() );
1951 
1952  // add new keyword
1953  OUString sNewStr = pFormatter->GetKeyword( nFormatLang, nNew );
1954  aFormatCode.append( sNewStr );
1955 
1956  return true; // changed
1957  }
1958  return false; // not found
1959 }
1960 
1961 void SvXMLNumFormatContext::AddCondition( const sal_Int32 nIndex )
1962 {
1963  OUString rApplyName = aMyConditions[nIndex].sMapName;
1964  OUString rCondition = aMyConditions[nIndex].sCondition;
1965  SvNumberFormatter* pFormatter = pData->GetNumberFormatter();
1966  sal_uInt32 l_nKey = pData->GetKeyForName( rApplyName );
1967 
1968  OUString sRealCond;
1969  if ( !(pFormatter && l_nKey != NUMBERFORMAT_ENTRY_NOT_FOUND &&
1970  rCondition.startsWith("value()", &sRealCond)) )
1971  return;
1972 
1975 
1976  bool bDefaultCond = false;
1977 
1980  if ( aConditions.isEmpty() && aMyConditions.size() == 1 && sRealCond == ">=0" )
1981  bDefaultCond = true;
1982 
1983  if ( nType == SvXMLStylesTokens::TEXT_STYLE && static_cast<size_t>(nIndex) == aMyConditions.size() - 1 )
1984  {
1985  // The last condition in a number format with a text part can only
1986  // be "all other numbers", the condition string must be empty.
1987  bDefaultCond = true;
1988  }
1989 
1990  if (!bDefaultCond)
1991  {
1992  // Convert != to <>
1993  sal_Int32 nPos = sRealCond.indexOf( "!=" );
1994  if ( nPos >= 0 )
1995  {
1996  sRealCond = sRealCond.replaceAt( nPos, 2, "<>" );
1997  }
1998 
1999  nPos = sRealCond.indexOf( '.' );
2000  if ( nPos >= 0 )
2001  {
2002  // #i8026# #103991# localize decimal separator
2003  const OUString& rDecSep = GetLocaleData().getNumDecimalSep();
2004  if ( rDecSep.getLength() > 1 || rDecSep[0] != '.' )
2005  {
2006  sRealCond = sRealCond.replaceAt( nPos, 1, rDecSep );
2007  }
2008  }
2009  aConditions.append("[").append(sRealCond).append("]");
2010  }
2011 
2012  const SvNumberformat* pFormat = pFormatter->GetEntry(l_nKey);
2013  if ( pFormat )
2014  aConditions.append( pFormat->GetFormatstring() );
2015 
2016  aConditions.append( ';' );
2017 }
2018 
2019 void SvXMLNumFormatContext::AddCondition( const OUString& rCondition, const OUString& rApplyName )
2020 {
2021  MyCondition aCondition;
2022  aCondition.sCondition = rCondition;
2023  aCondition.sMapName = rApplyName;
2024  aMyConditions.push_back(aCondition);
2025 }
2026 
2028 {
2029  SvNumberFormatter* pFormatter = pData->GetNumberFormatter();
2030  if (!pFormatter)
2031  return;
2032 
2033  OUStringBuffer aColName;
2034  for ( sal_uInt16 i=0; i<XML_NUMF_COLORCOUNT; i++ )
2035  if (nColor == aNumFmtStdColors[i])
2036  {
2037  aColName = pFormatter->GetKeyword( nFormatLang, sal::static_int_cast< sal_uInt16 >(NF_KEY_FIRSTCOLOR + i) );
2038  break;
2039  }
2040 
2041  if ( !aColName.isEmpty() )
2042  {
2043  aColName.insert( 0, '[' );
2044  aColName.append( ']' );
2045  aFormatCode.insert( 0, aColName.makeStringAndClear() );
2046  }
2047 }
2048 
2049 void SvXMLNumFormatContext::UpdateCalendar( const OUString& rNewCalendar, bool bImplicitSecondaryCalendarEC )
2050 {
2051  if ( rNewCalendar != sCalendar )
2052  {
2053  sCalendar = rNewCalendar;
2054  if ( !sCalendar.isEmpty() && !bImplicitSecondaryCalendarEC )
2055  {
2056  aFormatCode.append( "[~" ); // intro for calendar code
2057  aFormatCode.append( sCalendar );
2058  aFormatCode.append( ']' ); // end of calendar code
2059  }
2060  }
2061 }
2062 
2064 {
2065  return nFormatLang == LANGUAGE_SYSTEM;
2066 }
2067 
2068 
2069 // SvXMLNumFmtHelper
2070 
2071 
2073  const uno::Reference<util::XNumberFormatsSupplier>& rSupp,
2074  const uno::Reference<uno::XComponentContext>& rxContext )
2075 {
2076  SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
2077 
2078  SvNumberFormatter* pFormatter = nullptr;
2080  comphelper::getUnoTunnelImplementation<SvNumberFormatsSupplierObj>( rSupp );
2081  if (pObj)
2082  pFormatter = pObj->GetNumberFormatter();
2083 
2084  pData = std::make_unique<SvXMLNumImpData>( pFormatter, rxContext );
2085 }
2086 
2088  SvNumberFormatter* pNumberFormatter,
2089  const uno::Reference<uno::XComponentContext>& rxContext )
2090 {
2091  SAL_WARN_IF( !rxContext.is(), "xmloff", "got no service manager" );
2092 
2093  pData = std::make_unique<SvXMLNumImpData>( pNumberFormatter, rxContext );
2094 }
2095 
2097 {
2098  // remove temporary (volatile) formats from NumberFormatter
2099  pData->RemoveVolatileFormats();
2100 }
2101 
2102 
2104  sal_Int32 nElement,
2105  const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList,
2106  SvXMLStylesContext& rStyles )
2107 {
2108  SvXMLStylesTokens nStyleToken;
2109  switch (nElement)
2110  {
2112  nStyleToken = SvXMLStylesTokens::NUMBER_STYLE;
2113  break;
2115  nStyleToken = SvXMLStylesTokens::CURRENCY_STYLE;
2116  break;
2119  break;
2121  nStyleToken = SvXMLStylesTokens::DATE_STYLE;
2122  break;
2124  nStyleToken = SvXMLStylesTokens::TIME_STYLE;
2125  break;
2127  nStyleToken = SvXMLStylesTokens::BOOLEAN_STYLE;
2128  break;
2130  nStyleToken = SvXMLStylesTokens::TEXT_STYLE;
2131  break;
2132  default:
2133  // return NULL if not a data style, caller must handle other elements
2134  return nullptr;
2135  }
2136  return new SvXMLNumFormatContext( rImport, nElement,
2137  pData.get(), nStyleToken, xAttrList, rStyles );
2138 }
2139 
2141 {
2142  if (pData->GetNumberFormatter())
2143  {
2144  const SvNumberformat* pEntry = pData->GetNumberFormatter()->GetEntry(nKey);
2145  if (pEntry)
2146  return pEntry->GetLanguage();
2147  }
2148 
2149  return LANGUAGE_SYSTEM;
2150 }
2151 
2152 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
NF_KEY_AMPM
const Color aNumFmtStdColors[XML_NUMF_COLORCOUNT]
Definition: xmlnumfi.cxx:211
void AddNumber(const SvXMLNumberInfo &rInfo)
Definition: xmlnumfi.cxx:1630
sal_Int32 nIndex
const OUString & GetNumDecimalSep() const
NF_INDEX_TABLE_RESERVED_START
NF_KEY_YY
sal_Int32 nMinDecimalDigits
Definition: xmlnumfi.cxx:98
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_LIGHTMAGENTA
SvXMLStylesTokens nType
Definition: xmlnumfi.hxx:121
LanguageType nFormatLang
Definition: xmlnumfi.hxx:127
NF_KEY_NNNN
sal_Int32 nExpDigits
Definition: xmlnumfi.cxx:91
std::unique_ptr< ContentProperties > pData
the SvXMLTypeConverter converts values of various types from their internal representation to the tex...
Definition: xmluconv.hxx:80
NF_INDEX_TABLE_ENTRIES
NF_DATE_SYS_NNDMMMMYYYY
sal_uInt32 GetFormatIndex(NfIndexTableOffset, LanguageType eLnge=LANGUAGE_DONTKNOW)
OUString sCondition
Definition: xmlnumfi.hxx:112
sal_uInt16 getCurrDigits() const
LanguageType getLanguageType(bool bResolveSystem=true) const
NF_DATETIME_SYS_DDMMYYYY_HHMMSS
void GetCompatibilityCurrency(OUString &rSymbol, OUString &rAbbrev) const
SvXMLDateElementAttributes eDateSecs
Definition: xmlnumfi.hxx:148
constexpr size_t NF_MAX_FORMAT_SYMBOLS
virtual ~SvXMLNumFormatContext() override
Definition: xmlnumfi.cxx:1279
sal_Int32 nZerosDenomDigits
Definition: xmlnumfi.cxx:100
SvXMLNumFormatContext(SvXMLImport &rImport, sal_Int32 nElement, SvXMLNumImpData *pNewData, SvXMLStylesTokens nNewType, const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList, SvXMLStylesContext &rStyles)
SvXMLImport & GetImport()
Definition: xmlictxt.hxx:56
SvXMLDateElementAttributes eDateDay
Definition: xmlnumfi.hxx:143
OUString aIntegerFractionDelimiter
Definition: xmlnumfi.cxx:106
NfIndexTableOffset GetIndexTableOffset(sal_uInt32 nFormat) const
FastAttributeList & castToFastAttributeList(const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList)
LanguageTag getLanguageTag() const
Best call this only once per instance, it recreates a LanguageTag instance on every call...
SvNumberFormatter * pFormatter
Definition: xmlnumfi.cxx:68
SvXMLStyleContext * CreateChildContext(SvXMLImport &rImport, sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &xAttrList, SvXMLStylesContext &rStyles)
Definition: xmlnumfi.cxx:2103
sal_Int32 nMinNumerDigits
Definition: xmlnumfi.cxx:93
const LocaleDataWrapper & GetLocaleData(LanguageType nLang)
Definition: xmlnumfi.cxx:366
void SetComment(const OUString &rStr)
NF_KEY_NNN
sal_Int32 nExpInterval
Definition: xmlnumfi.cxx:92
NF_KEY_DD
const OUString & GetFormatstring() const
NF_DATE_SYS_DDMMM
NF_DATE_SYS_DDMMYY
sal_uInt32 GetKeyForName(std::u16string_view rName)
Definition: xmlnumfi.cxx:296
sal_Int32 nMaxNumerDigits
Definition: xmlnumfi.cxx:95
XmlStyleFamily
Definition: families.hxx:47
sal_uInt16 sal_Unicode
#define min(a, b)
sal_Int32 nZerosNumerDigits
Definition: xmlnumfi.cxx:99
NF_KEY_MMMM
const BorderLinePrimitive2D *pCandidateB assert(pCandidateA)
SvXMLNumImpData * pData
Definition: xmlnumfi.hxx:118
constexpr sal_uInt32 NUMBERFORMAT_ENTRY_NOT_FOUND
sal_uInt32 GetStandardIndex(LanguageType eLnge=LANGUAGE_DONTKNOW)
void RemoveVolatileFormats()
Definition: xmlnumfi.cxx:346
#define XMLOFF_WARN_UNKNOWN(area, rIter)
Definition: xmlictxt.hxx:110
std::vector< MyCondition > aMyConditions
Definition: xmlnumfi.hxx:120
NfIndexTableOffset
static void convertNumber(OUStringBuffer &b, sal_Int32 n)
OUString GetStandardName(LanguageType eLnge)
static OUString getPrefixAndNameFromToken(sal_Int32 nToken)
Definition: xmlimp.cxx:1912
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_LIGHTRED
const char * sName
bool PutEntry(OUString &rString, sal_Int32 &nCheckPos, SvNumFormatType &nType, sal_uInt32 &nKey, LanguageType eLnge=LANGUAGE_DONTKNOW)
SvXMLStylesContext * pStyles
Definition: xmlnumfi.hxx:119
LanguageType GetLanguageForKey(sal_Int32 nKey)
Definition: xmlnumfi.cxx:2140
OUString sMapName
Definition: xmlnumfi.hxx:113
const OUString & getNumDecimalSep() const
NF_BOOLEAN
SvNumFormatType GetType() const
void SetUsed(sal_uInt32 nKey)
Definition: xmlnumfi.cxx:331
SvXMLDateElementAttributes eDateMonth
Definition: xmlnumfi.hxx:144
NF_DATE_SYS_DMMMYYYY
int i
NF_KEY_MM
#define XML_NUMF_COLORCOUNT
Definition: xmlnumfi.cxx:209
NF_KEY_SS
sal_Int32 nDecimals
Definition: xmlnumfi.cxx:89
bool isEmpty() const
void AddCurrency(const OUString &rContent, LanguageType nLang)
Definition: xmlnumfi.cxx:1792
virtual css::uno::Reference< css::xml::sax::XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList > &AttrList) override
Definition: xmlnumfi.cxx:1283
SvXMLDateElementAttributes
Definition: xmlnumfi.hxx:49
#define LANGUAGE_SYSTEM
std::map< sal_Int32, OUString > m_EmbeddedElements
Definition: xmlnumfi.cxx:107
sal_Int32 nMaxDenomDigits
Definition: xmlnumfi.cxx:96
NF_KEY_GG
NF_KEY_FIRSTCOLOR
NUMBER
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_GRAY
SvXMLDateElementAttributes eDateDOW
Definition: xmlnumfi.hxx:142
NF_DATETIME_SYS_DDMMYYYY_HHMM
bool ReplaceNfKeyword(sal_uInt16 nOld, sal_uInt16 nNew)
Definition: xmlnumfi.cxx:1938
sal_Int32 nMinDenomDigits
Definition: xmlnumfi.cxx:94
OUStringBuffer aConditions
Definition: xmlnumfi.hxx:135
SvNumFormatType
SvXMLImportContext(SvXMLImport &rImport)
A contexts constructor does anything that is required if an element starts.
Definition: xmlictxt.cxx:30
NF_DATE_SYS_DDMMYYYY
SvXMLStylesTokens
Definition: xmlnumfi.hxx:38
OUStringBuffer aFormatCode
Definition: xmlnumfi.hxx:134
sal_uInt32 GetEntryKey(std::u16string_view sStr, LanguageType eLnge=LANGUAGE_DONTKNOW)
tuple index
std::vector< SvXMLNumFmtEntry > m_NameEntries
Definition: xmlnumfi.cxx:70
void DeleteEntry(sal_uInt32 nKey)
const uno::Reference< uno::XComponentContext > m_xContext
SvXMLDateElementAttributes eDateHours
Definition: xmlnumfi.hxx:146
void AddColor(Color nColor)
Definition: xmlnumfi.cxx:2027
NF_KEY_MMI
SvXMLNumFmtHelper(const css::uno::Reference< css::util::XNumberFormatsSupplier > &rSupp, const css::uno::Reference< css::uno::XComponentContext > &rxContext)
const css::uno::Reference< css::uno::XComponentContext > & GetComponentContext() const
NF_NUMBER_SYSTEM
NF_DATE_DIN_DMMMMYYYY
NF_KEY_GGG
SvXMLStyleTokens
Definition: xmlnumfi.cxx:181
const SvXMLDefaultDateFormat aDefaultDateFormats[]
Definition: xmlnumfi.cxx:262
This class deliberately does not support XWeak, to improve performance when loading large documents...
Definition: xmlictxt.hxx:45
const SvXMLEnumMapEntry< bool > aStyleValueMap[]
Definition: xmlnumfi.cxx:231
OUString GenerateFormat(sal_uInt32 nIndex, LanguageType eLnge=LANGUAGE_DONTKNOW, bool bThousand=false, bool IsRed=false, sal_uInt16 nPrecision=0, sal_uInt16 nLeadingCnt=1)
Map an XMLTokenEnum to an enum value.
Definition: ximpshap.hxx:40
void AddCondition(const sal_Int32 nIndex)
Definition: xmlnumfi.cxx:1961
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_LIGHTCYAN
const NativeNumberWrapper * GetNatNum() const
const SvXMLEnumMapEntry< bool > aFormatSourceMap[]
Definition: xmlnumfi.cxx:238
NF_KEY_YYYY
void ChangeIntl(LanguageType eLnge)
NF_DATE_SYSTEM_LONG
#define SAL_WARN_IF(condition, area, stream)
const OUString & GetName() const
Definition: xmlstyle.hxx:77
const SvNumberformat * GetEntry(sal_uInt32 nKey) const
SvXMLNumImpData(SvNumberFormatter *pFmt, const uno::Reference< uno::XComponentContext > &rxContext)
Definition: xmlnumfi.cxx:287
virtual void SAL_CALL endFastElement(sal_Int32 Element) override
endFastElement is called before a context will be destructed, but after an elements context has been ...
Definition: xmlictxt.cxx:40
NF_DATE_SYS_NNNNDMMMMYYYY
std::unique_ptr< SvXMLNumImpData > pData
Definition: xmlnumfi.hxx:72
sal_Int32 nInteger
Definition: xmlnumfi.cxx:90
Handling of tokens in XML:
void AddKey(sal_uInt32 nKey, const OUString &rName, bool bRemoveAfterUse)
Definition: xmlnumfi.cxx:306
void AddNumberStyle(sal_Int32 nKey, const OUString &sName)
Definition: xmlimp.cxx:1467
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_WHITE
#define SAL_INFO(area, stream)
OUString aName
std::unique_ptr< LocaleDataWrapper > pLocaleData
Definition: xmlnumfi.cxx:69
virtual css::uno::Reference< XFastContextHandler > SAL_CALL createFastChildContext(sal_Int32 Element, const css::uno::Reference< css::xml::sax::XFastAttributeList > &Attribs) override
Definition: xmlictxt.cxx:59
sal_Int32 nFracDenominator
Definition: xmlnumfi.cxx:97
NF_DATE_SYS_DMMMYY
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BROWN
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_LIGHTGREEN
NF_DATE_SYSTEM_SHORT
static sal_uInt16 GetDefaultDateFormat(SvXMLDateElementAttributes eDOW, SvXMLDateElementAttributes eDay, SvXMLDateElementAttributes eMonth, SvXMLDateElementAttributes eYear, SvXMLDateElementAttributes eHours, SvXMLDateElementAttributes eMins, SvXMLDateElementAttributes eSecs, bool bSystem)
Definition: xmlnumfi.cxx:1082
NF_DATE_SYS_MMYY
#define XML_ELEMENT(prefix, name)
Definition: xmlimp.hxx:96
SvNumberFormatter * GetNumberFormatter() const
NF_DATE_DIN_DMMMYYYY
QPRO_FUNC_TYPE nType
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_BLACK
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_YELLOW
SvXMLStylesTokens GetType() const
Definition: xmlnumfi.hxx:175
void UpdateCalendar(const OUString &rNewCalendar, bool bImplicitSecondaryCalendarEC=false)
Definition: xmlnumfi.cxx:2049
static void lcl_EnquoteIfNecessary(OUStringBuffer &rContent, const SvXMLNumFormatContext &rParent)
Definition: xmlnumfi.cxx:529
SAL_DLLPRIVATE sal_Int32 PrivateGetKey()
Definition: xmlnumfi.cxx:1419
NF_DATE_SYS_NNDMMMYY
const OUString & getNumThousandSep() const
const SvXMLStyleContext * FindStyleChildContext(XmlStyleFamily nFamily, const OUString &rName, bool bCreateIndex=false) const
Definition: xmlstyle.cxx:788
SvXMLDateElementAttributes eDateMins
Definition: xmlnumfi.hxx:147
const ::std::vector< Color > ImpSvNumberformatScan::StandardColor COL_LIGHTBLUE
static bool lcl_ValidChar(sal_Unicode cChar, const SvXMLNumFormatContext &rParent)
Definition: xmlnumfi.cxx:479
NF_DATETIME_SYSTEM_SHORT_HHMM
#define SAL_WARN(area, stream)
#define XMLOFF_WARN_UNKNOWN_ELEMENT(area, token)
Definition: xmlictxt.hxx:116
SvXMLDateElementAttributes eDateYear
Definition: xmlnumfi.hxx:145
SvNumberFormatter * GetNumberFormatter() const
Definition: xmlnumfi.cxx:79
NF_KEY_AP
sal_Int32 nLength
Definition: xmltoken.cxx:36
if(!pCandidateA->getEnd().equal(pCandidateB->getStart()))
OUString GetKeyword(LanguageType eLnge, sal_uInt16 nIndex)
uno::Reference< uno::XComponentContext > m_xContext
Definition: xmlnumfi.cxx:72
virtual void CreateAndInsert(bool bOverwrite) override
Definition: xmlnumfi.cxx:1451
const LocaleDataWrapper & GetLocaleData() const
Definition: xmlnumfi.cxx:1613
sal_Int32 nNatNum
NF_KEY_HH
Helper to gather the single language tag relevant attributes during reading ODF and form a resulting ...
virtual void SAL_CALL characters(const OUString &aChars) override
This method is called for all characters that are contained in the current element.
Definition: xmlictxt.cxx:70
bool IsSystemLanguage() const
determine whether number format uses the system language
Definition: xmlnumfi.cxx:2063
LanguageType GetLanguage() const
void AddToCode(const OUString &rString)
Definition: xmlnumfi.cxx:1624
void AddNfKeyword(sal_uInt16 nIndex)
Definition: xmlnumfi.cxx:1859
sal_uInt16 nPos
OUString toString(OptionInfo const *info)
double fDisplayFactor
Definition: xmlnumfi.cxx:105
NF_DATE_SYS_DMMMMYYYY
static bool convertNumber(sal_Int32 &rValue, std::u16string_view aString, sal_Int32 nMin=SAL_MIN_INT32, sal_Int32 nMax=SAL_MAX_INT32)
static bool lcl_IsAtEnd(OUStringBuffer &rBuffer, const OUString &rToken)
Definition: xmlnumfi.cxx:1922