bump product version to 5.0.4.1
[LibreOffice.git] / svl / source / numbers / zformat.cxx
blobe0ea8821b829c91b469e45f7c3aa7184a19be4aa
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <stdio.h>
21 #include <ctype.h>
22 #include <float.h>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <comphelper/string.hxx>
26 #include <sal/log.hxx>
27 #include <tools/debug.hxx>
28 #include <osl/diagnose.h>
29 #include <i18nlangtag/mslangid.hxx>
30 #include <rtl/math.hxx>
31 #include <unotools/charclass.hxx>
32 #include <unotools/calendarwrapper.hxx>
33 #include <unotools/nativenumberwrapper.hxx>
34 #include <com/sun/star/i18n/CalendarFieldIndex.hpp>
35 #include <com/sun/star/i18n/CalendarDisplayIndex.hpp>
36 #include <com/sun/star/i18n/CalendarDisplayCode.hpp>
37 #include <com/sun/star/i18n/AmPmValue.hpp>
39 #include <svl/zformat.hxx>
40 #include "zforscan.hxx"
42 #include "zforfind.hxx"
43 #include <svl/zforlist.hxx>
44 #include <unotools/digitgroupingiterator.hxx>
45 #include <svl/nfsymbol.hxx>
47 #include <cmath>
48 #include <boost/scoped_ptr.hpp>
50 using namespace svt;
52 namespace {
54 char const GREGORIAN[] = "gregorian";
56 const sal_uInt16 UPPER_PRECISION = 300; // entirely arbitrary...
57 const double EXP_LOWER_BOUND = 1.0E-4; // prefer scientific notation below this value.
58 const double EXP_ABS_UPPER_BOUND = 1.0E15; // use exponential notation above that absolute value.
59 // Back in time was E16 that lead
60 // to display rounding errors, see
61 // also sal/rtl/math.cxx
62 // doubleToString()
64 } // namespace
66 const double _D_MAX_U_LONG_ = (double) 0xffffffff; // 4294967295.0
67 const sal_uInt16 _MAX_FRACTION_PREC = 3;
68 const double D_EPS = 1.0E-2;
70 const double _D_MAX_D_BY_100 = 1.7E306;
71 const double _D_MIN_M_BY_1000 = 2.3E-305;
73 static const sal_uInt8 cCharWidths[ 128-32 ] = {
74 1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1,
75 2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,
76 3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3,
77 2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2,
78 1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2,
79 2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1
82 // static
83 sal_Int32 SvNumberformat::InsertBlanks( OUStringBuffer& r, sal_Int32 nPos, sal_Unicode c )
85 if( c >= 32 )
87 int n = 2; // Default for chars > 128 (HACK!)
88 if( c <= 127 )
90 n = (int)cCharWidths[ c - 32 ];
92 while( n-- )
94 r.insert( nPos++, ' ');
97 return nPos;
100 static long GetPrecExp( double fAbsVal )
102 DBG_ASSERT( fAbsVal > 0.0, "GetPrecExp: fAbsVal <= 0.0" );
103 if ( fAbsVal < 1e-7 || fAbsVal > 1e7 )
105 // Shear: whether it's faster or not, falls in between 1e6 and 1e7
106 return (long) floor( log10( fAbsVal ) ) + 1;
108 else
110 long nPrecExp = 1;
111 while( fAbsVal < 1 )
113 fAbsVal *= 10;
114 nPrecExp--;
116 while( fAbsVal >= 10 )
118 fAbsVal /= 10;
119 nPrecExp++;
121 return nPrecExp;
126 * SvNumberformatInfo
127 * */
129 void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo& rNumFor, sal_uInt16 nAnz )
131 for (sal_uInt16 i = 0; i < nAnz; ++i)
133 sStrArray[i] = rNumFor.sStrArray[i];
134 nTypeArray[i] = rNumFor.nTypeArray[i];
136 eScannedType = rNumFor.eScannedType;
137 bThousand = rNumFor.bThousand;
138 nThousand = rNumFor.nThousand;
139 nCntPre = rNumFor.nCntPre;
140 nCntPost = rNumFor.nCntPost;
141 nCntExp = rNumFor.nCntExp;
144 // static
145 sal_uInt8 SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum, LanguageType eLang, bool bDate )
147 sal_uInt8 nNatNum = 0;
148 eLang = MsLangId::getRealLanguage( eLang ); // resolve SYSTEM etc.
149 eLang &= 0x03FF; // 10 bit primary language
150 if ( bDate )
152 if ( nDBNum == 4 && eLang == (LANGUAGE_KOREAN & 0x03FF) )
154 nNatNum = 9;
156 else if ( nDBNum <= 3 )
158 nNatNum = nDBNum; // known to be good for: zh,ja,ko / 1,2,3
161 else
163 switch ( nDBNum )
165 case 1:
166 switch ( eLang )
168 case (LANGUAGE_CHINESE & 0x03FF):
169 nNatNum = 4;
170 break;
171 case (LANGUAGE_JAPANESE & 0x03FF):
172 nNatNum = 1;
173 break;
174 case (LANGUAGE_KOREAN & 0x03FF):
175 nNatNum = 1;
176 break;
178 break;
179 case 2:
180 switch ( eLang )
182 case (LANGUAGE_CHINESE & 0x03FF):
183 nNatNum = 5;
184 break;
185 case (LANGUAGE_JAPANESE & 0x03FF):
186 nNatNum = 4;
187 break;
188 case (LANGUAGE_KOREAN & 0x03FF):
189 nNatNum = 2;
190 break;
192 break;
193 case 3:
194 switch ( eLang )
196 case (LANGUAGE_CHINESE & 0x03FF):
197 nNatNum = 6;
198 break;
199 case (LANGUAGE_JAPANESE & 0x03FF):
200 nNatNum = 5;
201 break;
202 case (LANGUAGE_KOREAN & 0x03FF):
203 nNatNum = 3;
204 break;
206 break;
207 case 4:
208 switch ( eLang )
210 case (LANGUAGE_JAPANESE & 0x03FF):
211 nNatNum = 7;
212 break;
213 case (LANGUAGE_KOREAN & 0x03FF):
214 nNatNum = 9;
215 break;
217 break;
220 return nNatNum;
223 #ifdef THE_FUTURE
224 /* XXX NOTE: even though the MapNatNumToDBNum method is currently unused please
225 * don't remove it in case we'd have to use it for some obscure exports to
226 * Excel. */
228 // static
229 sal_uInt8 SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum, LanguageType eLang, bool bDate )
231 sal_uInt8 nDBNum = 0;
232 eLang = MsLangId::getRealLanguage( eLang ); // resolve SYSTEM etc.
233 eLang &= 0x03FF; // 10 bit primary language
234 if ( bDate )
236 if ( nNatNum == 9 && eLang == (LANGUAGE_KOREAN & 0x03FF) )
238 nDBNum = 4;
240 else if ( nNatNum <= 3 )
242 nDBNum = nNatNum; // known to be good for: zh,ja,ko / 1,2,3
245 else
247 switch ( nNatNum )
249 case 1:
250 switch ( eLang )
252 case (LANGUAGE_JAPANESE & 0x03FF):
253 nDBNum = 1;
254 break;
255 case (LANGUAGE_KOREAN & 0x03FF):
256 nDBNum = 1;
257 break;
259 break;
260 case 2:
261 switch ( eLang )
263 case (LANGUAGE_KOREAN & 0x03FF):
264 nDBNum = 2;
265 break;
267 break;
268 case 3:
269 switch ( eLang )
271 case (LANGUAGE_KOREAN & 0x03FF):
272 nDBNum = 3;
273 break;
275 break;
276 case 4:
277 switch ( eLang )
279 case (LANGUAGE_CHINESE & 0x03FF):
280 nDBNum = 1;
281 break;
282 case (LANGUAGE_JAPANESE & 0x03FF):
283 nDBNum = 2;
284 break;
286 break;
287 case 5:
288 switch ( eLang )
290 case (LANGUAGE_CHINESE & 0x03FF):
291 nDBNum = 2;
292 break;
293 case (LANGUAGE_JAPANESE & 0x03FF):
294 nDBNum = 3;
295 break;
297 break;
298 case 6:
299 switch ( eLang )
301 case (LANGUAGE_CHINESE & 0x03FF):
302 nDBNum = 3;
303 break;
305 break;
306 case 7:
307 switch ( eLang )
309 case (LANGUAGE_JAPANESE & 0x03FF):
310 nDBNum = 4;
311 break;
313 break;
314 case 8:
315 break;
316 case 9:
317 switch ( eLang )
319 case (LANGUAGE_KOREAN & 0x03FF):
320 nDBNum = 4;
321 break;
323 break;
324 case 10:
325 break;
326 case 11:
327 break;
330 return nDBNum;
332 #endif
335 * SvNumFor
338 ImpSvNumFor::ImpSvNumFor()
340 nAnzStrings = 0;
341 aI.nTypeArray = NULL;
342 aI.sStrArray = NULL;
343 aI.eScannedType = css::util::NumberFormat::UNDEFINED;
344 aI.bThousand = false;
345 aI.nThousand = 0;
346 aI.nCntPre = 0;
347 aI.nCntPost = 0;
348 aI.nCntExp = 0;
349 pColor = NULL;
352 ImpSvNumFor::~ImpSvNumFor()
354 delete [] aI.sStrArray;
355 delete [] aI.nTypeArray;
358 void ImpSvNumFor::Enlarge(sal_uInt16 nAnz)
360 if ( nAnzStrings != nAnz )
362 delete [] aI.nTypeArray;
363 delete [] aI.sStrArray;
364 nAnzStrings = nAnz;
365 if ( nAnz )
367 aI.nTypeArray = new short[nAnz];
368 aI.sStrArray = new OUString[nAnz];
370 else
372 aI.nTypeArray = NULL;
373 aI.sStrArray = NULL;
378 void ImpSvNumFor::Copy( const ImpSvNumFor& rNumFor, ImpSvNumberformatScan* pSc )
380 Enlarge( rNumFor.nAnzStrings );
381 aI.Copy( rNumFor.aI, nAnzStrings );
382 sColorName = rNumFor.sColorName;
383 if ( pSc )
385 pColor = pSc->GetColor( sColorName ); // #121103# don't copy pointer between documents
387 else
389 pColor = rNumFor.pColor;
391 aNatNum = rNumFor.aNatNum;
394 bool ImpSvNumFor::HasNewCurrency() const
396 for ( sal_uInt16 j=0; j<nAnzStrings; j++ )
398 if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
400 return true;
403 return false;
406 bool ImpSvNumFor::GetNewCurrencySymbol( OUString& rSymbol,
407 OUString& rExtension ) const
409 for ( sal_uInt16 j=0; j<nAnzStrings; j++ )
411 if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
413 rSymbol = aI.sStrArray[j];
414 if ( j < nAnzStrings-1 && aI.nTypeArray[j+1] == NF_SYMBOLTYPE_CURREXT )
416 rExtension = aI.sStrArray[j+1];
418 else
420 rExtension.clear();
422 return true;
425 //! No Erase at rSymbol, rExtension
426 return false;
430 * SvNumberformat
433 enum BracketFormatSymbolType
435 BRACKET_SYMBOLTYPE_FORMAT = -1, // subformat string
436 BRACKET_SYMBOLTYPE_COLOR = -2, // color
437 BRACKET_SYMBOLTYPE_ERROR = -3, // error
438 BRACKET_SYMBOLTYPE_DBNUM1 = -4, // DoubleByteNumber, represent numbers
439 BRACKET_SYMBOLTYPE_DBNUM2 = -5, // using CJK characters, Excel compatible
440 BRACKET_SYMBOLTYPE_DBNUM3 = -6,
441 BRACKET_SYMBOLTYPE_DBNUM4 = -7,
442 BRACKET_SYMBOLTYPE_DBNUM5 = -8,
443 BRACKET_SYMBOLTYPE_DBNUM6 = -9,
444 BRACKET_SYMBOLTYPE_DBNUM7 = -10,
445 BRACKET_SYMBOLTYPE_DBNUM8 = -11,
446 BRACKET_SYMBOLTYPE_DBNUM9 = -12,
447 BRACKET_SYMBOLTYPE_LOCALE = -13,
448 BRACKET_SYMBOLTYPE_NATNUM0 = -14, // Our NativeNumber support, ASCII
449 BRACKET_SYMBOLTYPE_NATNUM1 = -15, // Our NativeNumber support, represent
450 BRACKET_SYMBOLTYPE_NATNUM2 = -16, // numbers using CJK, CTL, ...
451 BRACKET_SYMBOLTYPE_NATNUM3 = -17,
452 BRACKET_SYMBOLTYPE_NATNUM4 = -18,
453 BRACKET_SYMBOLTYPE_NATNUM5 = -19,
454 BRACKET_SYMBOLTYPE_NATNUM6 = -20,
455 BRACKET_SYMBOLTYPE_NATNUM7 = -21,
456 BRACKET_SYMBOLTYPE_NATNUM8 = -22,
457 BRACKET_SYMBOLTYPE_NATNUM9 = -23,
458 BRACKET_SYMBOLTYPE_NATNUM10 = -24,
459 BRACKET_SYMBOLTYPE_NATNUM11 = -25,
460 BRACKET_SYMBOLTYPE_NATNUM12 = -26,
461 BRACKET_SYMBOLTYPE_NATNUM13 = -27,
462 BRACKET_SYMBOLTYPE_NATNUM14 = -28,
463 BRACKET_SYMBOLTYPE_NATNUM15 = -29,
464 BRACKET_SYMBOLTYPE_NATNUM16 = -30,
465 BRACKET_SYMBOLTYPE_NATNUM17 = -31,
466 BRACKET_SYMBOLTYPE_NATNUM18 = -32,
467 BRACKET_SYMBOLTYPE_NATNUM19 = -33
470 void SvNumberformat::ImpCopyNumberformat( const SvNumberformat& rFormat )
472 sFormatstring = rFormat.sFormatstring;
473 eType = rFormat.eType;
474 maLocale = rFormat.maLocale;
475 fLimit1 = rFormat.fLimit1;
476 fLimit2 = rFormat.fLimit2;
477 eOp1 = rFormat.eOp1;
478 eOp2 = rFormat.eOp2;
479 bStandard = rFormat.bStandard;
480 bIsUsed = rFormat.bIsUsed;
481 sComment = rFormat.sComment;
482 bAdditionalBuiltin = rFormat.bAdditionalBuiltin;
484 // #121103# when copying between documents, get color pointers from own scanner
485 ImpSvNumberformatScan* pColorSc = ( &rScan != &rFormat.rScan ) ? &rScan : NULL;
487 for (sal_uInt16 i = 0; i < 4; i++)
489 NumFor[i].Copy(rFormat.NumFor[i], pColorSc);
493 SvNumberformat::SvNumberformat( SvNumberformat& rFormat )
494 : rScan(rFormat.rScan), bStarFlag( rFormat.bStarFlag )
496 ImpCopyNumberformat( rFormat );
499 SvNumberformat::SvNumberformat( SvNumberformat& rFormat, ImpSvNumberformatScan& rSc )
500 : rScan(rSc)
501 , bStarFlag( rFormat.bStarFlag )
503 ImpCopyNumberformat( rFormat );
506 static bool lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType )
508 if ( nSymbolType > 0 )
510 return true; // conditions
512 switch ( nSymbolType )
514 case BRACKET_SYMBOLTYPE_COLOR :
515 case BRACKET_SYMBOLTYPE_DBNUM1 :
516 case BRACKET_SYMBOLTYPE_DBNUM2 :
517 case BRACKET_SYMBOLTYPE_DBNUM3 :
518 case BRACKET_SYMBOLTYPE_DBNUM4 :
519 case BRACKET_SYMBOLTYPE_DBNUM5 :
520 case BRACKET_SYMBOLTYPE_DBNUM6 :
521 case BRACKET_SYMBOLTYPE_DBNUM7 :
522 case BRACKET_SYMBOLTYPE_DBNUM8 :
523 case BRACKET_SYMBOLTYPE_DBNUM9 :
524 case BRACKET_SYMBOLTYPE_LOCALE :
525 case BRACKET_SYMBOLTYPE_NATNUM0 :
526 case BRACKET_SYMBOLTYPE_NATNUM1 :
527 case BRACKET_SYMBOLTYPE_NATNUM2 :
528 case BRACKET_SYMBOLTYPE_NATNUM3 :
529 case BRACKET_SYMBOLTYPE_NATNUM4 :
530 case BRACKET_SYMBOLTYPE_NATNUM5 :
531 case BRACKET_SYMBOLTYPE_NATNUM6 :
532 case BRACKET_SYMBOLTYPE_NATNUM7 :
533 case BRACKET_SYMBOLTYPE_NATNUM8 :
534 case BRACKET_SYMBOLTYPE_NATNUM9 :
535 case BRACKET_SYMBOLTYPE_NATNUM10 :
536 case BRACKET_SYMBOLTYPE_NATNUM11 :
537 case BRACKET_SYMBOLTYPE_NATNUM12 :
538 case BRACKET_SYMBOLTYPE_NATNUM13 :
539 case BRACKET_SYMBOLTYPE_NATNUM14 :
540 case BRACKET_SYMBOLTYPE_NATNUM15 :
541 case BRACKET_SYMBOLTYPE_NATNUM16 :
542 case BRACKET_SYMBOLTYPE_NATNUM17 :
543 case BRACKET_SYMBOLTYPE_NATNUM18 :
544 case BRACKET_SYMBOLTYPE_NATNUM19 :
545 return true;
547 return false;
551 OUString SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer & rString, sal_Int32 & nPos,
552 LanguageType & nLang, const LocaleType & aTmpLocale )
554 OUString sCalendar;
555 /* TODO: this could be enhanced to allow other possible locale dependent
556 * calendars and numerals. BUT only if our locale data allows it! For LCID
557 * numerals and calendars see
558 * http://office.microsoft.com/en-us/excel/HA010346351033.aspx */
559 if (MsLangId::getRealLanguage( aTmpLocale.meLanguage) == LANGUAGE_THAI)
561 // Numeral shape code "D" = Thai digits.
562 if (aTmpLocale.mnNumeralShape == 0xD)
564 rString.insert( nPos, "[NatNum1]");
566 // Calendar type code "07" = Thai Buddhist calendar, insert this after
567 // all prefixes have been consumed as it is actually a format modifier
568 // and not a prefix.
569 if (aTmpLocale.mnCalendarType == 0x07)
571 // Currently calendars are tied to the locale of the entire number
572 // format, e.g. [~buddhist] in en_US doesn't work.
573 // => Having different locales in sub formats does not work!
574 /* TODO: calendars could be tied to a sub format's NatNum info
575 * instead, or even better be available for any locale. Needs a
576 * different implementation of GetCal() and locale data calendars.
577 * */
578 // If this is not Thai yet, make it so.
579 if (MsLangId::getRealLanguage( maLocale.meLanguage) != LANGUAGE_THAI)
581 maLocale = aTmpLocale;
582 nLang = maLocale.meLanguage = LANGUAGE_THAI;
584 sCalendar="[~buddhist]";
587 return sCalendar;
591 SvNumberformat::SvNumberformat(OUString& rString,
592 ImpSvNumberformatScan* pSc,
593 ImpSvNumberInputScan* pISc,
594 sal_Int32& nCheckPos,
595 LanguageType& eLan,
596 bool bStan)
597 : rScan(*pSc)
598 , bAdditionalBuiltin( false )
599 , bStarFlag( false )
601 OUStringBuffer sBuff(rString);
603 // If the group (AKA thousand) separator is a No-Break Space (French)
604 // replace all occurrences by a simple space.
605 // The same for Narrow No-Break Space just in case some locale uses it.
606 // The tokens will be changed to the LocaleData separator again later on.
607 const OUString& rThSep = GetFormatter().GetNumThousandSep();
608 if ( rThSep.getLength() == 1)
610 const sal_Unicode cNBSp = 0xA0;
611 const sal_Unicode cNNBSp = 0x202F;
612 if (rThSep[0] == cNBSp )
613 sBuff.replace( cNBSp, ' ');
614 else if (rThSep[0] == cNNBSp )
615 sBuff.replace( cNNBSp, ' ');
618 if (rScan.GetConvertMode())
620 maLocale.meLanguage = rScan.GetNewLnge();
621 eLan = maLocale.meLanguage; // Make sure to return switch
623 else
625 maLocale.meLanguage = eLan;
627 bStandard = bStan;
628 bIsUsed = false;
629 fLimit1 = 0.0;
630 fLimit2 = 0.0;
631 eOp1 = NUMBERFORMAT_OP_NO;
632 eOp2 = NUMBERFORMAT_OP_NO;
633 eType = css::util::NumberFormat::DEFINED;
635 bool bCancel = false;
636 bool bCondition = false;
637 short eSymbolType;
638 sal_Int32 nPos = 0;
639 sal_Int32 nPosOld;
640 nCheckPos = 0;
642 // Split into 4 sub formats
643 sal_uInt16 nIndex;
644 for ( nIndex = 0; nIndex < 4 && !bCancel; nIndex++ )
646 // Original language/country may have to be reestablished
647 if (rScan.GetConvertMode())
649 (rScan.GetNumberformatter())->ChangeIntl(rScan.GetTmpLnge());
651 OUString sInsertCalendar; // a calendar resulting from parsing LCID
652 OUString sStr;
653 nPosOld = nPos; // Start position of substring
654 // first get bracketed prefixes; e.g. conditions, color
657 eSymbolType = ImpNextSymbol(sBuff, nPos, sStr);
658 if (eSymbolType > 0) // condition
660 if ( nIndex == 0 && !bCondition )
662 bCondition = true;
663 eOp1 = (SvNumberformatLimitOps) eSymbolType;
665 else if ( nIndex == 1 && bCondition )
667 eOp2 = (SvNumberformatLimitOps) eSymbolType;
669 else // error
671 bCancel = true; // break for
672 nCheckPos = nPosOld;
674 if (!bCancel)
676 double fNumber;
677 sal_Int32 nAnzChars = ImpGetNumber(sBuff, nPos, sStr);
678 if (nAnzChars > 0)
680 short F_Type = css::util::NumberFormat::UNDEFINED;
681 if (!pISc->IsNumberFormat(sStr,F_Type,fNumber) ||
682 ( F_Type != css::util::NumberFormat::NUMBER &&
683 F_Type != css::util::NumberFormat::SCIENTIFIC) )
685 fNumber = 0.0;
686 nPos = nPos - nAnzChars;
687 sBuff.remove(nPos, nAnzChars);
688 sBuff.insert(nPos, '0');
689 nPos++;
692 else
694 fNumber = 0.0;
695 sBuff.insert(nPos++, '0');
697 if (nIndex == 0)
699 fLimit1 = fNumber;
701 else
703 fLimit2 = fNumber;
705 if ( nPos < sBuff.getLength() && sBuff[nPos] == ']' )
707 nPos++;
709 else
711 bCancel = true; // break for
712 nCheckPos = nPos;
715 nPosOld = nPos; // position before string
717 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
719 OUString sSymbol( sStr);
720 switch ( eSymbolType )
722 case BRACKET_SYMBOLTYPE_COLOR :
723 if ( NumFor[nIndex].GetColor() != NULL )
724 { // error, more than one color
725 bCancel = true; // break for
726 nCheckPos = nPosOld;
728 else
730 Color* pColor = pSc->GetColor( sStr);
731 NumFor[nIndex].SetColor( pColor, sStr);
732 if (pColor == NULL)
733 { // error
734 bCancel = true; // break for
735 nCheckPos = nPosOld;
738 break;
739 case BRACKET_SYMBOLTYPE_NATNUM0 :
740 case BRACKET_SYMBOLTYPE_NATNUM1 :
741 case BRACKET_SYMBOLTYPE_NATNUM2 :
742 case BRACKET_SYMBOLTYPE_NATNUM3 :
743 case BRACKET_SYMBOLTYPE_NATNUM4 :
744 case BRACKET_SYMBOLTYPE_NATNUM5 :
745 case BRACKET_SYMBOLTYPE_NATNUM6 :
746 case BRACKET_SYMBOLTYPE_NATNUM7 :
747 case BRACKET_SYMBOLTYPE_NATNUM8 :
748 case BRACKET_SYMBOLTYPE_NATNUM9 :
749 case BRACKET_SYMBOLTYPE_NATNUM10 :
750 case BRACKET_SYMBOLTYPE_NATNUM11 :
751 case BRACKET_SYMBOLTYPE_NATNUM12 :
752 case BRACKET_SYMBOLTYPE_NATNUM13 :
753 case BRACKET_SYMBOLTYPE_NATNUM14 :
754 case BRACKET_SYMBOLTYPE_NATNUM15 :
755 case BRACKET_SYMBOLTYPE_NATNUM16 :
756 case BRACKET_SYMBOLTYPE_NATNUM17 :
757 case BRACKET_SYMBOLTYPE_NATNUM18 :
758 case BRACKET_SYMBOLTYPE_NATNUM19 :
759 if ( NumFor[nIndex].GetNatNum().IsSet() )
761 bCancel = true; // break for
762 nCheckPos = nPosOld;
764 else
766 sStr = "NatNum";
767 //! eSymbolType is negative
768 sal_uInt8 nNum = (sal_uInt8)(0 - (eSymbolType - BRACKET_SYMBOLTYPE_NATNUM0));
769 sStr += OUString::number( nNum );
770 NumFor[nIndex].SetNatNumNum( nNum, false );
772 break;
773 case BRACKET_SYMBOLTYPE_DBNUM1 :
774 case BRACKET_SYMBOLTYPE_DBNUM2 :
775 case BRACKET_SYMBOLTYPE_DBNUM3 :
776 case BRACKET_SYMBOLTYPE_DBNUM4 :
777 case BRACKET_SYMBOLTYPE_DBNUM5 :
778 case BRACKET_SYMBOLTYPE_DBNUM6 :
779 case BRACKET_SYMBOLTYPE_DBNUM7 :
780 case BRACKET_SYMBOLTYPE_DBNUM8 :
781 case BRACKET_SYMBOLTYPE_DBNUM9 :
782 if ( NumFor[nIndex].GetNatNum().IsSet() )
784 bCancel = true; // break for
785 nCheckPos = nPosOld;
787 else
789 sStr = "DBNum";
790 //! eSymbolType is negative
791 sal_uInt8 nNum = (sal_uInt8)(1 - (eSymbolType - BRACKET_SYMBOLTYPE_DBNUM1));
792 sStr += OUString((sal_Unicode)('0' + nNum));
793 NumFor[nIndex].SetNatNumNum( nNum, true );
795 break;
796 case BRACKET_SYMBOLTYPE_LOCALE :
797 if ( NumFor[nIndex].GetNatNum().GetLang() != LANGUAGE_DONTKNOW ||
798 sBuff[nPos-1] != ']' )
799 // Check also for ']' to avoid pulling in
800 // locale data for the preview string for not
801 // yet completed LCIDs in the dialog.
803 bCancel = true; // break for
804 nCheckPos = nPosOld;
806 else
808 sal_Int32 nTmp = 2;
809 LocaleType aTmpLocale( ImpGetLocaleType( sStr, nTmp));
810 if (aTmpLocale.meLanguage == LANGUAGE_DONTKNOW)
812 bCancel = true; // break for
813 nCheckPos = nPosOld;
815 else
817 // Only the first sub format's locale will be
818 // used as the format's overall locale.
819 // Sorts this also under the corresponding
820 // locale for the dialog.
821 // If we don't support the locale this would
822 // result in an unknown (empty) language
823 // listbox entry and the user would never see
824 // this format.
825 if (nIndex == 0 && (aTmpLocale.meLanguage == 0 ||
826 SvNumberFormatter::IsLocaleInstalled( aTmpLocale.meLanguage)))
828 maLocale = aTmpLocale;
829 eLan = aTmpLocale.meLanguage; // return to caller
830 /* TODO: fiddle with scanner to make this
831 * known? A change in the locale may affect
832 * separators and keywords. On the other
833 * hand they may have been entered as used
834 * in the originating locale, there's no
835 * way to predict other than analyzing the
836 * format code, we assume here the current
837 * context is used, which is most likely
838 * the case.
839 * */
841 sStr = "$-" + aTmpLocale.generateCode();
842 NumFor[nIndex].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale.meLanguage));
844 // "$-NNCCLLLL" Numerals and Calendar
845 if (sSymbol.getLength() > 6)
847 sInsertCalendar = ImpObtainCalendarAndNumerals( sBuff, nPos, eLan, aTmpLocale);
849 /* NOTE: there can be only one calendar
850 * inserted so the last one wins, though
851 * our own calendar modifiers support
852 * multiple calendars within one sub format
853 * code if at different positions. */
856 break;
858 if ( !bCancel )
860 if (sStr == sSymbol)
862 nPosOld = nPos;
864 else
866 sBuff.remove(nPosOld, nPos - nPosOld);
867 if (!sStr.isEmpty())
869 sBuff.insert(nPosOld, sStr);
870 nPos = nPosOld + sStr.getLength();
871 sBuff.insert(nPos, "]");
872 sBuff.insert(nPosOld, "[");
873 nPos += 2;
874 nPosOld = nPos; // position before string
876 else
878 nPos = nPosOld; // prefix removed for whatever reason
884 while ( !bCancel && lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) );
886 // The remaining format code string
887 if ( !bCancel )
889 if (eSymbolType == BRACKET_SYMBOLTYPE_FORMAT)
891 if (nIndex == 1 && eOp1 == NUMBERFORMAT_OP_NO)
893 eOp1 = NUMBERFORMAT_OP_GT; // undefined condition, default: > 0
895 else if (nIndex == 2 && eOp2 == NUMBERFORMAT_OP_NO)
897 eOp2 = NUMBERFORMAT_OP_LT; // undefined condition, default: < 0
899 if (sStr.isEmpty())
901 // empty sub format
903 else
905 if (!sInsertCalendar.isEmpty())
907 sStr = sInsertCalendar + sStr;
909 sal_Int32 nStrPos = pSc->ScanFormat( sStr);
910 sal_uInt16 nAnz = pSc->GetAnzResStrings();
911 if (nAnz == 0) // error
913 nStrPos = 1;
915 if (nStrPos == 0) // ok
917 // e.g. Thai T speciality
918 if (pSc->GetNatNumModifier() && !NumFor[nIndex].GetNatNum().IsSet())
920 sStr = "[NatNum" + OUString::number( pSc->GetNatNumModifier()) + "]" + sStr;
921 NumFor[nIndex].SetNatNumNum( pSc->GetNatNumModifier(), false );
923 // #i53826# #i42727# For the Thai T speciality we need
924 // to freeze the locale and immunize it against
925 // conversions during exports, just in case we want to
926 // save to Xcl. This disables the feature of being able
927 // to convert a NatNum to another locale. You can't
928 // have both.
929 // FIXME: implement a specialized export conversion
930 // that works on tokens (have to tokenize all first)
931 // and doesn't use the format string and
932 // PutandConvertEntry() to LANGUAGE_ENGLISH_US in
933 // sc/source/filter/excel/xestyle.cxx
934 // XclExpNumFmtBuffer::WriteFormatRecord().
935 LanguageType eLanguage;
936 if (NumFor[nIndex].GetNatNum().GetNatNum() == 1 &&
937 ((eLanguage = MsLangId::getRealLanguage( eLan)) == LANGUAGE_THAI) &&
938 NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW)
940 sStr = "[$-" + OUString::number( eLanguage, 16 ).toAsciiUpperCase() + "]" + sStr;
941 NumFor[nIndex].SetNatNumLang( eLanguage);
943 sBuff.remove(nPosOld, nPos - nPosOld);
944 sBuff.insert(nPosOld, sStr);
945 nPos = nPosOld + sStr.getLength();
946 if (nPos < sBuff.getLength())
948 sBuff.insert(nPos, ";");
949 nPos++;
951 NumFor[nIndex].Enlarge(nAnz);
952 pSc->CopyInfo(&(NumFor[nIndex].Info()), nAnz);
953 // type check
954 if (nIndex == 0)
956 eType = (short) NumFor[nIndex].Info().eScannedType;
958 else if (nIndex == 3)
959 { // #77026# Everything recognized IS text
960 NumFor[nIndex].Info().eScannedType = css::util::NumberFormat::TEXT;
962 else if ( (short) NumFor[nIndex].Info().eScannedType != eType)
964 eType = css::util::NumberFormat::DEFINED;
967 else
969 nCheckPos = nPosOld + nStrPos; // error in string
970 bCancel = true; // break for
974 else if (eSymbolType == BRACKET_SYMBOLTYPE_ERROR) // error
976 nCheckPos = nPosOld;
977 bCancel = true;
979 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
981 nCheckPos = nPosOld + 1; // error, prefix in string
982 bCancel = true; // break for
985 if ( bCancel && !nCheckPos )
987 nCheckPos = 1; // nCheckPos is used as an error condition
989 if ( !bCancel )
991 if ( NumFor[nIndex].GetNatNum().IsSet() &&
992 NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW )
994 NumFor[nIndex].SetNatNumLang( eLan );
997 if (sBuff.getLength() == nPos)
999 if ( nIndex == 2 && eSymbolType == BRACKET_SYMBOLTYPE_FORMAT &&
1000 sBuff[nPos - 1] == ';' )
1002 // #83510# A 4th subformat explicitly specified to be empty
1003 // hides any text. Need the type here for HasTextFormat()
1004 NumFor[3].Info().eScannedType = css::util::NumberFormat::TEXT;
1006 bCancel = true;
1008 if ( NumFor[nIndex].GetNatNum().IsSet() )
1010 NumFor[nIndex].SetNatNumDate( (NumFor[nIndex].Info().eScannedType & css::util::NumberFormat::DATE) != 0 );
1014 if ( bCondition && !nCheckPos )
1016 if ( nIndex == 1 && NumFor[0].GetCount() == 0 &&
1017 sBuff[sBuff.getLength() - 1] != ';' )
1019 // No format code => GENERAL but not if specified empty
1020 OUString aAdd( pSc->GetStandardName() );
1021 if ( !pSc->ScanFormat( aAdd ) )
1023 sal_uInt16 nAnz = pSc->GetAnzResStrings();
1024 if ( nAnz )
1026 NumFor[0].Enlarge(nAnz);
1027 pSc->CopyInfo( &(NumFor[0].Info()), nAnz );
1028 sBuff.append(aAdd);
1032 else if ( nIndex == 1 && NumFor[nIndex].GetCount() == 0 &&
1033 sBuff[sBuff.getLength() - 1] != ';' &&
1034 (NumFor[0].GetCount() > 1 ||
1035 (NumFor[0].GetCount() == 1 &&
1036 NumFor[0].Info().nTypeArray[0] != NF_KEY_GENERAL)) )
1038 // No trailing second subformat => GENERAL but not if specified empty
1039 // and not if first subformat is GENERAL
1040 OUString aAdd( pSc->GetStandardName() );
1041 if ( !pSc->ScanFormat( aAdd ) )
1043 sal_uInt16 nAnz = pSc->GetAnzResStrings();
1044 if ( nAnz )
1046 NumFor[nIndex].Enlarge(nAnz);
1047 pSc->CopyInfo( &(NumFor[nIndex].Info()), nAnz );
1048 sBuff.append(";");
1049 sBuff.append(aAdd);
1053 else if ( nIndex == 2 && NumFor[nIndex].GetCount() == 0 &&
1054 sBuff[sBuff.getLength() - 1] != ';' &&
1055 eOp2 != NUMBERFORMAT_OP_NO )
1057 // No trailing third subformat => GENERAL but not if specified empty
1058 OUString aAdd( pSc->GetStandardName() );
1059 if ( !pSc->ScanFormat( aAdd ) )
1061 sal_uInt16 nAnz = pSc->GetAnzResStrings();
1062 if ( nAnz )
1064 NumFor[nIndex].Enlarge(nAnz);
1065 pSc->CopyInfo( &(NumFor[nIndex].Info()), nAnz );
1066 sBuff.append(";");
1067 sBuff.append(aAdd);
1072 rString = sBuff.makeStringAndClear();
1073 sFormatstring = rString;
1075 if (NumFor[2].GetCount() == 0 && // No third partial string
1076 eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_NO &&
1077 fLimit1 == 0.0 && fLimit2 == 0.0)
1079 eOp1 = NUMBERFORMAT_OP_GE; // Add 0 to the first format
1084 SvNumberformat::~SvNumberformat()
1089 * Next_Symbol
1091 * Splits up the symbols for further processing (by the Turing machine)
1093 * Start state = SsStart, * = Special state
1094 * ---------------+-------------------+----------------------------+---------------
1095 * Old State | Symbol read | Event | New state
1096 * ---------------+-------------------+----------------------------+---------------
1097 * SsStart | ; | Pos-- | SsGetString
1098 * | [ | Symbol += Character | SsGetBracketed
1099 * | ] | Error | SsStop
1100 * | BLANK | |
1101 * | Else | Symbol += Character | SsGetString
1102 * ---------------+-------------------+----------------------------+---------------
1103 * SsGetString | ; | | SsStop
1104 * | Else | Symbol += Character |
1105 * ---------------+-------------------+----------------------------+---------------
1106 * SsGetBracketed | <, > = | del [ |
1107 * | | Symbol += Character | SsGetCon
1108 * | BLANK | |
1109 * | h, H, m, M, s, S | Symbol += Character | SsGetTime
1110 * | Else | del [ |
1111 * | | Symbol += Character | SsGetPrefix
1112 * ---------------+-------------------+----------------------------+---------------
1113 * SsGetTime | ] | Symbol += Character | SsGetString
1114 * | h, H, m, M, s, S | Symbol += Character, * | SsGetString
1115 * | Else | del [; Symbol += Character | SsGetPrefix
1116 * ---------------+-------------------+----------------------------+---------------
1117 * SsGetPrefix | ] | | SsStop
1118 * | Else | Symbol += Character |
1119 * ---------------+-------------------+----------------------------+---------------
1120 * SsGetCon | >, = | Symbol += Character |
1121 * | ] | | SsStop
1122 * | Else | Error | SsStop
1123 * ---------------+-------------------+----------------------------+---------------
1126 enum ScanState
1128 SsStop,
1129 SsStart,
1130 SsGetCon, // condition
1131 SsGetString, // format string
1132 SsGetPrefix, // color or NatNumN
1133 SsGetTime, // [HH] for time
1134 SsGetBracketed // any [...] not decided yet
1137 // read a string until ']' and delete spaces in input
1138 // static
1139 sal_Int32 SvNumberformat::ImpGetNumber(OUStringBuffer& rString,
1140 sal_Int32& nPos,
1141 OUString& sSymbol)
1143 sal_Int32 nStartPos = nPos;
1144 sal_Unicode cToken;
1145 sal_Int32 nLen = rString.getLength();
1146 OUStringBuffer sBuffSymbol;
1147 while ( nPos < nLen && ((cToken = rString[nPos]) != ']') )
1149 if (cToken == ' ')
1150 { // delete spaces
1151 rString.remove(nPos,1);
1152 nLen--;
1154 else
1156 nPos++;
1157 sBuffSymbol.append(cToken);
1160 sSymbol = sBuffSymbol.makeStringAndClear();
1161 return nPos - nStartPos;
1164 namespace {
1166 sal_Unicode toUniChar(sal_uInt8 n)
1168 sal_Char c;
1169 if (n < 10)
1171 c = '0' + n;
1173 else
1175 c = 'A' + n - 10;
1177 return sal_Unicode(c);
1180 bool IsCombiningSymbol( OUStringBuffer& rStringBuffer, sal_Int32 nPos )
1182 bool bRet = false;
1183 while (nPos >= 0)
1185 switch (rStringBuffer[nPos])
1187 case '*':
1188 case '\\':
1189 case '_':
1190 bRet = !bRet;
1191 --nPos;
1192 break;
1193 default:
1194 return bRet;
1197 return bRet;
1200 } // namespace
1202 OUString SvNumberformat::LocaleType::generateCode() const
1204 OUStringBuffer aBuf;
1205 #if 0
1206 // TODO: We may re-enable this later. Don't remove it! --Kohei
1207 if (mnNumeralShape)
1209 sal_uInt8 nVal = mnNumeralShape;
1210 for (sal_uInt8 i = 0; i < 2; ++i)
1212 sal_uInt8 n = (nVal & 0xF0) >> 4;
1213 if (n || aBuf.getLength())
1215 aBuf.append(toUniChar(n));
1217 nVal = nVal << 4;
1221 if (mnNumeralShape || mnCalendarType)
1223 sal_uInt8 nVal = mnCalendarType;
1224 for (sal_uInt8 i = 0; i < 2; ++i)
1226 sal_uInt8 n = (nVal & 0xF0) >> 4;
1227 if (n || aBuf.getLength())
1229 aBuf.append(toUniChar(n));
1231 nVal = nVal << 4;
1234 #endif
1236 sal_uInt16 n16 = static_cast<sal_uInt16>(meLanguage);
1237 for (sal_uInt8 i = 0; i < 4; ++i)
1239 sal_uInt8 n = static_cast<sal_uInt8>((n16 & 0xF000) >> 12);
1240 // Omit leading zeros for consistency.
1241 if (n || !aBuf.isEmpty() || i == 3)
1243 aBuf.append(toUniChar(n));
1245 n16 = n16 << 4;
1248 return aBuf.makeStringAndClear();
1251 SvNumberformat::LocaleType::LocaleType()
1252 : mnNumeralShape(0)
1253 , mnCalendarType(0)
1254 , meLanguage(LANGUAGE_DONTKNOW)
1258 SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum)
1259 : mnNumeralShape(0)
1260 , mnCalendarType(0)
1261 , meLanguage(LANGUAGE_DONTKNOW)
1263 meLanguage = static_cast<LanguageType>(nRawNum & 0x0000FFFF);
1264 nRawNum = (nRawNum >> 16);
1265 mnCalendarType = static_cast<sal_uInt8>(nRawNum & 0xFF);
1266 nRawNum = (nRawNum >> 8);
1267 mnNumeralShape = static_cast<sal_uInt8>(nRawNum & 0xFF);
1270 // static
1271 SvNumberformat::LocaleType SvNumberformat::ImpGetLocaleType(const OUString& rString, sal_Int32& nPos )
1273 sal_uInt32 nNum = 0;
1274 sal_Unicode cToken = 0;
1275 sal_Int32 nStart = nPos;
1276 sal_Int32 nLen = rString.getLength();
1277 while ( nPos < nLen && (nPos - nStart < 8) && ((cToken = rString[nPos]) != ']') )
1279 if ( '0' <= cToken && cToken <= '9' )
1281 nNum *= 16;
1282 nNum += cToken - '0';
1284 else if ( 'a' <= cToken && cToken <= 'f' )
1286 nNum *= 16;
1287 nNum += cToken - 'a' + 10;
1289 else if ( 'A' <= cToken && cToken <= 'F' )
1291 nNum *= 16;
1292 nNum += cToken - 'A' + 10;
1294 else
1296 return LANGUAGE_DONTKNOW;
1298 ++nPos;
1301 return (cToken == ']' || nPos == nLen) ? LocaleType(nNum) : LocaleType();
1304 static bool lcl_matchKeywordAndGetNumber( const OUString & rString, const sal_Int32 nPos,
1305 const OUString & rKeyword, sal_Int32 & nNumber )
1307 if (0 <= nPos && nPos + rKeyword.getLength() < rString.getLength() && rString.matchIgnoreAsciiCase( rKeyword, nPos))
1309 nNumber = rString.copy( nPos + rKeyword.getLength()).toInt32();
1310 return true;
1312 else
1314 nNumber = 0;
1315 return false;
1319 short SvNumberformat::ImpNextSymbol(OUStringBuffer& rString,
1320 sal_Int32& nPos,
1321 OUString& sSymbol)
1323 short eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1324 sal_Unicode cToken;
1325 sal_Unicode cLetter = ' '; // Preliminary result
1326 sal_Int32 nLen = rString.getLength();
1327 ScanState eState = SsStart;
1328 OUStringBuffer sBuffSymbol;
1330 const NfKeywordTable & rKeywords = rScan.GetKeywords();
1331 while (nPos < nLen && eState != SsStop)
1333 cToken = rString[nPos];
1334 nPos++;
1335 switch (eState)
1337 case SsStart:
1338 if (cToken == '[')
1340 eState = SsGetBracketed;
1341 sBuffSymbol.append(cToken);
1343 else if (cToken == ';')
1345 eState = SsGetString;
1346 nPos--;
1347 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1349 else if (cToken == ']')
1351 eState = SsStop;
1352 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1354 else if (cToken == ' ') // Skip Blanks
1356 nPos--;
1357 rString.remove(nPos, 1);
1358 nLen--;
1360 else
1362 sBuffSymbol.append(cToken);
1363 eState = SsGetString;
1364 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1366 break;
1367 case SsGetBracketed:
1368 switch (cToken)
1370 case '<':
1371 case '>':
1372 case '=':
1373 sBuffSymbol.stripStart('[');
1374 sBuffSymbol.append(cToken);
1375 cLetter = cToken;
1376 eState = SsGetCon;
1377 switch (cToken)
1379 case '<':
1380 eSymbolType = NUMBERFORMAT_OP_LT;
1381 break;
1382 case '>':
1383 eSymbolType = NUMBERFORMAT_OP_GT;
1384 break;
1385 case '=':
1386 eSymbolType = NUMBERFORMAT_OP_EQ;
1387 break;
1389 break;
1390 case ' ':
1391 nPos--;
1392 rString.remove(nPos, 1);
1393 nLen--;
1394 break;
1395 case '$' :
1396 if ( rString[nPos] == '-' )
1398 // [$-xxx] locale
1399 sBuffSymbol.stripStart('[');
1400 eSymbolType = BRACKET_SYMBOLTYPE_LOCALE;
1401 eState = SsGetPrefix;
1403 else
1404 { // currency
1405 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1406 eState = SsGetString;
1408 sBuffSymbol.append(cToken);
1409 break;
1410 case '~' :
1411 // calendarID
1412 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1413 sBuffSymbol.append(cToken);
1414 eState = SsGetString;
1415 break;
1416 default:
1418 const OUString aNatNum("NATNUM");
1419 const OUString aDBNum("DBNUM");
1420 const OUString aBufStr( rString.toString());
1421 sal_Int32 nNatNumNum;
1422 sal_Int32 nDBNum;
1423 if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aNatNum, nNatNumNum) &&
1424 0 <= nNatNumNum && nNatNumNum <= 19 )
1426 sBuffSymbol.stripStart('[');
1427 sBuffSymbol.append( aBufStr.copy( --nPos, aNatNum.getLength()+1 ));
1428 nPos += aNatNum.getLength()+1;
1429 //! SymbolType is negative
1430 eSymbolType = (short) (BRACKET_SYMBOLTYPE_NATNUM0 - nNatNumNum);
1431 eState = SsGetPrefix;
1433 else if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aDBNum, nDBNum) &&
1434 '1' <= nDBNum && nDBNum <= '9' )
1436 sBuffSymbol.stripStart('[');
1437 sBuffSymbol.append( aBufStr.copy( --nPos, aDBNum.getLength()+1 ));
1438 nPos += aDBNum.getLength()+1;
1439 //! SymbolType is negative
1440 eSymbolType = sal::static_int_cast< short >( BRACKET_SYMBOLTYPE_DBNUM1 - (nDBNum - '1'));
1441 eState = SsGetPrefix;
1443 else
1445 sal_Unicode cUpper = rChrCls().uppercase( aBufStr, nPos-1, 1)[0];
1446 if ( cUpper == rKeywords[NF_KEY_H][0] || // H
1447 cUpper == rKeywords[NF_KEY_MI][0] || // M
1448 cUpper == rKeywords[NF_KEY_S][0] ) // S
1450 sBuffSymbol.append(cToken);
1451 eState = SsGetTime;
1452 cLetter = cToken;
1454 else
1456 sBuffSymbol.stripStart('[');
1457 sBuffSymbol.append(cToken);
1458 eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1459 eState = SsGetPrefix;
1464 break;
1465 case SsGetString:
1466 if (cToken == ';' && (nPos < 2 || !IsCombiningSymbol( rString, nPos-2)))
1468 eState = SsStop;
1470 else
1472 sBuffSymbol.append(cToken);
1474 break;
1475 case SsGetTime:
1476 if (cToken == ']')
1478 sBuffSymbol.append(cToken);
1479 eState = SsGetString;
1480 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1482 else
1484 sal_Unicode cUpper = rChrCls().uppercase(rString.toString(), nPos-1, 1)[0];
1485 if (cUpper == rKeywords[NF_KEY_H][0] || // H
1486 cUpper == rKeywords[NF_KEY_MI][0] || // M
1487 cUpper == rKeywords[NF_KEY_S][0] ) // S
1489 if (cLetter == cToken)
1491 sBuffSymbol.append(cToken);
1492 cLetter = ' ';
1494 else
1496 sBuffSymbol.stripStart('[');
1497 sBuffSymbol.append(cToken);
1498 eState = SsGetPrefix;
1501 else
1503 sBuffSymbol.stripStart('[');
1504 sBuffSymbol.append(cToken);
1505 eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1506 eState = SsGetPrefix;
1509 break;
1510 case SsGetCon:
1511 switch (cToken)
1513 case '<':
1514 eState = SsStop;
1515 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1516 break;
1517 case '>':
1518 if (cLetter == '<')
1520 sBuffSymbol.append(cToken);
1521 cLetter = ' ';
1522 eState = SsStop;
1523 eSymbolType = NUMBERFORMAT_OP_NE;
1525 else
1527 eState = SsStop;
1528 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1530 break;
1531 case '=':
1532 if (cLetter == '<')
1534 sBuffSymbol.append(cToken);
1535 cLetter = ' ';
1536 eSymbolType = NUMBERFORMAT_OP_LE;
1538 else if (cLetter == '>')
1540 sBuffSymbol.append(cToken);
1541 cLetter = ' ';
1542 eSymbolType = NUMBERFORMAT_OP_GE;
1544 else
1546 eState = SsStop;
1547 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1549 break;
1550 case ' ':
1551 nPos--;
1552 rString.remove(nPos,1);
1553 nLen--;
1554 break;
1555 default:
1556 eState = SsStop;
1557 nPos--;
1558 break;
1560 break;
1561 case SsGetPrefix:
1562 if (cToken == ']')
1564 eState = SsStop;
1566 else
1568 sBuffSymbol.append(cToken);
1570 break;
1571 default:
1572 break;
1573 } // of switch
1574 } // of while
1575 sSymbol = sBuffSymbol.makeStringAndClear();
1576 return eSymbolType;
1579 void SvNumberformat::ConvertLanguage( SvNumberFormatter& rConverter,
1580 LanguageType eConvertFrom,
1581 LanguageType eConvertTo, bool bSystem )
1583 sal_Int32 nCheckPos;
1584 sal_uInt32 nKey;
1585 short nType = eType;
1586 OUString aFormatString( sFormatstring );
1587 if ( bSystem )
1589 rConverter.PutandConvertEntrySystem( aFormatString, nCheckPos, nType,
1590 nKey, eConvertFrom, eConvertTo );
1592 else
1594 rConverter.PutandConvertEntry( aFormatString, nCheckPos, nType,
1595 nKey, eConvertFrom, eConvertTo );
1597 const SvNumberformat* pFormat = rConverter.GetEntry( nKey );
1598 DBG_ASSERT( pFormat, "SvNumberformat::ConvertLanguage: Conversion ohne Format" );
1599 if ( pFormat )
1601 ImpCopyNumberformat( *pFormat );
1602 // Reset values taken over from Formatter/Scanner
1603 if ( bSystem )
1605 maLocale.meLanguage = LANGUAGE_SYSTEM;
1607 // pColor still points to table in temporary Formatter/Scanner
1608 for ( sal_uInt16 i = 0; i < 4; i++ )
1610 OUString aColorName = NumFor[i].GetColorName();
1611 Color* pColor = rScan.GetColor( aColorName );
1612 NumFor[i].SetColor( pColor, aColorName );
1617 bool SvNumberformat::HasNewCurrency() const
1619 for ( sal_uInt16 j=0; j<4; j++ )
1621 if ( NumFor[j].HasNewCurrency() )
1623 return true;
1626 return false;
1629 bool SvNumberformat::GetNewCurrencySymbol( OUString& rSymbol,
1630 OUString& rExtension ) const
1632 for ( sal_uInt16 j=0; j<4; j++ )
1634 if ( NumFor[j].GetNewCurrencySymbol( rSymbol, rExtension ) )
1636 return true;
1639 rSymbol.clear();
1640 rExtension.clear();
1641 return false;
1644 // static
1645 OUString SvNumberformat::StripNewCurrencyDelimiters( const OUString& rStr,
1646 bool bQuoteSymbol )
1648 OUString aTmp;
1649 OUString aSource(rStr);
1650 sal_Int32 nStartPos, nPos, nLen;
1651 nLen = aSource.getLength();
1652 nStartPos = 0;
1653 while ( (nPos = aSource.indexOf( "[$", nStartPos )) >= 0 )
1655 sal_Int32 nEnd;
1656 if ( (nEnd = GetQuoteEnd( aSource, nPos )) >= 0 )
1658 aTmp += aSource.copy( nStartPos, ++nEnd - nStartPos );
1659 nStartPos = nEnd;
1661 else
1663 aTmp += aSource.copy( nStartPos, nPos - nStartPos );
1664 nStartPos = nPos + 2;
1665 sal_Int32 nDash;
1666 nEnd = nStartPos - 1;
1669 nDash = aSource.indexOf( '-', ++nEnd );
1671 while ( (nEnd = GetQuoteEnd( aSource, nDash )) >= 0 );
1672 sal_Int32 nClose;
1673 nEnd = nStartPos - 1;
1676 nClose = aSource.indexOf( ']', ++nEnd );
1678 while ( (nEnd = GetQuoteEnd( aSource, nClose )) >= 0 );
1680 if(nClose < 0)
1682 /* there should always be a closing ]
1683 * but the old String class would have hidden
1684 * that. so be conservative too
1686 nClose = nLen;
1689 nPos = nClose;
1690 if(nDash >= 0 && nDash < nClose)
1692 nPos = nDash;
1694 if ( !bQuoteSymbol || aSource[ nStartPos ] == '"' )
1696 aTmp += aSource.copy( nStartPos, nPos - nStartPos );
1698 else
1700 aTmp += "\"";
1701 aTmp += aSource.copy( nStartPos, nPos - nStartPos );
1702 aTmp += "\"";
1704 nStartPos = nClose + 1;
1707 if ( nLen > nStartPos )
1709 aTmp += aSource.copy( nStartPos, nLen - nStartPos );
1711 return aTmp;
1714 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUStringBuffer& rOutString)
1716 OUString sTemp;
1717 ImpGetOutputStandard(fNumber, sTemp);
1718 rOutString = sTemp;
1721 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUString& rOutString)
1723 sal_uInt16 nStandardPrec = rScan.GetStandardPrec();
1725 if ( fabs(fNumber) > EXP_ABS_UPPER_BOUND )
1727 nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals
1728 rOutString = ::rtl::math::doubleToUString( fNumber,
1729 rtl_math_StringFormat_E2, nStandardPrec /*2*/,
1730 GetFormatter().GetNumDecimalSep()[0]);
1732 else
1734 ImpGetOutputStdToPrecision(fNumber, rOutString, nStandardPrec);
1738 void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber, OUString& rOutString, sal_uInt16 nPrecision) const
1740 // Make sure the precision doesn't go over the maximum allowable precision.
1741 nPrecision = ::std::min(UPPER_PRECISION, nPrecision);
1743 #if 0
1745 // debugger test case for ANSI standard correctness
1746 OUString aTest;
1747 // expect 0.00123 OK
1748 aTest = ::rtl::math::doubleToUString( 0.001234567,
1749 rtl_math_StringFormat_G, 3, '.', true );
1750 // expect 123 OK
1751 aTest = ::rtl::math::doubleToUString( 123.4567,
1752 rtl_math_StringFormat_G, 3, '.', true );
1753 // expect 123.5 OK
1754 aTest = ::rtl::math::doubleToUString( 123.4567,
1755 rtl_math_StringFormat_G, 4, '.', true );
1756 // expect 1e+03 (as 999.6 rounded to 3 significant digits results in
1757 // 1000 with an exponent equal to significant digits)
1758 // Currently (24-Jan-2003) we do fail in this case and output 1000
1759 // instead, negligible.
1760 aTest = ::rtl::math::doubleToUString( 999.6,
1761 rtl_math_StringFormat_G, 3, '.', true );
1762 // expect what? result is 1.2e+004
1763 aTest = ::rtl::math::doubleToUString( 12345.6789,
1764 rtl_math_StringFormat_G, -3, '.', true );
1766 #endif
1768 // We decided to strip trailing zeros unconditionally, since binary
1769 // double-precision rounding error makes it impossible to determine e.g.
1770 // whether 844.10000000000002273737 is what the user has typed, or the
1771 // user has typed 844.1 but IEEE 754 represents it that way internally.
1773 rOutString = ::rtl::math::doubleToUString( rNumber,
1774 rtl_math_StringFormat_F, nPrecision /*2*/,
1775 GetFormatter().GetNumDecimalSep()[0], true );
1776 if (rOutString[0] == '-' &&
1777 comphelper::string::getTokenCount(rOutString, '0') == rOutString.getLength())
1779 rOutString = comphelper::string::stripStart(rOutString, '-'); // not -0
1781 rOutString = impTransliterate(rOutString, NumFor[0].GetNatNum());
1784 void SvNumberformat::ImpGetOutputInputLine(double fNumber, OUString& OutString)
1786 bool bModified = false;
1787 if ( (eType & css::util::NumberFormat::PERCENT) && (fabs(fNumber) < _D_MAX_D_BY_100))
1789 if (fNumber == 0.0)
1791 OutString = "0%";
1792 return;
1794 fNumber *= 100;
1795 bModified = true;
1798 if (fNumber == 0.0)
1800 OutString = "0";
1801 return;
1804 OutString = ::rtl::math::doubleToUString( fNumber,
1805 rtl_math_StringFormat_Automatic,
1806 rtl_math_DecimalPlaces_Max,
1807 GetFormatter().GetNumDecimalSep()[0], true );
1809 if ( eType & css::util::NumberFormat::PERCENT && bModified)
1811 OutString += "%";
1813 return;
1816 short SvNumberformat::ImpCheckCondition(double& fNumber,
1817 double& fLimit,
1818 SvNumberformatLimitOps eOp)
1820 switch(eOp)
1822 case NUMBERFORMAT_OP_NO:
1823 return -1;
1824 case NUMBERFORMAT_OP_EQ:
1825 return (short) (fNumber == fLimit);
1826 case NUMBERFORMAT_OP_NE:
1827 return (short) (fNumber != fLimit);
1828 case NUMBERFORMAT_OP_LT:
1829 return (short) (fNumber < fLimit);
1830 case NUMBERFORMAT_OP_LE:
1831 return (short) (fNumber <= fLimit);
1832 case NUMBERFORMAT_OP_GT:
1833 return (short) (fNumber > fLimit);
1834 case NUMBERFORMAT_OP_GE:
1835 return (short) (fNumber >= fLimit);
1836 default:
1837 return -1;
1841 static bool lcl_appendStarFillChar( OUStringBuffer& rBuf, const OUString& rStr )
1843 // Right during user input the star symbol is the very
1844 // last character before the user enters another one.
1845 if (rStr.getLength() > 1)
1847 rBuf.append((sal_Unicode) 0x1B);
1848 rBuf.append(rStr[1]);
1849 return true;
1851 return false;
1854 static bool lcl_insertStarFillChar( OUStringBuffer& rBuf, sal_Int32 nPos, const OUString& rStr )
1856 if (rStr.getLength() > 1)
1858 rBuf.insert( nPos, rStr[1]);
1859 rBuf.insert( nPos, (sal_Unicode) 0x1B);
1860 return true;
1862 return false;
1865 bool SvNumberformat::GetOutputString(const OUString& sString,
1866 OUString& OutString,
1867 Color** ppColor)
1869 OUStringBuffer sOutBuff;
1870 sal_uInt16 nIx;
1871 if (eType & css::util::NumberFormat::TEXT)
1873 nIx = 0;
1875 else if (NumFor[3].GetCount() > 0)
1877 nIx = 3;
1879 else
1881 *ppColor = NULL; // no change of color
1882 return false;
1884 *ppColor = NumFor[nIx].GetColor();
1885 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
1886 bool bRes = false;
1887 if (rInfo.eScannedType == css::util::NumberFormat::TEXT)
1889 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
1890 for (sal_uInt16 i = 0; i < nAnz; i++)
1892 switch (rInfo.nTypeArray[i])
1894 case NF_SYMBOLTYPE_STAR:
1895 if( bStarFlag )
1897 bRes = lcl_appendStarFillChar( sOutBuff, rInfo.sStrArray[i]);
1899 break;
1900 case NF_SYMBOLTYPE_BLANK:
1901 InsertBlanks( sOutBuff, sOutBuff.getLength(),
1902 rInfo.sStrArray[i][1] );
1903 break;
1904 case NF_KEY_GENERAL : // #77026# "General" is the same as "@"
1905 case NF_SYMBOLTYPE_DEL :
1906 sOutBuff.append(sString);
1907 break;
1908 default:
1909 sOutBuff.append(rInfo.sStrArray[i]);
1913 OutString = sOutBuff.makeStringAndClear();
1914 return bRes;
1917 sal_uLong SvNumberformat::ImpGGT(sal_uLong x, sal_uLong y)
1919 if (y == 0)
1921 return x;
1923 else
1925 sal_uLong z = x%y;
1926 while (z)
1928 x = y;
1929 y = z;
1930 z = x%y;
1932 return y;
1936 sal_uLong SvNumberformat::ImpGGTRound(sal_uLong x, sal_uLong y)
1938 if (y == 0)
1940 return x;
1942 else
1944 sal_uLong z = x%y;
1945 while ((double)z/(double)y > D_EPS)
1947 x = y;
1948 y = z;
1949 z = x%y;
1951 return y;
1955 namespace {
1957 void lcl_GetOutputStringScientific(double fNumber, sal_uInt16 nCharCount,
1958 const SvNumberFormatter& rFormatter, OUString& rOutString)
1960 bool bSign = ::rtl::math::isSignBitSet(fNumber);
1962 // 1.000E+015 (one digit and the decimal point, and the two chars +
1963 // nExpDigit for the exponential part, totalling 6 or 7).
1964 double fExp = log10( fabs(fNumber) );
1965 if( fExp < 0.0 )
1966 fExp = 1.0 - fExp;
1967 sal_uInt16 nCharFormat = 6 + (fExp >= 100.0 ? 1 : 0);
1968 sal_uInt16 nPrec = nCharCount > nCharFormat ? nCharCount - nCharFormat : 0;
1969 if (nPrec && bSign)
1971 // Make room for the negative sign.
1972 --nPrec;
1974 nPrec = ::std::min(nPrec, static_cast<sal_uInt16>(14)); // limit to 14 decimals.
1976 rOutString = ::rtl::math::doubleToUString(fNumber, rtl_math_StringFormat_E2,
1977 nPrec, rFormatter.GetNumDecimalSep()[0], true );
1980 sal_Int32 lcl_GetForcedDenominator(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nAnz)
1982 sal_uInt16 i;
1983 OUString aDiv;
1984 for( i = 0; i < nAnz; i++ )
1986 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC_FDIV )
1988 aDiv += rInfo.sStrArray[i];
1991 return aDiv.toInt32();
1994 // TODO: More optimizations?
1995 void lcl_ForcedDenominator(sal_uLong &nFrac, sal_uLong &nDiv, sal_uLong nForcedDiv)
1997 double fFrac = (double)nFrac / (double)nDiv;
1998 double fMultiplier = (double)nForcedDiv / (double)nDiv;
1999 nFrac = (sal_uLong)( (double)nFrac * fMultiplier );
2001 double fFracNew = (double)nFrac / (double)nForcedDiv;
2002 double fFracNew1 = (double)(nFrac + 1) / (double)nForcedDiv;
2003 double fDiff = fFrac - fFracNew;
2004 if( fDiff > ( fFracNew1 - fFrac ) )
2006 nFrac++;
2008 nDiv = nForcedDiv;
2013 sal_Int32 SvNumberformat::GetForcedDenominatorForType( sal_uInt16 nNumFor ) const
2015 const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2016 sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
2017 return lcl_GetForcedDenominator( rInfo, nAnz );
2020 bool SvNumberformat::GetOutputString(double fNumber, sal_uInt16 nCharCount, OUString& rOutString) const
2022 using namespace std;
2024 if (eType != css::util::NumberFormat::NUMBER)
2026 return false;
2028 double fTestNum = fNumber;
2029 bool bSign = ::rtl::math::isSignBitSet(fTestNum);
2030 if (bSign)
2032 fTestNum = -fTestNum;
2034 if (fTestNum < EXP_LOWER_BOUND)
2036 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2037 return true;
2040 double fExp = log10(fTestNum);
2041 // Values < 1.0 always have one digit before the decimal point.
2042 sal_uInt16 nDigitPre = fExp >= 0.0 ? static_cast<sal_uInt16>(ceil(fExp)) : 1;
2044 if (nDigitPre > 15)
2046 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2047 return true;
2050 sal_uInt16 nPrec = nCharCount >= nDigitPre ? nCharCount - nDigitPre : 0;
2051 if (nPrec && bSign)
2053 // Subtract the negative sign.
2054 --nPrec;
2056 if (nPrec)
2058 // Subtract the decimal point.
2059 --nPrec;
2061 ImpGetOutputStdToPrecision(fNumber, rOutString, nPrec);
2062 if (rOutString.getLength() > nCharCount)
2064 // String still wider than desired. Switch to scientific notation.
2065 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2067 return true;
2070 bool SvNumberformat::GetOutputString(double fNumber,
2071 OUString& OutString,
2072 Color** ppColor)
2074 bool bRes = false;
2075 OUStringBuffer sBuff;
2076 OutString.clear();
2077 *ppColor = NULL; // No color change
2078 if (eType & css::util::NumberFormat::LOGICAL)
2080 if (fNumber)
2082 OutString = rScan.GetTrueString();
2084 else
2086 OutString = rScan.GetFalseString();
2088 return false;
2090 if (eType & css::util::NumberFormat::TEXT)
2092 ImpGetOutputStandard(fNumber, sBuff);
2093 OutString = sBuff.makeStringAndClear();
2094 return false;
2096 bool bHadStandard = false;
2097 if (bStandard) // Individual standard formats
2099 if (rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION) // All number format InputLine
2101 ImpGetOutputInputLine(fNumber, OutString);
2102 return false;
2104 switch (eType)
2106 case css::util::NumberFormat::NUMBER: // Standard number format
2107 if (rScan.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION)
2109 bool bSign = ::rtl::math::isSignBitSet(fNumber);
2110 if (bSign)
2112 if (!(fNumber < 0.0))
2114 bSign = false;
2116 fNumber = -fNumber;
2118 /* TODO: why did we insist on 10 decimals for the non-exponent
2119 * case? doubleToUString() handles rtl_math_DecimalPlaces_Max
2120 * gracefully when used with rtl_math_StringFormat_Automatic,
2121 * so all that special casing and mumbo-jumbo in the else
2122 * branch below might not be needed at all. */
2123 if (fNumber > EXP_ABS_UPPER_BOUND)
2125 sBuff.append( ::rtl::math::doubleToUString( fNumber,
2126 rtl_math_StringFormat_Automatic,
2127 rtl_math_DecimalPlaces_Max,
2128 GetFormatter().GetNumDecimalSep()[0], true));
2130 else
2132 OUString sTemp;
2133 ImpGetOutputStdToPrecision(fNumber, sTemp, 10); // Use 10 decimals for general 'unlimited' format.
2134 sBuff.append(sTemp);
2135 if (fNumber < EXP_LOWER_BOUND)
2137 sal_Int32 nLen = sBuff.getLength();
2138 if (!nLen)
2140 return false;
2142 // #i112250# With the 10-decimal limit, small numbers are formatted as "0".
2143 // Switch to scientific in that case, too:
2144 if (nLen > 11 || ((nLen == 1 && sBuff[0] == '0') && fNumber != 0.0))
2146 sal_uInt16 nStandardPrec = rScan.GetStandardPrec();
2147 nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals
2148 sBuff = ::rtl::math::doubleToUString( fNumber,
2149 rtl_math_StringFormat_E2, nStandardPrec /*2*/,
2150 GetFormatter().GetNumDecimalSep()[0], true);
2154 if (bSign)
2156 sBuff.insert(0, '-');
2158 OutString = sBuff.makeStringAndClear();
2159 return false;
2161 ImpGetOutputStandard(fNumber, sBuff);
2162 bHadStandard = true;
2163 break;
2164 case css::util::NumberFormat::DATE:
2165 bRes |= ImpGetDateOutput(fNumber, 0, sBuff);
2166 bHadStandard = true;
2167 break;
2168 case css::util::NumberFormat::TIME:
2169 bRes |= ImpGetTimeOutput(fNumber, 0, sBuff);
2170 bHadStandard = true;
2171 break;
2172 case css::util::NumberFormat::DATETIME:
2173 bRes |= ImpGetDateTimeOutput(fNumber, 0, sBuff);
2174 bHadStandard = true;
2175 break;
2178 if ( !bHadStandard )
2180 sal_uInt16 nIx; // Index of the partial format
2181 short nCheck = ImpCheckCondition(fNumber, fLimit1, eOp1);
2182 if (nCheck == -1 || nCheck == 1) // Only 1 String or True
2184 nIx = 0;
2186 else
2188 nCheck = ImpCheckCondition(fNumber, fLimit2, eOp2);
2189 if (nCheck == -1 || nCheck == 1)
2191 nIx = 1;
2193 else
2195 nIx = 2;
2198 if (fNumber < 0.0 &&
2199 ((nIx == 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat
2200 (nIx == 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat
2202 fNumber = -fNumber; // eliminate sign
2204 *ppColor = NumFor[nIx].GetColor();
2205 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2206 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
2207 if (nAnz == 0 && rInfo.eScannedType == css::util::NumberFormat::UNDEFINED)
2209 return false; // Empty => nothing
2211 else if (nAnz == 0) // Else Standard Format
2213 ImpGetOutputStandard(fNumber, sBuff);
2214 OutString = sBuff.makeStringAndClear();
2215 return false;
2217 switch (rInfo.eScannedType)
2219 case css::util::NumberFormat::TEXT:
2220 case css::util::NumberFormat::DEFINED:
2221 for (sal_uInt16 i = 0; i < nAnz; i++)
2223 switch (rInfo.nTypeArray[i])
2225 case NF_SYMBOLTYPE_STAR:
2226 if( bStarFlag )
2228 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
2230 break;
2231 case NF_SYMBOLTYPE_BLANK:
2232 InsertBlanks(sBuff, sBuff.getLength(),
2233 rInfo.sStrArray[i][1] );
2234 break;
2235 case NF_SYMBOLTYPE_STRING:
2236 case NF_SYMBOLTYPE_CURRENCY:
2237 sBuff.append(rInfo.sStrArray[i]);
2238 break;
2239 case NF_SYMBOLTYPE_THSEP:
2240 if (rInfo.nThousand == 0)
2242 sBuff.append(rInfo.sStrArray[i]);
2244 break;
2245 default:
2246 break;
2249 break;
2250 case css::util::NumberFormat::DATE:
2251 bRes |= ImpGetDateOutput(fNumber, nIx, sBuff);
2252 break;
2253 case css::util::NumberFormat::TIME:
2254 bRes |= ImpGetTimeOutput(fNumber, nIx, sBuff);
2255 break;
2256 case css::util::NumberFormat::DATETIME:
2257 bRes |= ImpGetDateTimeOutput(fNumber, nIx, sBuff);
2258 break;
2259 case css::util::NumberFormat::NUMBER:
2260 case css::util::NumberFormat::PERCENT:
2261 case css::util::NumberFormat::CURRENCY:
2262 bRes |= ImpGetNumberOutput(fNumber, nIx, sBuff);
2263 break;
2264 case css::util::NumberFormat::FRACTION:
2265 bRes |= ImpGetFractionOutput(fNumber, nIx, sBuff);
2266 break;
2267 case css::util::NumberFormat::SCIENTIFIC:
2268 bRes |= ImpGetScientificOutput(fNumber, nIx, sBuff);
2269 break;
2272 OutString = sBuff.makeStringAndClear();
2273 return bRes;
2276 bool SvNumberformat::ImpGetScientificOutput(double fNumber,
2277 sal_uInt16 nIx,
2278 OUStringBuffer& sStr)
2280 bool bRes = false;
2281 bool bSign = false;
2283 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2284 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
2286 if (fNumber < 0)
2288 if (nIx == 0) // Not in the ones at the end
2290 bSign = true; // Formats
2292 fNumber = -fNumber;
2295 sStr = ::rtl::math::doubleToUString( fNumber,
2296 rtl_math_StringFormat_E,
2297 rInfo.nCntPre + rInfo.nCntPost - 1, '.' );
2298 OUStringBuffer ExpStr;
2299 short nExpSign = 1;
2300 sal_Int32 nExPos = sStr.indexOf('E');
2302 if ( nExPos >= 0 )
2304 // split into mantisse and exponent and get rid of "E+" or "E-"
2305 sal_Int32 nExpStart = nExPos + 1;
2307 switch ( sStr[ nExpStart ] )
2309 case '-' :
2310 nExpSign = -1;
2311 // fall through
2312 case '+' :
2313 ++nExpStart;
2314 break;
2316 ExpStr = sStr.toString().copy( nExpStart ); // part following the "E+"
2317 sStr.truncate( nExPos );
2319 if ( rInfo.nCntPre != 1 ) // rescale Exp
2321 sal_Int32 nExp = ExpStr.toString().toInt32() * nExpSign;
2322 sal_Int32 nRescale = (rInfo.nCntPre != 0) ? nExp % (sal_Int32)rInfo.nCntPre : -1;
2323 if( nRescale < 0 && rInfo.nCntPre != 0 )
2324 nRescale += (sal_Int32)rInfo.nCntPre;
2325 nExp -= nRescale;
2326 if ( nExp < 0 )
2328 nExpSign = -1;
2329 nExp = -nExp;
2331 else
2333 nExpSign = 1;
2335 ExpStr = OUString::number( nExp );
2336 // rescale mantissa
2337 sStr = ::rtl::math::doubleToUString( fNumber,
2338 rtl_math_StringFormat_E,
2339 nRescale + rInfo.nCntPost, '.' );
2340 sStr.truncate( sStr.indexOf('E') );
2343 // cut any decimal delimiter
2344 sal_Int32 index = 0;
2346 while((index = sStr.indexOf('.', index)) >= 0)
2348 sStr.remove(index, 1);
2352 sal_uInt16 j = nAnz-1; // Last symbol
2353 sal_Int32 k; // Position in ExpStr
2354 sal_Int32 nZeros = 0; // Erase leading zeros
2356 bRes |= ImpNumberFill(ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP);
2358 while (nZeros < k && ExpStr[nZeros] == '0')
2360 ++nZeros;
2362 if (nZeros)
2364 ExpStr.remove( 0, nZeros);
2367 bool bCont = true;
2369 if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_EXP)
2371 const OUString& rStr = rInfo.sStrArray[j];
2372 if (nExpSign == -1)
2374 ExpStr.insert(0, '-');
2376 else if (rStr.getLength() > 1 && rStr[1] == '+')
2378 ExpStr.insert(0, '+');
2380 ExpStr.insert(0, rStr[0]);
2381 if ( j )
2383 j--;
2385 else
2387 bCont = false;
2390 // Continure main number:
2391 if ( !bCont )
2393 sStr.truncate();
2395 else
2397 bRes |= ImpDecimalFill(sStr, fNumber, j, nIx, false);
2400 if (bSign)
2402 sStr.insert(0, '-');
2404 sStr.append(ExpStr);
2406 return bRes;
2409 bool SvNumberformat::ImpGetFractionOutput(double fNumber,
2410 sal_uInt16 nIx,
2411 OUStringBuffer& sBuff)
2413 bool bRes = false;
2414 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2415 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
2416 OUStringBuffer sStr, sFrac, sDiv; // Strings, value for
2417 sal_uLong nFrac, nDiv; // Integral part
2418 bool bSign = false; // Numerator and denominator
2420 if (fNumber < 0)
2422 if (nIx == 0) // Not in the ones at the end
2423 bSign = true; // Formats
2424 fNumber = -fNumber;
2427 double fNum = floor(fNumber); // Integral part
2429 fNumber -= fNum; // Fractional part
2430 if (fNum > _D_MAX_U_LONG_ || rInfo.nCntExp > 9) // Too large
2432 sBuff = rScan.GetErrorString();
2433 return false;
2435 if (rInfo.nCntExp == 0)
2437 SAL_WARN( "svl.numbers", "SvNumberformat:: Fraction, nCntExp == 0");
2438 sBuff.truncate();
2439 return false;
2442 sal_uLong nBasis = ((sal_uLong)floor( pow(10.0,rInfo.nCntExp))) - 1; // 9, 99, 999 ,...
2443 sal_uLong x0, y0, x1, y1;
2445 if (rInfo.nCntExp <= _MAX_FRACTION_PREC)
2447 bool bUpperHalf;
2449 if (fNumber > 0.5)
2451 bUpperHalf = true;
2452 fNumber -= (fNumber - 0.5) * 2.0;
2454 else
2456 bUpperHalf = false;
2458 // Find entry to Farey sequence:
2459 x0 = (sal_uLong) floor(fNumber*nBasis); // e.g.: 2/9 <= x < 3/9
2460 if (x0 == 0) // => x0 = 2
2462 y0 = 1;
2463 x1 = 1;
2464 y1 = nBasis;
2466 else if (x0 == (nBasis-1)/2) // (b-1)/2, 1/2
2467 { // is ok (nBasis is odd)
2468 y0 = nBasis;
2469 x1 = 1;
2470 y1 = 2;
2472 else if (x0 == 1)
2474 y0 = nBasis; // 1/n; 1/(n-1)
2475 x1 = 1;
2476 y1 = nBasis - 1;
2478 else
2480 y0 = nBasis; // e.g.: 2/9 2/8
2481 x1 = x0;
2482 y1 = nBasis - 1;
2483 double fUg = (double) x0 / (double) y0;
2484 double fOg = (double) x1 / (double) y1;
2485 sal_uLong nGgt = ImpGGT(y0, x0); // x0/y0 kuerzen
2486 x0 /= nGgt;
2487 y0 /= nGgt; // Nest:
2488 sal_uLong x2 = 0;
2489 sal_uLong y2 = 0;
2490 bool bStop = false;
2491 while (!bStop)
2493 #ifdef __GNUC__
2494 // #i21648# GCC over-optimizes something resulting
2495 // in wrong fTest values throughout the loops.
2496 volatile
2497 #endif
2498 double fTest = (double)x1/(double)y1;
2499 while (!bStop)
2501 while (fTest > fOg)
2503 x1--;
2504 fTest = (double)x1/(double)y1;
2506 while (fTest < fUg && y1 > 1)
2508 y1--;
2509 fTest = (double)x1/(double)y1;
2511 if (fTest <= fOg)
2513 fOg = fTest;
2514 bStop = true;
2516 else if (y1 == 1)
2518 bStop = true;
2520 } // of while
2521 nGgt = ImpGGT(y1, x1); // Shorten x1/y1
2522 x2 = x1 / nGgt;
2523 y2 = y1 / nGgt;
2524 if (x2*y0 - x0*y2 == 1 || y1 <= 1) // Test for x2/y2
2525 bStop = true; // Next Farey number
2526 else
2528 y1--;
2529 bStop = false;
2531 } // of while
2532 x1 = x2;
2533 y1 = y2;
2534 } // of else
2536 double fup, flow;
2538 flow = (double)x0/(double)y0;
2539 fup = (double)x1/(double)y1;
2540 while (fNumber > fup)
2542 sal_uLong x2 = ((y0+nBasis)/y1)*x1 - x0; // Next Farey number
2543 sal_uLong y2 = ((y0+nBasis)/y1)*y1 - y0;
2545 x0 = x1;
2546 y0 = y1;
2547 x1 = x2;
2548 y1 = y2;
2549 flow = fup;
2550 fup = (double)x1/(double)y1;
2552 if (fNumber - flow < fup - fNumber)
2554 nFrac = x0;
2555 nDiv = y0;
2557 else
2559 nFrac = x1;
2560 nDiv = y1;
2562 if (bUpperHalf) // Recover original
2564 if (nFrac == 0 && nDiv == 1) // 1/1
2566 fNum += 1.0;
2568 else
2570 nFrac = nDiv - nFrac;
2574 else // Large denominator
2575 { // 0,1234->123/1000
2576 sal_uLong nGgt;
2578 nDiv = 10000000;
2579 nFrac = ((sal_uLong)floor(0.5 + fNumber * 10000000.0));
2580 nGgt = ImpGGT(nDiv, nFrac);
2581 if (nGgt > 1)
2583 nDiv /= nGgt;
2584 nFrac /= nGgt;
2586 if (nDiv > nBasis)
2588 nGgt = ImpGGTRound(nDiv, nFrac);
2589 if (nGgt > 1)
2591 nDiv /= nGgt;
2592 nFrac /= nGgt;
2595 if (nDiv > nBasis)
2597 nDiv = nBasis;
2598 nFrac = ((sal_uLong)floor(0.5 + fNumber *
2599 pow(10.0,rInfo.nCntExp)));
2600 nGgt = ImpGGTRound(nDiv, nFrac);
2601 if (nGgt > 1)
2603 nDiv /= nGgt;
2604 nFrac /= nGgt;
2609 if( sal_Int32 nForcedDiv = lcl_GetForcedDenominator(NumFor[nIx].Info(), nAnz) )
2611 lcl_ForcedDenominator(nFrac, nDiv, nForcedDiv);
2612 if( nFrac >= nDiv )
2614 nFrac = nDiv = 0;
2615 fNum = fNum + 1.0;
2619 if (rInfo.nCntPre == 0) // Improper fraction
2621 double fNum1 = fNum * (double)nDiv + (double)nFrac;
2623 if (fNum1 > _D_MAX_U_LONG_)
2625 sBuff = rScan.GetErrorString();
2626 return false;
2628 nFrac = (sal_uLong) floor(fNum1);
2630 else if (fNum == 0.0 && nFrac != 0)
2633 else
2635 char aBuf[100];
2636 sprintf( aBuf, "%.f", fNum ); // simple rounded integer (#100211# - checked)
2637 sStr.appendAscii( aBuf );
2638 impTransliterate(sStr, NumFor[nIx].GetNatNum());
2640 if (rInfo.nCntPre > 0 && nFrac == 0)
2642 sDiv.truncate();
2644 else
2646 sFrac = ImpIntToString( nIx, nFrac );
2647 sDiv = ImpIntToString( nIx, nDiv );
2650 sal_uInt16 j = nAnz-1; // Last symbol -> backwards
2651 sal_Int32 k; // Denominator
2653 bRes |= ImpNumberFill(sDiv, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRAC);
2655 bool bCont = true;
2656 if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRAC)
2658 if (rInfo.nCntPre > 0 && nFrac == 0)
2660 sDiv.insert(0, ' ');
2662 else
2664 sDiv.insert(0, rInfo.sStrArray[j][0]);
2666 if ( j )
2668 j--;
2670 else
2672 bCont = false;
2675 // Further numerators:
2676 if ( !bCont )
2678 sFrac.truncate();
2680 else
2682 bRes |= ImpNumberFill(sFrac, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRACBLANK);
2683 if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRACBLANK)
2685 sFrac.insert(0, rInfo.sStrArray[j]);
2686 if ( j )
2688 j--;
2690 else
2692 bCont = false;
2696 // Continue main number
2697 if ( !bCont )
2699 sStr.truncate();
2701 else
2703 k = sStr.getLength(); // After last figure
2704 bRes |= ImpNumberFillWithThousands(sStr, fNumber, k, j, nIx,
2705 rInfo.nCntPre);
2707 if (bSign && !(nFrac == 0 && fNum == 0.0))
2709 sBuff.insert(0, '-'); // Not -0
2711 sBuff.append(sStr);
2712 sBuff.append(sFrac);
2713 sBuff.append(sDiv);
2714 return bRes;
2717 bool SvNumberformat::ImpGetTimeOutput(double fNumber,
2718 sal_uInt16 nIx,
2719 OUStringBuffer& sBuff)
2721 using namespace ::com::sun::star::i18n;
2722 bool bCalendarSet = false;
2723 double fNumberOrig = fNumber;
2724 bool bRes = false;
2725 bool bSign = false;
2726 if (fNumber < 0.0)
2728 fNumber = -fNumber;
2729 if (nIx == 0)
2731 bSign = true;
2734 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2735 if (rInfo.bThousand) // [] format
2737 if (fNumber > 1.0E10) // Too large
2739 sBuff = rScan.GetErrorString();
2740 return false;
2743 else
2745 fNumber -= floor(fNumber); // Else truncate date
2747 bool bInputLine;
2748 sal_Int32 nCntPost;
2749 if ( rScan.GetStandardPrec() == 300 &&
2750 0 < rInfo.nCntPost && rInfo.nCntPost < 7 )
2751 { // round at 7 decimals (+5 of 86400 == 12 significant digits)
2752 bInputLine = true;
2753 nCntPost = 7;
2755 else
2757 bInputLine = false;
2758 nCntPost = rInfo.nCntPost;
2760 if (bSign && !rInfo.bThousand) // No [] format
2762 fNumber = 1.0 - fNumber; // "Inverse"
2764 double fTime = fNumber * 86400.0;
2765 fTime = ::rtl::math::round( fTime, int(nCntPost) );
2766 if (bSign && fTime == 0.0)
2768 bSign = false; // Not -00:00:00
2770 if( floor( fTime ) > _D_MAX_U_LONG_ )
2772 sBuff = rScan.GetErrorString();
2773 return false;
2775 sal_uLong nSeconds = (sal_uLong)floor( fTime );
2777 OUStringBuffer sSecStr( ::rtl::math::doubleToUString( fTime-nSeconds,
2778 rtl_math_StringFormat_F, int(nCntPost), '.'));
2779 sSecStr.stripStart('0');
2780 sSecStr.stripStart('.');
2781 if ( bInputLine )
2783 sSecStr.stripEnd('0');
2784 for(sal_Int32 index = sSecStr.getLength(); index < rInfo.nCntPost; ++index)
2786 sSecStr.append('0');
2788 impTransliterate(sSecStr, NumFor[nIx].GetNatNum());
2789 nCntPost = sSecStr.getLength();
2791 else
2793 impTransliterate(sSecStr, NumFor[nIx].GetNatNum());
2796 sal_Int32 nSecPos = 0; // For figure by figure processing
2797 sal_uLong nHour, nMin, nSec;
2798 if (!rInfo.bThousand) // No [] format
2800 nHour = (nSeconds/3600) % 24;
2801 nMin = (nSeconds%3600) / 60;
2802 nSec = nSeconds%60;
2804 else if (rInfo.nThousand == 3) // [ss]
2806 nHour = 0;
2807 nMin = 0;
2808 nSec = nSeconds;
2810 else if (rInfo.nThousand == 2) // [mm]:ss
2812 nHour = 0;
2813 nMin = nSeconds / 60;
2814 nSec = nSeconds % 60;
2816 else if (rInfo.nThousand == 1) // [hh]:mm:ss
2818 nHour = nSeconds / 3600;
2819 nMin = (nSeconds%3600) / 60;
2820 nSec = nSeconds%60;
2822 else
2824 // TODO What should these be set to?
2825 nHour = 0;
2826 nMin = 0;
2827 nSec = 0;
2830 sal_Unicode cAmPm = ' '; // a or p
2831 if (rInfo.nCntExp) // AM/PM
2833 if (nHour == 0)
2835 nHour = 12;
2836 cAmPm = 'a';
2838 else if (nHour < 12)
2840 cAmPm = 'a';
2842 else
2844 cAmPm = 'p';
2845 if (nHour > 12)
2847 nHour -= 12;
2851 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
2852 for (sal_uInt16 i = 0; i < nAnz; i++)
2854 sal_Int32 nLen;
2855 switch (rInfo.nTypeArray[i])
2857 case NF_SYMBOLTYPE_STAR:
2858 if( bStarFlag )
2860 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
2862 break;
2863 case NF_SYMBOLTYPE_BLANK:
2864 InsertBlanks(sBuff, sBuff.getLength(),
2865 rInfo.sStrArray[i][1] );
2866 break;
2867 case NF_SYMBOLTYPE_STRING:
2868 case NF_SYMBOLTYPE_CURRENCY:
2869 case NF_SYMBOLTYPE_DATESEP:
2870 case NF_SYMBOLTYPE_TIMESEP:
2871 case NF_SYMBOLTYPE_TIME100SECSEP:
2872 sBuff.append(rInfo.sStrArray[i]);
2873 break;
2874 case NF_SYMBOLTYPE_DIGIT:
2875 nLen = ( bInputLine && i > 0 &&
2876 (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
2877 rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
2878 nCntPost : rInfo.sStrArray[i].getLength() );
2879 for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost; j++)
2881 sBuff.append(sSecStr[nSecPos]);
2882 nSecPos++;
2884 break;
2885 case NF_KEY_AMPM: // AM/PM
2886 if ( !bCalendarSet )
2888 double fDiff = DateTime(*(rScan.GetNullDate())) - GetCal().getEpochStart();
2889 fDiff += fNumberOrig;
2890 GetCal().setLocalDateTime( fDiff );
2891 bCalendarSet = true;
2893 if (cAmPm == 'a')
2895 sBuff.append(GetCal().getDisplayName(
2896 CalendarDisplayIndex::AM_PM, AmPmValue::AM, 0 ));
2898 else
2900 sBuff.append(GetCal().getDisplayName(
2901 CalendarDisplayIndex::AM_PM, AmPmValue::PM, 0 ));
2903 break;
2904 case NF_KEY_AP: // A/P
2905 if (cAmPm == 'a')
2907 sBuff.append('a');
2909 else
2911 sBuff.append('p');
2913 break;
2914 case NF_KEY_MI: // M
2915 sBuff.append(ImpIntToString( nIx, nMin ));
2916 break;
2917 case NF_KEY_MMI: // MM
2918 sBuff.append(ImpIntToString( nIx, nMin, 2 ));
2919 break;
2920 case NF_KEY_H: // H
2921 sBuff.append(ImpIntToString( nIx, nHour ));
2922 break;
2923 case NF_KEY_HH: // HH
2924 sBuff.append(ImpIntToString( nIx, nHour, 2 ));
2925 break;
2926 case NF_KEY_S: // S
2927 sBuff.append(ImpIntToString( nIx, nSec ));
2928 break;
2929 case NF_KEY_SS: // SS
2930 sBuff.append(ImpIntToString( nIx, nSec, 2 ));
2931 break;
2932 default:
2933 break;
2936 if (bSign && rInfo.bThousand)
2938 sBuff.insert(0, '-');
2940 return bRes;
2944 /** If a day of month occurs within the format, the month name is in possessive
2945 genitive case if the day follows the month, and partitive case if the day
2946 precedes the month. If there is no day of month the nominative case (noun)
2947 is returned. Also if the month is immediately preceded or followed by a
2948 literal string other than space the nominative name is used, this prevents
2949 duplicated casing for MMMM\t\a and such in documents imported from (e.g.
2950 Finnish) Excel or older LibO/OOo releases.
2953 // IDEA: instead of eCodeType pass the index to nTypeArray and restrict
2954 // inspection of month name around that one, that would enable different month
2955 // cases in one format. Though probably the most rare use case ever..
2957 sal_Int32 SvNumberformat::ImpUseMonthCase( int & io_nState, const ImpSvNumFor& rNumFor, NfKeywordIndex eCodeType ) const
2959 using namespace ::com::sun::star::i18n;
2960 if (!io_nState)
2962 bool bMonthSeen = false;
2963 bool bDaySeen = false;
2964 const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
2965 const sal_uInt16 nCount = rNumFor.GetCount();
2966 for (sal_uInt16 i = 0; i < nCount && io_nState == 0; ++i)
2968 sal_Int32 nLen;
2969 switch (rInfo.nTypeArray[i])
2971 case NF_KEY_D :
2972 case NF_KEY_DD :
2973 if (bMonthSeen)
2975 io_nState = 2;
2977 else
2979 bDaySeen = true;
2981 break;
2982 case NF_KEY_MMM:
2983 case NF_KEY_MMMM:
2984 case NF_KEY_MMMMM:
2985 if ((i < nCount-1 &&
2986 rInfo.nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
2987 rInfo.sStrArray[i+1][0] != ' ') ||
2988 (i > 0 && rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING &&
2989 ((nLen = rInfo.sStrArray[i-1].getLength()) > 0) &&
2990 rInfo.sStrArray[i-1][nLen-1] != ' '))
2992 io_nState = 1;
2994 else if (bDaySeen)
2996 io_nState = 3;
2998 else
3000 bMonthSeen = true;
3002 break;
3005 if (io_nState == 0)
3007 io_nState = 1; // No day of month
3010 switch (io_nState)
3012 case 1:
3013 // No day of month or forced nominative
3014 switch (eCodeType)
3016 case NF_KEY_MMM:
3017 return CalendarDisplayCode::SHORT_MONTH_NAME;
3018 case NF_KEY_MMMM:
3019 return CalendarDisplayCode::LONG_MONTH_NAME;
3020 case NF_KEY_MMMMM:
3021 return CalendarDisplayCode::NARROW_MONTH_NAME;
3022 default:
3023 ; // nothing
3025 case 2:
3026 // Day of month follows month (the month's 17th)
3027 switch (eCodeType)
3029 case NF_KEY_MMM:
3030 return CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME;
3031 case NF_KEY_MMMM:
3032 return CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME;
3033 case NF_KEY_MMMMM:
3034 return CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME;
3035 default:
3036 ; // Nothing
3038 case 3:
3039 // Day of month precedes month (17 of month)
3040 switch (eCodeType)
3042 case NF_KEY_MMM:
3043 return CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME;
3044 case NF_KEY_MMMM:
3045 return CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME;
3046 case NF_KEY_MMMMM:
3047 return CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME;
3048 default:
3049 ; // nothing
3052 SAL_WARN( "svl.numbers", "ImpUseMonthCase: unhandled keyword index eCodeType");
3053 return CalendarDisplayCode::LONG_MONTH_NAME;
3057 bool SvNumberformat::ImpIsOtherCalendar( const ImpSvNumFor& rNumFor ) const
3059 if ( GetCal().getUniqueID() != GREGORIAN )
3061 return false;
3063 const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3064 const sal_uInt16 nAnz = rNumFor.GetCount();
3065 sal_uInt16 i;
3066 for ( i = 0; i < nAnz; i++ )
3068 switch ( rInfo.nTypeArray[i] )
3070 case NF_SYMBOLTYPE_CALENDAR :
3071 return false;
3072 case NF_KEY_EC :
3073 case NF_KEY_EEC :
3074 case NF_KEY_R :
3075 case NF_KEY_RR :
3076 case NF_KEY_AAA :
3077 case NF_KEY_AAAA :
3078 return true;
3081 return false;
3084 void SvNumberformat::SwitchToOtherCalendar( OUString& rOrgCalendar,
3085 double& fOrgDateTime ) const
3087 CalendarWrapper& rCal = GetCal();
3088 if ( rCal.getUniqueID() == GREGORIAN )
3090 using namespace ::com::sun::star::i18n;
3091 ::com::sun::star::uno::Sequence< OUString > xCals = rCal.getAllCalendars(
3092 rLoc().getLanguageTag().getLocale() );
3093 sal_Int32 nCnt = xCals.getLength();
3094 if ( nCnt > 1 )
3096 for ( sal_Int32 j=0; j < nCnt; j++ )
3098 if ( xCals[j] != GREGORIAN )
3100 if ( !rOrgCalendar.getLength() )
3102 rOrgCalendar = rCal.getUniqueID();
3103 fOrgDateTime = rCal.getDateTime();
3105 rCal.loadCalendar( xCals[j], rLoc().getLanguageTag().getLocale() );
3106 rCal.setDateTime( fOrgDateTime );
3107 break; // for
3114 void SvNumberformat::SwitchToGregorianCalendar( const OUString& rOrgCalendar,
3115 double fOrgDateTime ) const
3117 CalendarWrapper& rCal = GetCal();
3118 if ( rOrgCalendar.getLength() && rCal.getUniqueID() != GREGORIAN )
3120 rCal.loadCalendar( GREGORIAN, rLoc().getLanguageTag().getLocale() );
3121 rCal.setDateTime( fOrgDateTime );
3125 bool SvNumberformat::ImpFallBackToGregorianCalendar( OUString& rOrgCalendar, double& fOrgDateTime )
3127 using namespace ::com::sun::star::i18n;
3128 CalendarWrapper& rCal = GetCal();
3129 if ( rCal.getUniqueID() != GREGORIAN )
3131 sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3132 if ( nVal == 0 && rCal.getLoadedCalendar().Eras[0].ID == "Dummy" )
3134 if ( !rOrgCalendar.getLength() )
3136 rOrgCalendar = rCal.getUniqueID();
3137 fOrgDateTime = rCal.getDateTime();
3139 else if ( rOrgCalendar == GREGORIAN )
3141 rOrgCalendar.clear();
3143 rCal.loadCalendar( GREGORIAN, rLoc().getLanguageTag().getLocale() );
3144 rCal.setDateTime( fOrgDateTime );
3145 return true;
3148 return false;
3152 #ifdef THE_FUTURE
3153 /* XXX NOTE: even if the ImpSwitchToSpecifiedCalendar method is currently
3154 * unused please don't remove it, it would be needed by
3155 * SwitchToSpecifiedCalendar(), see comment in
3156 * ImpSvNumberInputScan::GetDateRef() */
3158 bool SvNumberformat::ImpSwitchToSpecifiedCalendar( OUString& rOrgCalendar,
3159 double& fOrgDateTime,
3160 const ImpSvNumFor& rNumFor ) const
3162 const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3163 const sal_uInt16 nAnz = rNumFor.GetCount();
3164 for ( sal_uInt16 i = 0; i < nAnz; i++ )
3166 if ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_CALENDAR )
3168 CalendarWrapper& rCal = GetCal();
3169 if ( !rOrgCalendar.getLength() )
3171 rOrgCalendar = rCal.getUniqueID();
3172 fOrgDateTime = rCal.getDateTime();
3174 rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLocale() );
3175 rCal.setDateTime( fOrgDateTime );
3176 return true;
3179 return false;
3181 #endif
3183 // static
3184 void SvNumberformat::ImpAppendEraG( OUStringBuffer& OutString,
3185 const CalendarWrapper& rCal,
3186 sal_Int16 nNatNum )
3188 using namespace ::com::sun::star::i18n;
3189 if ( rCal.getUniqueID() == "gengou" )
3191 sal_Unicode cEra;
3192 sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3193 switch ( nVal )
3195 case 1:
3196 cEra = 'M';
3197 break;
3198 case 2:
3199 cEra = 'T';
3200 break;
3201 case 3:
3202 cEra = 'S';
3203 break;
3204 case 4:
3205 cEra = 'H';
3206 break;
3207 default:
3208 cEra = '?';
3209 break;
3211 OutString.append(cEra);
3213 else
3215 OutString.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3219 bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor& rNumFor )
3221 bool bIsIso = false;
3222 if ((eType & css::util::NumberFormat::DATE) == css::util::NumberFormat::DATE)
3224 enum State
3226 eNone,
3227 eAtYear,
3228 eAtSep1,
3229 eAtMonth,
3230 eAtSep2,
3231 eNotIso
3233 State eState = eNone;
3234 short const * const pType = rNumFor.Info().nTypeArray;
3235 sal_uInt16 nAnz = rNumFor.GetCount();
3236 for (sal_uInt16 i=0; i < nAnz && !bIsIso && eState != eNotIso; ++i)
3238 switch ( pType[i] )
3240 case NF_KEY_YY: // two digits not strictly ISO 8601
3241 case NF_KEY_YYYY:
3242 if (eState != eNone)
3244 eState = eNotIso;
3246 else
3248 eState = eAtYear;
3250 break;
3251 case NF_KEY_M: // single digit not strictly ISO 8601
3252 case NF_KEY_MM:
3253 if (eState != eAtSep1)
3255 eState = eNotIso;
3257 else
3259 eState = eAtMonth;
3261 break;
3262 case NF_KEY_D: // single digit not strictly ISO 8601
3263 case NF_KEY_DD:
3264 if (eState != eAtSep2)
3266 eState = eNotIso;
3268 else
3270 bIsIso = true;
3272 break;
3273 case NF_SYMBOLTYPE_STRING:
3274 case NF_SYMBOLTYPE_DATESEP:
3275 if (comphelper::string::equals(rNumFor.Info().sStrArray[i], '-'))
3277 if (eState == eAtYear)
3279 eState = eAtSep1;
3281 else if (eState == eAtMonth)
3283 eState = eAtSep2;
3285 else
3287 eState = eNotIso;
3290 else
3292 eState = eNotIso;
3294 break;
3295 default:
3296 eState = eNotIso;
3300 else
3302 SAL_WARN( "svl.numbers", "SvNumberformat::ImpIsIso8601: no date" );
3304 return bIsIso;
3307 bool SvNumberformat::ImpGetDateOutput(double fNumber,
3308 sal_uInt16 nIx,
3309 OUStringBuffer& sBuff)
3311 using namespace ::com::sun::star::i18n;
3312 bool bRes = false;
3314 CalendarWrapper& rCal = GetCal();
3315 double fDiff = DateTime(*(rScan.GetNullDate())) - rCal.getEpochStart();
3316 fNumber += fDiff;
3317 rCal.setLocalDateTime( fNumber );
3318 int nUseMonthCase = 0; // Not decided yet
3319 OUString aOrgCalendar; // empty => not changed yet
3321 double fOrgDateTime(0.0);
3322 bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx] );
3323 if ( bOtherCalendar )
3325 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3327 if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ) )
3329 bOtherCalendar = false;
3331 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3332 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
3333 sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
3334 OUString aYear;
3336 for (sal_uInt16 i = 0; i < nAnz; i++)
3338 switch (rInfo.nTypeArray[i])
3340 case NF_SYMBOLTYPE_CALENDAR :
3341 if ( !aOrgCalendar.getLength() )
3343 aOrgCalendar = rCal.getUniqueID();
3344 fOrgDateTime = rCal.getDateTime();
3346 rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
3347 rCal.setDateTime( fOrgDateTime );
3348 ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3349 break;
3350 case NF_SYMBOLTYPE_STAR:
3351 if( bStarFlag )
3353 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
3355 break;
3356 case NF_SYMBOLTYPE_BLANK:
3357 InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
3358 break;
3359 case NF_SYMBOLTYPE_STRING:
3360 case NF_SYMBOLTYPE_CURRENCY:
3361 case NF_SYMBOLTYPE_DATESEP:
3362 case NF_SYMBOLTYPE_TIMESEP:
3363 case NF_SYMBOLTYPE_TIME100SECSEP:
3364 sBuff.append(rInfo.sStrArray[i]);
3365 break;
3366 case NF_KEY_M: // M
3367 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_MONTH, nNatNum ));
3368 break;
3369 case NF_KEY_MM: // MM
3370 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_MONTH, nNatNum ));
3371 break;
3372 case NF_KEY_MMM: // MMM
3373 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3374 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3375 nNatNum));
3376 break;
3377 case NF_KEY_MMMM: // MMMM
3378 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3379 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3380 nNatNum));
3381 break;
3382 case NF_KEY_MMMMM: // MMMMM
3383 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3384 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3385 nNatNum));
3386 break;
3387 case NF_KEY_Q: // Q
3388 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
3389 break;
3390 case NF_KEY_QQ: // QQ
3391 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
3392 break;
3393 case NF_KEY_D: // D
3394 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum ));
3395 break;
3396 case NF_KEY_DD: // DD
3397 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
3398 break;
3399 case NF_KEY_DDD: // DDD
3400 if ( bOtherCalendar )
3402 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3404 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
3405 if ( bOtherCalendar )
3407 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3409 break;
3410 case NF_KEY_DDDD: // DDDD
3411 if ( bOtherCalendar )
3413 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3415 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3416 if ( bOtherCalendar )
3418 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3420 break;
3421 case NF_KEY_YY: // YY
3422 if ( bOtherCalendar )
3424 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3426 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3427 if ( bOtherCalendar )
3429 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3431 break;
3432 case NF_KEY_YYYY: // YYYY
3433 if ( bOtherCalendar )
3435 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3437 aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
3438 if (aYear.getLength() < 4)
3440 using namespace comphelper::string;
3441 // Ensure that year consists of at least 4 digits, so it
3442 // can be distinguished from 2 digits display and edited
3443 // without suddenly being hit by the 2-digit year magic.
3444 OUStringBuffer aBuf;
3445 padToLength(aBuf, 4 - aYear.getLength(), '0');
3446 impTransliterate(aBuf, NumFor[nIx].GetNatNum());
3447 aBuf.append(aYear);
3448 sBuff.append(aBuf);
3450 else
3452 sBuff.append(aYear);
3454 if ( bOtherCalendar )
3456 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3458 break;
3459 case NF_KEY_EC: // E
3460 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3461 break;
3462 case NF_KEY_EEC: // EE
3463 case NF_KEY_R: // R
3464 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
3465 break;
3466 case NF_KEY_NN: // NN
3467 case NF_KEY_AAA: // AAA
3468 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
3469 break;
3470 case NF_KEY_NNN: // NNN
3471 case NF_KEY_AAAA: // AAAA
3472 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3473 break;
3474 case NF_KEY_NNNN: // NNNN
3475 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3476 sBuff.append(rLoc().getLongDateDayOfWeekSep());
3477 break;
3478 case NF_KEY_WW : // WW
3479 sBuff.append(ImpIntToString( nIx,
3480 rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
3481 break;
3482 case NF_KEY_G: // G
3483 ImpAppendEraG(sBuff, rCal, nNatNum );
3484 break;
3485 case NF_KEY_GG: // GG
3486 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3487 break;
3488 case NF_KEY_GGG: // GGG
3489 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
3490 break;
3491 case NF_KEY_RR: // RR => GGGEE
3492 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
3493 break;
3496 if ( aOrgCalendar.getLength() )
3498 rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() ); // restore calendar
3500 return bRes;
3503 bool SvNumberformat::ImpGetDateTimeOutput(double fNumber,
3504 sal_uInt16 nIx,
3505 OUStringBuffer& sBuff)
3507 using namespace ::com::sun::star::i18n;
3508 bool bRes = false;
3510 CalendarWrapper& rCal = GetCal();
3511 double fDiff = DateTime(*(rScan.GetNullDate())) - rCal.getEpochStart();
3512 fNumber += fDiff;
3514 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3515 bool bInputLine;
3516 sal_Int32 nCntPost;
3517 if ( rScan.GetStandardPrec() == 300 &&
3518 0 < rInfo.nCntPost && rInfo.nCntPost < 7 )
3520 // round at 7 decimals (+5 of 86400 == 12 significant digits)
3521 bInputLine = true;
3522 nCntPost = 7;
3524 else
3526 bInputLine = false;
3527 nCntPost = rInfo.nCntPost;
3529 double fTime = (fNumber - floor( fNumber )) * 86400.0;
3530 fTime = ::rtl::math::round( fTime, int(nCntPost) );
3531 if (fTime >= 86400.0)
3533 // result of fNumber==x.999999999... rounded up, use correct date/time
3534 fTime -= 86400.0;
3535 fNumber = floor( fNumber + 0.5) + fTime;
3537 rCal.setLocalDateTime( fNumber );
3539 int nUseMonthCase = 0; // Not decided yet
3540 OUString aOrgCalendar; // empty => not changed yet
3541 double fOrgDateTime(0.0);
3542 bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx] );
3543 if ( bOtherCalendar )
3545 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3547 if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ) )
3549 bOtherCalendar = false;
3551 sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
3553 sal_uLong nSeconds = (sal_uLong)floor( fTime );
3554 OUStringBuffer sSecStr( ::rtl::math::doubleToUString( fTime-nSeconds,
3555 rtl_math_StringFormat_F, int(nCntPost), '.'));
3556 sSecStr.stripStart('0');
3557 sSecStr.stripStart('.');
3558 if ( bInputLine )
3560 sSecStr.stripEnd('0');
3561 for(sal_Int32 index = sSecStr.getLength(); index < rInfo.nCntPost; ++index)
3563 sSecStr.append('0');
3565 impTransliterate(sSecStr, NumFor[nIx].GetNatNum());
3566 nCntPost = sSecStr.getLength();
3568 else
3570 impTransliterate(sSecStr, NumFor[nIx].GetNatNum());
3573 sal_Int32 nSecPos = 0; // For figure by figure processing
3574 sal_uLong nHour, nMin, nSec;
3575 if (!rInfo.bThousand) // [] format
3577 nHour = (nSeconds/3600) % 24;
3578 nMin = (nSeconds%3600) / 60;
3579 nSec = nSeconds%60;
3581 else if (rInfo.nThousand == 3) // [ss]
3583 nHour = 0;
3584 nMin = 0;
3585 nSec = nSeconds;
3587 else if (rInfo.nThousand == 2) // [mm]:ss
3589 nHour = 0;
3590 nMin = nSeconds / 60;
3591 nSec = nSeconds % 60;
3593 else if (rInfo.nThousand == 1) // [hh]:mm:ss
3595 nHour = nSeconds / 3600;
3596 nMin = (nSeconds%3600) / 60;
3597 nSec = nSeconds%60;
3599 else
3601 nHour = 0; // TODO What should these values be?
3602 nMin = 0;
3603 nSec = 0;
3605 sal_Unicode cAmPm = ' '; // a or p
3606 if (rInfo.nCntExp) // AM/PM
3608 if (nHour == 0)
3610 nHour = 12;
3611 cAmPm = 'a';
3613 else if (nHour < 12)
3615 cAmPm = 'a';
3617 else
3619 cAmPm = 'p';
3620 if (nHour > 12)
3622 nHour -= 12;
3626 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
3627 sal_Int32 nLen;
3628 OUString aYear;
3629 for (sal_uInt16 i = 0; i < nAnz; i++)
3631 switch (rInfo.nTypeArray[i])
3633 case NF_SYMBOLTYPE_CALENDAR :
3634 if ( !aOrgCalendar.getLength() )
3636 aOrgCalendar = rCal.getUniqueID();
3637 fOrgDateTime = rCal.getDateTime();
3639 rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
3640 rCal.setDateTime( fOrgDateTime );
3641 ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3642 break;
3643 case NF_SYMBOLTYPE_STAR:
3644 if( bStarFlag )
3646 bRes = lcl_appendStarFillChar( sBuff, rInfo.sStrArray[i]);
3648 break;
3649 case NF_SYMBOLTYPE_BLANK:
3650 InsertBlanks( sBuff, sBuff.getLength(),
3651 rInfo.sStrArray[i][1] );
3652 break;
3653 case NF_SYMBOLTYPE_STRING:
3654 case NF_SYMBOLTYPE_CURRENCY:
3655 case NF_SYMBOLTYPE_DATESEP:
3656 case NF_SYMBOLTYPE_TIMESEP:
3657 case NF_SYMBOLTYPE_TIME100SECSEP:
3658 sBuff.append(rInfo.sStrArray[i]);
3659 break;
3660 case NF_SYMBOLTYPE_DIGIT:
3661 nLen = ( bInputLine && i > 0 &&
3662 (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
3663 rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
3664 nCntPost : rInfo.sStrArray[i].getLength() );
3665 for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost; j++)
3667 sBuff.append(sSecStr[ nSecPos ]);
3668 nSecPos++;
3670 break;
3671 case NF_KEY_AMPM: // AM/PM
3672 if (cAmPm == 'a')
3674 sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
3675 AmPmValue::AM, 0 ));
3677 else
3679 sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
3680 AmPmValue::PM, 0 ));
3682 break;
3683 case NF_KEY_AP: // A/P
3684 if (cAmPm == 'a')
3686 sBuff.append('a');
3688 else
3690 sBuff.append('p');
3692 break;
3693 case NF_KEY_MI: // M
3694 sBuff.append(ImpIntToString( nIx, nMin ));
3695 break;
3696 case NF_KEY_MMI: // MM
3697 sBuff.append(ImpIntToString( nIx, nMin, 2 ));
3698 break;
3699 case NF_KEY_H: // H
3700 sBuff.append(ImpIntToString( nIx, nHour ));
3701 break;
3702 case NF_KEY_HH: // HH
3703 sBuff.append(ImpIntToString( nIx, nHour, 2 ));
3704 break;
3705 case NF_KEY_S: // S
3706 sBuff.append(ImpIntToString( nIx, nSec ));
3707 break;
3708 case NF_KEY_SS: // SS
3709 sBuff.append(ImpIntToString( nIx, nSec, 2 ));
3710 break;
3711 case NF_KEY_M: // M
3712 sBuff.append(rCal.getDisplayString(
3713 CalendarDisplayCode::SHORT_MONTH, nNatNum ));
3714 break;
3715 case NF_KEY_MM: // MM
3716 sBuff.append(rCal.getDisplayString(
3717 CalendarDisplayCode::LONG_MONTH, nNatNum ));
3718 break;
3719 case NF_KEY_MMM: // MMM
3720 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3721 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3722 nNatNum));
3723 break;
3724 case NF_KEY_MMMM: // MMMM
3725 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3726 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3727 nNatNum));
3728 break;
3729 case NF_KEY_MMMMM: // MMMMM
3730 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3731 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3732 nNatNum));
3733 break;
3734 case NF_KEY_Q: // Q
3735 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
3736 break;
3737 case NF_KEY_QQ: // QQ
3738 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
3739 break;
3740 case NF_KEY_D: // D
3741 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum ));
3742 break;
3743 case NF_KEY_DD: // DD
3744 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
3745 break;
3746 case NF_KEY_DDD: // DDD
3747 if ( bOtherCalendar )
3749 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3751 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
3752 if ( bOtherCalendar )
3754 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3756 break;
3757 case NF_KEY_DDDD: // DDDD
3758 if ( bOtherCalendar )
3760 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3762 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3763 if ( bOtherCalendar )
3765 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3767 break;
3768 case NF_KEY_YY: // YY
3769 if ( bOtherCalendar )
3771 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3773 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3774 if ( bOtherCalendar )
3776 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3778 break;
3779 case NF_KEY_YYYY: // YYYY
3780 if ( bOtherCalendar )
3782 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3784 aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
3785 if (aYear.getLength() < 4)
3787 using namespace comphelper::string;
3788 // Ensure that year consists of at least 4 digits, so it
3789 // can be distinguished from 2 digits display and edited
3790 // without suddenly being hit by the 2-digit year magic.
3791 OUStringBuffer aBuf;
3792 padToLength(aBuf, 4 - aYear.getLength(), '0');
3793 impTransliterate(aBuf, NumFor[nIx].GetNatNum());
3794 aBuf.append(aYear);
3795 sBuff.append(aBuf);
3797 else
3799 sBuff.append(aYear);
3801 if ( bOtherCalendar )
3803 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3805 break;
3806 case NF_KEY_EC: // E
3807 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3808 break;
3809 case NF_KEY_EEC: // EE
3810 case NF_KEY_R: // R
3811 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
3812 break;
3813 case NF_KEY_NN: // NN
3814 case NF_KEY_AAA: // AAA
3815 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
3816 break;
3817 case NF_KEY_NNN: // NNN
3818 case NF_KEY_AAAA: // AAAA
3819 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3820 break;
3821 case NF_KEY_NNNN: // NNNN
3822 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3823 sBuff.append(rLoc().getLongDateDayOfWeekSep());
3824 break;
3825 case NF_KEY_WW : // WW
3826 sBuff.append(ImpIntToString( nIx, rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
3827 break;
3828 case NF_KEY_G: // G
3829 ImpAppendEraG( sBuff, rCal, nNatNum );
3830 break;
3831 case NF_KEY_GG: // GG
3832 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3833 break;
3834 case NF_KEY_GGG: // GGG
3835 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
3836 break;
3837 case NF_KEY_RR: // RR => GGGEE
3838 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
3839 break;
3842 if ( aOrgCalendar.getLength() )
3844 rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() ); // restore calendar
3846 return bRes;
3849 bool SvNumberformat::ImpGetNumberOutput(double fNumber,
3850 sal_uInt16 nIx,
3851 OUStringBuffer& sStr)
3853 bool bRes = false;
3854 bool bSign;
3855 if (fNumber < 0.0)
3857 if (nIx == 0) // Not in the ones at the back
3859 bSign = true; // Formats
3861 else
3863 bSign = false;
3865 fNumber = -fNumber;
3867 else
3869 bSign = false;
3870 if ( ::rtl::math::isSignBitSet( fNumber ) )
3872 fNumber = -fNumber; // yes, -0.0 is possible, eliminate '-'
3875 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3876 if (rInfo.eScannedType == css::util::NumberFormat::PERCENT)
3878 if (fNumber < _D_MAX_D_BY_100)
3880 fNumber *= 100.0;
3882 else
3884 sStr = rScan.GetErrorString();
3885 return false;
3888 sal_uInt16 i, j;
3889 bool bInteger = false;
3890 if ( rInfo.nThousand != FLAG_STANDARD_IN_FORMAT )
3892 // Special formatting only if no GENERAL keyword in format code
3893 const sal_uInt16 nThousand = rInfo.nThousand;
3894 long nPrecExp;
3895 for (i = 0; i < nThousand; i++)
3897 if (fNumber > _D_MIN_M_BY_1000)
3899 fNumber /= 1000.0;
3901 else
3903 fNumber = 0.0;
3906 if (fNumber > 0.0)
3908 nPrecExp = GetPrecExp( fNumber );
3910 else
3912 nPrecExp = 0;
3914 if (rInfo.nCntPost) // Decimal places
3916 if ((rInfo.nCntPost + nPrecExp) > 15 && nPrecExp < 15)
3918 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 15-nPrecExp, '.');
3919 for (long l = 15-nPrecExp; l < (long) rInfo.nCntPost; l++)
3921 sStr.append('0');
3924 else
3926 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, rInfo.nCntPost, '.' );
3928 sStr.stripStart('0'); // Strip leading zeros
3930 else if (fNumber == 0.0) // Null
3932 // Nothing to be done here, keep empty string sStr,
3933 // ImpNumberFillWithThousands does the rest
3935 else // Integer
3937 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 0, '.');
3938 sStr.stripStart('0'); // Strip leading zeros
3940 sal_Int32 nPoint = sStr.indexOf('.' );
3941 if ( nPoint >= 0)
3943 const sal_Unicode* p = sStr.getStr() + nPoint;
3944 while ( *++p == '0' )
3946 if ( !*p )
3948 bInteger = true;
3950 sStr.remove( nPoint, 1 ); // Remove .
3952 if (bSign && (sStr.isEmpty() ||
3953 comphelper::string::getTokenCount(sStr.toString(), '0') == sStr.getLength()+1)) // Only 00000
3955 bSign = false; // Not -0.00
3957 } // End of != FLAG_STANDARD_IN_FORMAT
3959 // Edit backwards:
3960 j = NumFor[nIx].GetCount()-1; // Last symbol
3961 // Decimal places:
3962 bRes |= ImpDecimalFill( sStr, fNumber, j, nIx, bInteger );
3963 if (bSign)
3965 sStr.insert(0, '-');
3967 impTransliterate(sStr, NumFor[nIx].GetNatNum());
3968 return bRes;
3971 bool SvNumberformat::ImpDecimalFill( OUStringBuffer& sStr, // number string
3972 double& rNumber, // number
3973 sal_uInt16 j, // symbol index within format code
3974 sal_uInt16 nIx, // subformat index
3975 bool bInteger) // is integer
3977 bool bRes = false;
3978 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3979 sal_Int32 k = sStr.getLength(); // After last figure
3980 // Decimal places:
3981 if (rInfo.nCntPost > 0)
3983 bool bTrailing = true; // Trailing zeros?
3984 bool bFilled = false; // Was filled?
3985 short nType;
3986 while (j > 0 && // Backwards
3987 (nType = rInfo.nTypeArray[j]) != NF_SYMBOLTYPE_DECSEP)
3989 switch ( nType )
3991 case NF_SYMBOLTYPE_STAR:
3992 if( bStarFlag )
3994 bRes = lcl_insertStarFillChar( sStr, k, rInfo.sStrArray[j]);
3996 break;
3997 case NF_SYMBOLTYPE_BLANK:
3998 if (rInfo.sStrArray[j].getLength() >= 2)
3999 /*k = */ InsertBlanks(sStr, k, rInfo.sStrArray[j][1] );
4000 break;
4001 case NF_SYMBOLTYPE_STRING:
4002 case NF_SYMBOLTYPE_CURRENCY:
4003 case NF_SYMBOLTYPE_PERCENT:
4004 sStr.insert(k, rInfo.sStrArray[j]);
4005 break;
4006 case NF_SYMBOLTYPE_THSEP:
4007 if (rInfo.nThousand == 0)
4009 sStr.insert(k, rInfo.sStrArray[j]);
4011 break;
4012 case NF_SYMBOLTYPE_DIGIT:
4014 const OUString& rStr = rInfo.sStrArray[j];
4015 const sal_Unicode* p1 = rStr.getStr();
4016 const sal_Unicode* p = p1 + rStr.getLength();
4017 while (k && p1 < p--)
4019 const sal_Unicode c = *p;
4020 k--;
4021 if ( sStr[k] != '0' )
4023 bTrailing = false;
4025 if (bTrailing)
4027 if ( c == '0' )
4029 bFilled = true;
4031 else if ( c == '-' )
4033 if ( bInteger )
4035 sStr[ k ] = '-';
4037 bFilled = true;
4039 else if ( c == '?' )
4041 sStr[ k ] = ' ';
4042 bFilled = true;
4044 else if ( !bFilled ) // #
4046 sStr.remove(k,1);
4049 } // of for
4050 break;
4051 } // of case digi
4052 case NF_KEY_CCC: // CCC currency
4053 sStr.insert(k, rScan.GetCurAbbrev());
4054 break;
4055 case NF_KEY_GENERAL: // Standard in the String
4057 OUStringBuffer sNum;
4058 ImpGetOutputStandard(rNumber, sNum);
4059 sNum.stripStart('-');
4060 sStr.insert(k, sNum.makeStringAndClear());
4061 break;
4063 default:
4064 break;
4065 } // of switch
4066 j--;
4067 } // of while
4068 } // of decimal places
4070 bRes |= ImpNumberFillWithThousands(sStr, rNumber, k, j, nIx, // Fill with . if needed
4071 rInfo.nCntPre);
4072 if ( rInfo.nCntPost > 0 )
4074 const OUString& rDecSep = GetFormatter().GetNumDecimalSep();
4075 sal_Int32 nLen = rDecSep.getLength();
4076 if ( sStr.getLength() > nLen && ( sStr.indexOf( rDecSep, sStr.getLength() - nLen) == sStr.getLength() - nLen) )
4078 sStr.truncate( sStr.getLength() - nLen ); // no decimals => strip DecSep
4082 return bRes;
4085 bool SvNumberformat::ImpNumberFillWithThousands( OUStringBuffer& sBuff, // number string
4086 double& rNumber, // number
4087 sal_Int32 k, // position within string
4088 sal_uInt16 j, // symbol index within format code
4089 sal_uInt16 nIx, // subformat index
4090 sal_Int32 nDigCnt) // count of integer digits in format
4092 bool bRes = false;
4093 sal_Int32 nLeadingStringChars = 0; // inserted StringChars before number
4094 sal_Int32 nDigitCount = 0; // count of integer digits from the right
4095 bool bStop = false;
4096 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4097 // no normal thousands separators if number divided by thousands
4098 bool bDoThousands = (rInfo.nThousand == 0);
4099 utl::DigitGroupingIterator aGrouping( GetFormatter().GetLocaleData()->getDigitGrouping());
4101 while (!bStop) // backwards
4103 if (j == 0)
4105 bStop = true;
4107 switch (rInfo.nTypeArray[j])
4109 case NF_SYMBOLTYPE_DECSEP:
4110 aGrouping.reset();
4111 // fall through
4112 case NF_SYMBOLTYPE_STRING:
4113 case NF_SYMBOLTYPE_CURRENCY:
4114 case NF_SYMBOLTYPE_PERCENT:
4115 sBuff.insert(k, rInfo.sStrArray[j]);
4116 if ( k == 0 )
4118 nLeadingStringChars = nLeadingStringChars + rInfo.sStrArray[j].getLength();
4120 break;
4121 case NF_SYMBOLTYPE_STAR:
4122 if( bStarFlag )
4124 bRes = lcl_insertStarFillChar( sBuff, k, rInfo.sStrArray[j]);
4126 break;
4127 case NF_SYMBOLTYPE_BLANK:
4128 if (rInfo.sStrArray[j].getLength() >= 2)
4129 /*k = */ InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4130 break;
4131 case NF_SYMBOLTYPE_THSEP:
4132 // #i7284# #102685# Insert separator also if number is divided
4133 // by thousands and the separator is specified somewhere in
4134 // between and not only at the end.
4135 // #i12596# But do not insert if it's a parenthesized negative
4136 // format like (#,)
4137 // In fact, do not insert if divided and regex [0#,],[^0#] and
4138 // no other digit symbol follows (which was already detected
4139 // during scan of format code, otherwise there would be no
4140 // division), else do insert. Same in ImpNumberFill() below.
4141 if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4143 bDoThousands = ((j == 0) ||
4144 (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4145 rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4146 (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4148 if ( bDoThousands )
4150 if (k > 0)
4152 sBuff.insert(k, rInfo.sStrArray[j]);
4154 else if (nDigitCount < nDigCnt)
4156 // Leading '#' displays nothing (e.g. no leading
4157 // separator for numbers <1000 with #,##0 format).
4158 // Leading '?' displays blank.
4159 // Everything else, including nothing, displays the
4160 // separator.
4161 sal_Unicode cLeader = 0;
4162 if (j > 0 && rInfo.nTypeArray[j-1] == NF_SYMBOLTYPE_DIGIT)
4164 const OUString& rStr = rInfo.sStrArray[j-1];
4165 sal_Int32 nLen = rStr.getLength();
4166 if (nLen)
4168 cLeader = rStr[ nLen - 1 ];
4171 switch (cLeader)
4173 case '#':
4174 ; // nothing
4175 break;
4176 case '?':
4177 // erAck: 2008-04-03T16:24+0200
4178 // Actually this currently isn't executed
4179 // because the format scanner in the context of
4180 // "?," doesn't generate a group separator but
4181 // a literal ',' character instead that is
4182 // inserted unconditionally. Should be changed
4183 // on some occasion.
4184 sBuff.insert(k, ' ');
4185 break;
4186 default:
4187 sBuff.insert(k, rInfo.sStrArray[j]);
4190 aGrouping.advance();
4192 break;
4193 case NF_SYMBOLTYPE_DIGIT:
4195 const OUString& rStr = rInfo.sStrArray[j];
4196 const sal_Unicode* p1 = rStr.getStr();
4197 const sal_Unicode* p = p1 + rStr.getLength();
4198 while ( p1 < p-- )
4200 nDigitCount++;
4201 if (k > 0)
4203 k--;
4205 else
4207 switch (*p)
4209 case '0':
4210 sBuff.insert(0, '0');
4211 break;
4212 case '?':
4213 sBuff.insert(0, ' ');
4214 break;
4217 if (nDigitCount == nDigCnt && k > 0)
4219 // more digits than specified
4220 ImpDigitFill(sBuff, 0, k, nIx, nDigitCount, aGrouping);
4223 break;
4225 case NF_KEY_CCC: // CCC currency
4226 sBuff.insert(k, rScan.GetCurAbbrev());
4227 break;
4228 case NF_KEY_GENERAL: // "General" in string
4230 OUStringBuffer sNum;
4231 ImpGetOutputStandard(rNumber, sNum);
4232 sNum.stripStart('-');
4233 sBuff.insert(k, sNum.makeStringAndClear());
4234 break;
4236 default:
4237 break;
4238 } // switch
4239 j--; // next format code string
4240 } // while
4242 k = k + nLeadingStringChars; // MSC converts += to int and then warns, so ...
4243 if (k > nLeadingStringChars)
4245 ImpDigitFill(sBuff, nLeadingStringChars, k, nIx, nDigitCount, aGrouping);
4247 return bRes;
4250 void SvNumberformat::ImpDigitFill(OUStringBuffer& sStr, // number string
4251 sal_Int32 nStart, // start of digits
4252 sal_Int32 & k, // position within string
4253 sal_uInt16 nIx, // subformat index
4254 sal_Int32 & nDigitCount, // count of integer digits from the right so far
4255 utl::DigitGroupingIterator & rGrouping ) // current grouping
4257 if (NumFor[nIx].Info().bThousand) // Only if grouping fill in separators
4259 const OUString& rThousandSep = GetFormatter().GetNumThousandSep();
4260 while (k > nStart)
4262 if (nDigitCount == rGrouping.getPos())
4264 sStr.insert( k, rThousandSep );
4265 rGrouping.advance();
4267 nDigitCount++;
4268 k--;
4271 else // simply skip
4273 k = nStart;
4277 bool SvNumberformat::ImpNumberFill( OUStringBuffer& sBuff, // number string
4278 double& rNumber, // number for "General" format
4279 sal_Int32& k, // position within string
4280 sal_uInt16& j, // symbol index within format code
4281 sal_uInt16 nIx, // subformat index
4282 short eSymbolType ) // type of stop condition
4284 bool bRes = false;
4285 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4286 // no normal thousands separators if number divided by thousands
4287 bool bDoThousands = (rInfo.nThousand == 0);
4288 short nType;
4290 k = sBuff.getLength(); // behind last digit
4292 while (j > 0 && (nType = rInfo.nTypeArray[j]) != eSymbolType ) // Backwards
4294 switch ( nType )
4296 case NF_SYMBOLTYPE_STAR:
4297 if( bStarFlag )
4299 bRes = lcl_insertStarFillChar( sBuff, k, rInfo.sStrArray[j]);
4301 break;
4302 case NF_SYMBOLTYPE_BLANK:
4303 k = InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4304 break;
4305 case NF_SYMBOLTYPE_THSEP:
4306 // Same as in ImpNumberFillWithThousands() above, do not insert
4307 // if divided and regex [0#,],[^0#] and no other digit symbol
4308 // follows (which was already detected during scan of format
4309 // code, otherwise there would be no division), else do insert.
4310 if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4312 bDoThousands = ((j == 0) ||
4313 (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4314 rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4315 (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4317 if ( bDoThousands && k > 0 )
4319 sBuff.insert(k, rInfo.sStrArray[j]);
4321 break;
4322 case NF_SYMBOLTYPE_DIGIT:
4324 const OUString& rStr = rInfo.sStrArray[j];
4325 const sal_Unicode* p1 = rStr.getStr();
4326 const sal_Unicode* p = p1 + rStr.getLength();
4327 while ( p1 < p-- )
4329 if (k > 0)
4331 k--;
4333 else
4335 switch (*p)
4337 case '0':
4338 sBuff.insert(0, '0');
4339 break;
4340 case '?':
4341 sBuff.insert(0, ' ');
4342 break;
4347 break;
4348 case NF_KEY_CCC: // CCC currency
4349 sBuff.insert(k, rScan.GetCurAbbrev());
4350 break;
4351 case NF_KEY_GENERAL: // Standard in the String
4353 OUStringBuffer sNum;
4354 ImpGetOutputStandard(rNumber, sNum);
4355 sNum.stripStart('-');
4356 sBuff.insert(k, sNum.makeStringAndClear());
4358 break;
4359 case NF_SYMBOLTYPE_FRAC_FDIV: // Do Nothing
4360 break;
4362 default:
4363 sBuff.insert(k, rInfo.sStrArray[j]);
4364 break;
4365 } // of switch
4366 j--; // Next String
4367 } // of while
4368 return bRes;
4371 void SvNumberformat::GetFormatSpecialInfo(bool& bThousand,
4372 bool& IsRed,
4373 sal_uInt16& nPrecision,
4374 sal_uInt16& nAnzLeading) const
4376 // as before: take info from nNumFor=0 for whole format (for dialog etc.)
4378 short nDummyType;
4379 GetNumForInfo( 0, nDummyType, bThousand, nPrecision, nAnzLeading );
4381 // "negative in red" is only useful for the whole format
4383 const Color* pColor = NumFor[1].GetColor();
4384 if (fLimit1 == 0.0 && fLimit2 == 0.0 && pColor
4385 && (*pColor == rScan.GetRedColor()))
4387 IsRed = true;
4389 else
4391 IsRed = false;
4395 void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor, short& rScannedType,
4396 bool& bThousand, sal_uInt16& nPrecision, sal_uInt16& nAnzLeading ) const
4398 // take info from a specified sub-format (for XML export)
4400 if ( nNumFor > 3 )
4402 return; // invalid
4405 const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
4406 rScannedType = rInfo.eScannedType;
4407 bThousand = rInfo.bThousand;
4408 nPrecision = rInfo.nCntPost;
4409 if (bStandard && rInfo.eScannedType == css::util::NumberFormat::NUMBER)
4411 // StandardFormat
4412 nAnzLeading = 1;
4414 else
4416 nAnzLeading = 0;
4417 bool bStop = false;
4418 sal_uInt16 i = 0;
4419 const sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
4420 while (!bStop && i < nAnz)
4422 short nType = rInfo.nTypeArray[i];
4423 if ( nType == NF_SYMBOLTYPE_DIGIT)
4425 const sal_Unicode* p = rInfo.sStrArray[i].getStr();
4426 while ( *p == '#' )
4428 p++;
4430 while ( *p++ == '0' )
4432 nAnzLeading++;
4435 else if (nType == NF_SYMBOLTYPE_DECSEP || nType == NF_SYMBOLTYPE_EXP)
4437 bStop = true;
4439 i++;
4444 const OUString* SvNumberformat::GetNumForString( sal_uInt16 nNumFor, sal_uInt16 nPos,
4445 bool bString /* = false */ ) const
4447 if ( nNumFor > 3 )
4449 return NULL;
4451 sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
4452 if ( !nAnz )
4454 return NULL;
4456 if ( nPos == 0xFFFF )
4458 nPos = nAnz - 1;
4459 if ( bString )
4460 { // Backwards
4461 short* pType = NumFor[nNumFor].Info().nTypeArray + nPos;
4462 while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) &&
4463 (*pType != NF_SYMBOLTYPE_CURRENCY) )
4465 pType--;
4466 nPos--;
4468 if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
4470 return NULL;
4474 else if ( nPos > nAnz - 1 )
4476 return NULL;
4478 else if ( bString )
4480 // vorwaerts
4481 short* pType = NumFor[nNumFor].Info().nTypeArray + nPos;
4482 while ( nPos < nAnz && (*pType != NF_SYMBOLTYPE_STRING) &&
4483 (*pType != NF_SYMBOLTYPE_CURRENCY) )
4485 pType++;
4486 nPos++;
4488 if ( nPos >= nAnz || ((*pType != NF_SYMBOLTYPE_STRING) &&
4489 (*pType != NF_SYMBOLTYPE_CURRENCY)) )
4491 return NULL;
4494 return &NumFor[nNumFor].Info().sStrArray[nPos];
4497 short SvNumberformat::GetNumForType( sal_uInt16 nNumFor, sal_uInt16 nPos,
4498 bool bString /* = false */ ) const
4500 if ( nNumFor > 3 )
4502 return 0;
4504 sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
4505 if ( !nAnz )
4507 return 0;
4509 if ( nPos == 0xFFFF )
4511 nPos = nAnz - 1;
4512 if ( bString )
4514 // Backwards
4515 short* pType = NumFor[nNumFor].Info().nTypeArray + nPos;
4516 while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) &&
4517 (*pType != NF_SYMBOLTYPE_CURRENCY) )
4519 pType--;
4520 nPos--;
4522 if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
4524 return 0;
4528 else if ( nPos > nAnz - 1 )
4530 return 0;
4532 else if ( bString )
4534 // Forwards
4535 short* pType = NumFor[nNumFor].Info().nTypeArray + nPos;
4536 while ( nPos < nAnz && (*pType != NF_SYMBOLTYPE_STRING) &&
4537 (*pType != NF_SYMBOLTYPE_CURRENCY) )
4539 pType++;
4540 nPos++;
4542 if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
4544 return 0;
4547 return NumFor[nNumFor].Info().nTypeArray[nPos];
4550 bool SvNumberformat::IsNegativeWithoutSign() const
4552 if ( IsSecondSubformatRealNegative() )
4554 const OUString* pStr = GetNumForString( 1, 0, true );
4555 if ( pStr )
4557 return !HasStringNegativeSign( *pStr );
4560 return false;
4563 bool SvNumberformat::IsNegativeInBracket() const
4565 sal_uInt16 nAnz = NumFor[1].GetCount();
4566 if (!nAnz)
4568 return false;
4570 OUString *tmpStr = NumFor[1].Info().sStrArray;
4571 using comphelper::string::equals;
4572 return (equals(tmpStr[0], '(') && equals(tmpStr[nAnz-1], ')'));
4575 bool SvNumberformat::HasPositiveBracketPlaceholder() const
4577 sal_uInt16 nAnz = NumFor[0].GetCount();
4578 OUString *tmpStr = NumFor[0].Info().sStrArray;
4579 return tmpStr[nAnz-1] == "_)";
4582 DateFormat SvNumberformat::GetDateOrder() const
4584 if ( (eType & css::util::NumberFormat::DATE) == css::util::NumberFormat::DATE )
4586 short const * const pType = NumFor[0].Info().nTypeArray;
4587 sal_uInt16 nAnz = NumFor[0].GetCount();
4588 for ( sal_uInt16 j=0; j<nAnz; j++ )
4590 switch ( pType[j] )
4592 case NF_KEY_D :
4593 case NF_KEY_DD :
4594 return DMY;
4595 case NF_KEY_M :
4596 case NF_KEY_MM :
4597 case NF_KEY_MMM :
4598 case NF_KEY_MMMM :
4599 case NF_KEY_MMMMM :
4600 return MDY;
4601 case NF_KEY_YY :
4602 case NF_KEY_YYYY :
4603 case NF_KEY_EC :
4604 case NF_KEY_EEC :
4605 case NF_KEY_R :
4606 case NF_KEY_RR :
4607 return YMD;
4611 else
4613 SAL_WARN( "svl.numbers", "SvNumberformat::GetDateOrder: no date" );
4615 return rLoc().getDateFormat();
4618 sal_uInt32 SvNumberformat::GetExactDateOrder() const
4620 sal_uInt32 nRet = 0;
4621 if ( (eType & css::util::NumberFormat::DATE) != css::util::NumberFormat::DATE )
4623 SAL_WARN( "svl.numbers", "SvNumberformat::GetExactDateOrder: no date" );
4624 return nRet;
4626 short const * const pType = NumFor[0].Info().nTypeArray;
4627 sal_uInt16 nAnz = NumFor[0].GetCount();
4628 int nShift = 0;
4629 for ( sal_uInt16 j=0; j<nAnz && nShift < 3; j++ )
4631 switch ( pType[j] )
4633 case NF_KEY_D :
4634 case NF_KEY_DD :
4635 nRet = (nRet << 8) | 'D';
4636 ++nShift;
4637 break;
4638 case NF_KEY_M :
4639 case NF_KEY_MM :
4640 case NF_KEY_MMM :
4641 case NF_KEY_MMMM :
4642 case NF_KEY_MMMMM :
4643 nRet = (nRet << 8) | 'M';
4644 ++nShift;
4645 break;
4646 case NF_KEY_YY :
4647 case NF_KEY_YYYY :
4648 case NF_KEY_EC :
4649 case NF_KEY_EEC :
4650 case NF_KEY_R :
4651 case NF_KEY_RR :
4652 nRet = (nRet << 8) | 'Y';
4653 ++nShift;
4654 break;
4657 return nRet;
4660 void SvNumberformat::GetConditions( SvNumberformatLimitOps& rOper1, double& rVal1,
4661 SvNumberformatLimitOps& rOper2, double& rVal2 ) const
4663 rOper1 = eOp1;
4664 rOper2 = eOp2;
4665 rVal1 = fLimit1;
4666 rVal2 = fLimit2;
4669 Color* SvNumberformat::GetColor( sal_uInt16 nNumFor ) const
4671 if ( nNumFor > 3 )
4673 return NULL;
4675 return NumFor[nNumFor].GetColor();
4678 static void lcl_SvNumberformat_AddLimitStringImpl( OUString& rStr,
4679 SvNumberformatLimitOps eOp,
4680 double fLimit, const OUString& rDecSep )
4682 if ( eOp != NUMBERFORMAT_OP_NO )
4684 switch ( eOp )
4686 case NUMBERFORMAT_OP_EQ :
4687 rStr = "[=";
4688 break;
4689 case NUMBERFORMAT_OP_NE :
4690 rStr = "[<>";
4691 break;
4692 case NUMBERFORMAT_OP_LT :
4693 rStr = "[<";
4694 break;
4695 case NUMBERFORMAT_OP_LE :
4696 rStr = "[<=";
4697 break;
4698 case NUMBERFORMAT_OP_GT :
4699 rStr = "[>";
4700 break;
4701 case NUMBERFORMAT_OP_GE :
4702 rStr = "[>=";
4703 break;
4704 default:
4705 SAL_WARN( "svl.numbers", "unsupported number format" );
4706 break;
4708 rStr += ::rtl::math::doubleToUString( fLimit,
4709 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
4710 rDecSep[0], true);
4711 rStr += "]";
4715 OUString SvNumberformat::GetMappedFormatstring( const NfKeywordTable& rKeywords,
4716 const LocaleDataWrapper& rLocWrp,
4717 bool bDontQuote ) const
4719 OUStringBuffer aStr;
4720 bool bDefault[4];
4721 // 1 subformat matches all if no condition specified,
4722 bDefault[0] = ( NumFor[1].GetCount() == 0 && eOp1 == NUMBERFORMAT_OP_NO );
4723 // with 2 subformats [>=0];[<0] is implied if no condition specified
4724 bDefault[1] = ( !bDefault[0] && NumFor[2].GetCount() == 0 &&
4725 eOp1 == NUMBERFORMAT_OP_GE && fLimit1 == 0.0 &&
4726 eOp2 == NUMBERFORMAT_OP_NO && fLimit2 == 0.0 );
4727 // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified,
4728 // note that subformats may be empty (;;;) and NumFor[2].GetnAnz()>0 is not checked.
4729 bDefault[2] = ( !bDefault[0] && !bDefault[1] &&
4730 eOp1 == NUMBERFORMAT_OP_GT && fLimit1 == 0.0 &&
4731 eOp2 == NUMBERFORMAT_OP_LT && fLimit2 == 0.0 );
4732 bool bDefaults = bDefault[0] || bDefault[1] || bDefault[2];
4733 // from now on bDefault[] values are used to append empty subformats at the end
4734 bDefault[3] = false;
4735 if ( !bDefaults )
4737 // conditions specified
4738 if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 == NUMBERFORMAT_OP_NO )
4740 bDefault[0] = bDefault[1] = true; // [];x
4742 else if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 != NUMBERFORMAT_OP_NO &&
4743 NumFor[2].GetCount() == 0 )
4745 bDefault[0] = bDefault[1] = bDefault[2] = bDefault[3] = true; // [];[];;
4747 // nothing to do if conditions specified for every subformat
4749 else if ( bDefault[0] )
4751 bDefault[0] = false; // a single unconditional subformat is never delimited
4753 else
4755 if ( bDefault[2] && NumFor[2].GetCount() == 0 && NumFor[1].GetCount() > 0 )
4757 bDefault[3] = true; // special cases x;x;; and ;x;;
4759 for ( int i=0; i<3 && !bDefault[i]; ++i )
4761 bDefault[i] = true;
4764 int nSem = 0; // needed ';' delimiters
4765 int nSub = 0; // subformats delimited so far
4766 for ( int n=0; n<4; n++ )
4768 if ( n > 0 )
4770 nSem++;
4772 OUString aPrefix;
4773 bool LCIDInserted = false;
4775 if ( !bDefaults )
4777 switch ( n )
4779 case 0 :
4780 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp1,
4781 fLimit1, rLocWrp.getNumDecimalSep() );
4782 break;
4783 case 1 :
4784 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp2,
4785 fLimit2, rLocWrp.getNumDecimalSep() );
4786 break;
4790 const OUString& rColorName = NumFor[n].GetColorName();
4791 if ( !rColorName.isEmpty() )
4793 const NfKeywordTable & rKey = rScan.GetKeywords();
4794 for ( int j = NF_KEY_FIRSTCOLOR; j <= NF_KEY_LASTCOLOR; j++ )
4796 if ( rKey[j] == rColorName )
4798 aPrefix += "[";
4799 aPrefix += rKeywords[j];
4800 aPrefix += "]";
4801 break; // for
4806 const SvNumberNatNum& rNum = NumFor[n].GetNatNum();
4808 sal_uInt16 nAnz = NumFor[n].GetCount();
4809 if ( nSem && (nAnz || !aPrefix.isEmpty()) )
4811 for ( ; nSem; --nSem )
4813 aStr.append( ';' );
4815 for ( ; nSub <= n; ++nSub )
4817 bDefault[nSub] = false;
4821 if ( !aPrefix.isEmpty() )
4823 aStr.append( aPrefix );
4825 if ( nAnz )
4827 const short* pType = NumFor[n].Info().nTypeArray;
4828 const OUString* pStr = NumFor[n].Info().sStrArray;
4829 for ( sal_uInt16 j=0; j<nAnz; j++ )
4831 if ( 0 <= pType[j] && pType[j] < NF_KEYWORD_ENTRIES_COUNT )
4833 aStr.append( rKeywords[pType[j]] );
4834 if( NF_KEY_NNNN == pType[j] )
4836 aStr.append( rLocWrp.getLongDateDayOfWeekSep() );
4839 else
4841 switch ( pType[j] )
4843 case NF_SYMBOLTYPE_DECSEP :
4844 aStr.append( rLocWrp.getNumDecimalSep() );
4845 break;
4846 case NF_SYMBOLTYPE_THSEP :
4847 aStr.append( rLocWrp.getNumThousandSep() );
4848 break;
4849 case NF_SYMBOLTYPE_EXP :
4850 // tdf#95677: Excel does not support exponent without sign
4851 aStr.append( rKeywords[NF_KEY_E] );
4852 aStr.append( "+" );
4853 break;
4854 case NF_SYMBOLTYPE_DATESEP :
4855 aStr.append( rLocWrp.getDateSep() );
4856 break;
4857 case NF_SYMBOLTYPE_TIMESEP :
4858 aStr.append( rLocWrp.getTimeSep() );
4859 break;
4860 case NF_SYMBOLTYPE_TIME100SECSEP :
4861 aStr.append( rLocWrp.getTime100SecSep() );
4862 break;
4863 case NF_SYMBOLTYPE_STRING :
4864 if( bDontQuote )
4866 aStr.append( pStr[j] );
4868 else if ( pStr[j].getLength() == 1 )
4870 aStr.append( '\\' );
4871 aStr.append( pStr[j] );
4873 else
4875 aStr.append( '"' );
4876 aStr.append( pStr[j] );
4877 aStr.append( '"' );
4879 break;
4880 case NF_SYMBOLTYPE_CALDEL :
4881 if ( pStr[j+1] == "buddhist" )
4883 aStr.insert( 0, "[$-" );
4884 if ( rNum.IsSet() && rNum.GetNatNum() == 1 &&
4885 MsLangId::getRealLanguage( rNum.GetLang() ) ==
4886 LANGUAGE_THAI )
4888 aStr.insert( 3, "D07041E]" ); // date in Thai digit, Buddhist era
4890 else
4892 aStr.insert( 3, "107041E]" ); // date in Arabic digit, Buddhist era
4894 j = j+2;
4896 LCIDInserted = true;
4897 break;
4898 default:
4899 aStr.append( pStr[j] );
4904 // The Thai T NatNum modifier during Xcl export.
4905 if (rNum.IsSet() && rNum.GetNatNum() == 1 &&
4906 rKeywords[NF_KEY_THAI_T] == "T" &&
4907 MsLangId::getRealLanguage( rNum.GetLang()) ==
4908 LANGUAGE_THAI && !LCIDInserted )
4911 aStr.insert( 0, "[$-D00041E]" ); // number in Thai digit
4914 for ( ; nSub<4 && bDefault[nSub]; ++nSub )
4915 { // append empty subformats
4916 aStr.append( ';' );
4918 return aStr.makeStringAndClear();
4921 OUString SvNumberformat::ImpGetNatNumString( const SvNumberNatNum& rNum,
4922 sal_Int32 nVal, sal_uInt16 nMinDigits ) const
4924 OUString aStr;
4925 if ( nMinDigits )
4927 if ( nMinDigits == 2 )
4929 // speed up the most common case
4930 if ( 0 <= nVal && nVal < 10 )
4932 sal_Unicode aBuf[2];
4933 aBuf[0] = '0';
4934 aBuf[1] = '0' + nVal;
4935 aStr = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
4937 else
4939 aStr = OUString::number( nVal );
4942 else
4944 OUString aValStr( OUString::number( nVal ) );
4945 if ( aValStr.getLength() >= nMinDigits )
4947 aStr = aValStr;
4949 else
4951 OUStringBuffer aBuf;
4952 for(sal_Int32 index = 0; index < nMinDigits - aValStr.getLength(); ++index)
4954 aBuf.append('0');
4956 aBuf.append(aValStr);
4957 aStr = aBuf.makeStringAndClear();
4961 else
4963 aStr = OUString::number( nVal );
4965 return impTransliterate(aStr, rNum);
4968 OUString SvNumberformat::impTransliterateImpl(const OUString& rStr,
4969 const SvNumberNatNum& rNum ) const
4971 com::sun::star::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
4972 return GetFormatter().GetNatNum()->getNativeNumberString( rStr,
4973 aLocale, rNum.GetNatNum() );
4976 void SvNumberformat::impTransliterateImpl(OUStringBuffer& rStr,
4977 const SvNumberNatNum& rNum ) const
4979 com::sun::star::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
4981 OUString sTemp(rStr.makeStringAndClear());
4982 sTemp = GetFormatter().GetNatNum()->getNativeNumberString( sTemp, aLocale, rNum.GetNatNum() );
4983 rStr.append(sTemp);
4986 void SvNumberformat::GetNatNumXml( com::sun::star::i18n::NativeNumberXmlAttributes& rAttr,
4987 sal_uInt16 nNumFor ) const
4989 if ( nNumFor <= 3 )
4991 const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum();
4992 if ( rNum.IsSet() )
4994 com::sun::star::lang::Locale aLocale(
4995 LanguageTag( rNum.GetLang() ).getLocale() );
4996 rAttr = GetFormatter().GetNatNum()->convertToXmlAttributes(
4997 aLocale, rNum.GetNatNum() );
4999 else
5001 rAttr = com::sun::star::i18n::NativeNumberXmlAttributes();
5004 else
5006 rAttr = com::sun::star::i18n::NativeNumberXmlAttributes();
5010 // static
5011 bool SvNumberformat::HasStringNegativeSign( const OUString& rStr )
5013 // For Sign '-' needs to be at the start or at the end of the string (blanks ignored)
5014 sal_Int32 nLen = rStr.getLength();
5015 if ( !nLen )
5017 return false;
5019 const sal_Unicode* const pBeg = rStr.getStr();
5020 const sal_Unicode* const pEnd = pBeg + nLen;
5021 const sal_Unicode* p = pBeg;
5023 { // Start
5024 if ( *p == '-' )
5026 return true;
5029 while ( *p == ' ' && ++p < pEnd );
5031 p = pEnd - 1;
5034 { // End
5035 if ( *p == '-' )
5037 return true;
5040 while ( *p == ' ' && pBeg < --p );
5041 return false;
5044 // static
5045 bool SvNumberformat::IsInQuote( const OUString& rStr, sal_Int32 nPos,
5046 sal_Unicode cQuote, sal_Unicode cEscIn, sal_Unicode cEscOut )
5048 sal_Int32 nLen = rStr.getLength();
5049 if ( nPos >= nLen )
5051 return false;
5053 const sal_Unicode* p0 = rStr.getStr();
5054 const sal_Unicode* p = p0;
5055 const sal_Unicode* p1 = p0 + nPos;
5056 bool bQuoted = false;
5057 while ( p <= p1 )
5059 if ( *p == cQuote )
5061 if ( p == p0 )
5063 bQuoted = true;
5065 else if ( bQuoted )
5067 if ( *(p-1) != cEscIn )
5069 bQuoted = false;
5072 else
5074 if ( *(p-1) != cEscOut )
5076 bQuoted = true;
5080 p++;
5082 return bQuoted;
5085 // static
5086 sal_Int32 SvNumberformat::GetQuoteEnd( const OUString& rStr, sal_Int32 nPos,
5087 sal_Unicode cQuote, sal_Unicode cEscIn,
5088 sal_Unicode cEscOut )
5090 if ( nPos < 0 )
5092 return -1;
5094 sal_Int32 nLen = rStr.getLength();
5095 if ( nPos >= nLen )
5097 return -1;
5099 if ( !IsInQuote( rStr, nPos, cQuote, cEscIn, cEscOut ) )
5101 if ( rStr[ nPos ] == cQuote )
5103 return nPos; // Closing cQuote
5105 return -1;
5107 const sal_Unicode* p0 = rStr.getStr();
5108 const sal_Unicode* p = p0 + nPos;
5109 const sal_Unicode* p1 = p0 + nLen;
5110 while ( p < p1 )
5112 if ( *p == cQuote && p > p0 && *(p-1) != cEscIn )
5114 return sal::static_int_cast< sal_Int32 >(p - p0);
5116 p++;
5118 return nLen; // End of String
5121 sal_uInt16 SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor ) const
5123 sal_uInt16 nCnt = 0;
5124 sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
5125 short const * const pType = NumFor[nNumFor].Info().nTypeArray;
5126 for ( sal_uInt16 j=0; j<nAnz; ++j )
5128 switch ( pType[j] )
5130 case NF_SYMBOLTYPE_STRING:
5131 case NF_SYMBOLTYPE_CURRENCY:
5132 case NF_SYMBOLTYPE_DATESEP:
5133 case NF_SYMBOLTYPE_TIMESEP:
5134 case NF_SYMBOLTYPE_TIME100SECSEP:
5135 case NF_SYMBOLTYPE_PERCENT:
5136 ++nCnt;
5137 break;
5140 return nCnt;
5143 const CharClass& SvNumberformat::rChrCls() const
5145 return rScan.GetChrCls();
5148 const LocaleDataWrapper& SvNumberformat::rLoc() const
5150 return rScan.GetLoc();
5153 CalendarWrapper& SvNumberformat::GetCal() const
5155 return rScan.GetCal();
5158 const SvNumberFormatter& SvNumberformat::GetFormatter() const
5160 return *rScan.GetNumberformatter();
5163 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */