bump product version to 4.1.6.2
[LibreOffice.git] / svl / source / numbers / zformat.cxx
blobfc9254be53ea1500a0c2b1c08fc66395f1a7be4e
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 <tools/debug.hxx>
27 #include <osl/diagnose.h>
28 #include <i18nlangtag/mslangid.hxx>
29 #include <rtl/math.hxx>
30 #include <rtl/instance.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 "numhead.hxx"
45 #include <unotools/digitgroupingiterator.hxx>
46 #include <svl/nfsymbol.hxx>
48 #include <cmath>
50 using namespace svt;
52 namespace {
53 struct Gregorian : public rtl::StaticWithInit<const OUString, Gregorian>
55 const OUString operator () ()
57 return OUString("gregorian");
61 const sal_uInt16 UPPER_PRECISION = 300; // entirely arbitrary...
62 const double EXP_LOWER_BOUND = 1.0E-4; // prefer scientific notation below this value.
64 } // namespace
66 const double _D_MAX_U_LONG_ = (double) 0xffffffff; // 4294967295.0
67 const double _D_MAX_LONG_ = (double) 0x7fffffff; // 2147483647.0
68 const sal_uInt16 _MAX_FRACTION_PREC = 3;
69 const double D_EPS = 1.0E-2;
71 const double _D_MAX_D_BY_100 = 1.7E306;
72 const double _D_MIN_M_BY_1000 = 2.3E-305;
74 static sal_uInt8 cCharWidths[ 128-32 ] = {
75 1,1,1,2,2,3,2,1,1,1,1,2,1,1,1,1,
76 2,2,2,2,2,2,2,2,2,2,1,1,2,2,2,2,
77 3,2,2,2,2,2,2,3,2,1,2,2,2,3,3,3,
78 2,3,2,2,2,2,2,3,2,2,2,1,1,1,2,2,
79 1,2,2,2,2,2,1,2,2,1,1,2,1,3,2,2,
80 2,2,1,2,1,2,2,2,2,2,2,1,1,1,2,1
83 // static
84 sal_Int32 SvNumberformat::InsertBlanks( OUStringBuffer& r, sal_Int32 nPos, sal_Unicode c )
86 if( c >= 32 )
88 int n = 2; // Default fuer Zeichen > 128 (HACK!)
89 if( c <= 127 )
91 n = (int)cCharWidths[ c - 32 ];
93 while( n-- )
95 r.insert( nPos++, (sal_Unicode)' ');
98 return nPos;
101 static long GetPrecExp( double fAbsVal )
103 DBG_ASSERT( fAbsVal > 0.0, "GetPrecExp: fAbsVal <= 0.0" );
104 if ( fAbsVal < 1e-7 || fAbsVal > 1e7 )
106 // die Schere, ob's schneller ist oder nicht, liegt zwischen 1e6 und 1e7
107 return (long) floor( log10( fAbsVal ) ) + 1;
109 else
111 long nPrecExp = 1;
112 while( fAbsVal < 1 )
114 fAbsVal *= 10;
115 nPrecExp--;
117 while( fAbsVal >= 10 )
119 fAbsVal /= 10;
120 nPrecExp++;
122 return nPrecExp;
126 const sal_uInt16 nNewCurrencyVersionId = 0x434E; // "NC"
127 const sal_Unicode cNewCurrencyMagic = 0x01; // Magic for format code in comment
128 const sal_uInt16 nNewStandardFlagVersionId = 0x4653; // "SF"
130 /***********************Funktion SvNumberformatInfo******************************/
132 void ImpSvNumberformatInfo::Copy( const ImpSvNumberformatInfo& rNumFor, sal_uInt16 nAnz )
134 for (sal_uInt16 i = 0; i < nAnz; ++i)
136 sStrArray[i] = rNumFor.sStrArray[i];
137 nTypeArray[i] = rNumFor.nTypeArray[i];
139 eScannedType = rNumFor.eScannedType;
140 bThousand = rNumFor.bThousand;
141 nThousand = rNumFor.nThousand;
142 nCntPre = rNumFor.nCntPre;
143 nCntPost = rNumFor.nCntPost;
144 nCntExp = rNumFor.nCntExp;
147 void ImpSvNumberformatInfo::Save(SvStream& rStream, sal_uInt16 nAnz) const
149 for (sal_uInt16 i = 0; i < nAnz; i++)
151 rStream.WriteUniOrByteString( sStrArray[i], rStream.GetStreamCharSet() );
152 short nType = nTypeArray[i];
153 switch ( nType )
155 // der Krampf fuer Versionen vor SV_NUMBERFORMATTER_VERSION_NEW_CURR
156 case NF_SYMBOLTYPE_CURRENCY :
157 rStream << short( NF_SYMBOLTYPE_STRING );
158 break;
159 case NF_SYMBOLTYPE_CURRDEL :
160 case NF_SYMBOLTYPE_CURREXT :
161 rStream << short(0); // werden ignoriert (hoffentlich..)
162 break;
163 default:
164 if ( nType > NF_KEY_LASTKEYWORD_SO5 )
166 rStream << short( NF_SYMBOLTYPE_STRING ); // all new keywords are string
168 else
170 rStream << nType;
175 rStream << eScannedType << sal_Bool(bThousand) << nThousand
176 << nCntPre << nCntPost << nCntExp;
179 void ImpSvNumberformatInfo::Load(SvStream& rStream, sal_uInt16 nAnz)
181 for (sal_uInt16 i = 0; i < nAnz; ++i)
183 sStrArray[i] = SvNumberformat::LoadString( rStream );
184 rStream >> nTypeArray[i];
186 sal_Bool bStreamThousand;
187 rStream >> eScannedType >> bStreamThousand >> nThousand
188 >> nCntPre >> nCntPost >> nCntExp;
189 bThousand = bStreamThousand;
192 //============================================================================
194 // static
195 sal_uInt8 SvNumberNatNum::MapDBNumToNatNum( sal_uInt8 nDBNum, LanguageType eLang, bool bDate )
197 sal_uInt8 nNatNum = 0;
198 eLang = MsLangId::getRealLanguage( eLang ); // resolve SYSTEM etc.
199 eLang &= 0x03FF; // 10 bit primary language
200 if ( bDate )
202 if ( nDBNum == 4 && eLang == LANGUAGE_KOREAN )
204 nNatNum = 9;
206 else if ( nDBNum <= 3 )
208 nNatNum = nDBNum; // known to be good for: zh,ja,ko / 1,2,3
211 else
213 switch ( nDBNum )
215 case 1:
216 switch ( eLang )
218 case (LANGUAGE_CHINESE & 0x03FF):
219 nNatNum = 4;
220 break;
221 case (LANGUAGE_JAPANESE & 0x03FF):
222 nNatNum = 1;
223 break;
224 case (LANGUAGE_KOREAN & 0x03FF):
225 nNatNum = 1;
226 break;
228 break;
229 case 2:
230 switch ( eLang )
232 case (LANGUAGE_CHINESE & 0x03FF):
233 nNatNum = 5;
234 break;
235 case (LANGUAGE_JAPANESE & 0x03FF):
236 nNatNum = 4;
237 break;
238 case (LANGUAGE_KOREAN & 0x03FF):
239 nNatNum = 2;
240 break;
242 break;
243 case 3:
244 switch ( eLang )
246 case (LANGUAGE_CHINESE & 0x03FF):
247 nNatNum = 6;
248 break;
249 case (LANGUAGE_JAPANESE & 0x03FF):
250 nNatNum = 5;
251 break;
252 case (LANGUAGE_KOREAN & 0x03FF):
253 nNatNum = 3;
254 break;
256 break;
257 case 4:
258 switch ( eLang )
260 case (LANGUAGE_JAPANESE & 0x03FF):
261 nNatNum = 7;
262 break;
263 case (LANGUAGE_KOREAN & 0x03FF):
264 nNatNum = 9;
265 break;
267 break;
270 return nNatNum;
273 #ifdef THE_FUTURE
274 /* XXX NOTE: even though the MapNatNumToDBNum method is currently unused please
275 * don't remove it in case we'd have to use it for some obscure exports to
276 * Excel. */
278 // static
279 sal_uInt8 SvNumberNatNum::MapNatNumToDBNum( sal_uInt8 nNatNum, LanguageType eLang, bool bDate )
281 sal_uInt8 nDBNum = 0;
282 eLang = MsLangId::getRealLanguage( eLang ); // resolve SYSTEM etc.
283 eLang &= 0x03FF; // 10 bit primary language
284 if ( bDate )
286 if ( nNatNum == 9 && eLang == LANGUAGE_KOREAN )
288 nDBNum = 4;
290 else if ( nNatNum <= 3 )
292 nDBNum = nNatNum; // known to be good for: zh,ja,ko / 1,2,3
295 else
297 switch ( nNatNum )
299 case 1:
300 switch ( eLang )
302 case (LANGUAGE_JAPANESE & 0x03FF):
303 nDBNum = 1;
304 break;
305 case (LANGUAGE_KOREAN & 0x03FF):
306 nDBNum = 1;
307 break;
309 break;
310 case 2:
311 switch ( eLang )
313 case (LANGUAGE_KOREAN & 0x03FF):
314 nDBNum = 2;
315 break;
317 break;
318 case 3:
319 switch ( eLang )
321 case (LANGUAGE_KOREAN & 0x03FF):
322 nDBNum = 3;
323 break;
325 break;
326 case 4:
327 switch ( eLang )
329 case (LANGUAGE_CHINESE & 0x03FF):
330 nDBNum = 1;
331 break;
332 case (LANGUAGE_JAPANESE & 0x03FF):
333 nDBNum = 2;
334 break;
336 break;
337 case 5:
338 switch ( eLang )
340 case (LANGUAGE_CHINESE & 0x03FF):
341 nDBNum = 2;
342 break;
343 case (LANGUAGE_JAPANESE & 0x03FF):
344 nDBNum = 3;
345 break;
347 break;
348 case 6:
349 switch ( eLang )
351 case (LANGUAGE_CHINESE & 0x03FF):
352 nDBNum = 3;
353 break;
355 break;
356 case 7:
357 switch ( eLang )
359 case (LANGUAGE_JAPANESE & 0x03FF):
360 nDBNum = 4;
361 break;
363 break;
364 case 8:
365 break;
366 case 9:
367 switch ( eLang )
369 case (LANGUAGE_KOREAN & 0x03FF):
370 nDBNum = 4;
371 break;
373 break;
374 case 10:
375 break;
376 case 11:
377 break;
380 return nDBNum;
382 #endif
384 /***********************Funktionen SvNumFor******************************/
386 ImpSvNumFor::ImpSvNumFor()
388 nAnzStrings = 0;
389 aI.nTypeArray = NULL;
390 aI.sStrArray = NULL;
391 aI.eScannedType = NUMBERFORMAT_UNDEFINED;
392 aI.bThousand = false;
393 aI.nThousand = 0;
394 aI.nCntPre = 0;
395 aI.nCntPost = 0;
396 aI.nCntExp = 0;
397 pColor = NULL;
400 ImpSvNumFor::~ImpSvNumFor()
402 delete [] aI.sStrArray;
403 delete [] aI.nTypeArray;
406 void ImpSvNumFor::Enlarge(sal_uInt16 nAnz)
408 if ( nAnzStrings != nAnz )
410 delete [] aI.nTypeArray;
411 delete [] aI.sStrArray;
412 nAnzStrings = nAnz;
413 if ( nAnz )
415 aI.nTypeArray = new short[nAnz];
416 aI.sStrArray = new OUString[nAnz];
418 else
420 aI.nTypeArray = NULL;
421 aI.sStrArray = NULL;
426 void ImpSvNumFor::Copy( const ImpSvNumFor& rNumFor, ImpSvNumberformatScan* pSc )
428 Enlarge( rNumFor.nAnzStrings );
429 aI.Copy( rNumFor.aI, nAnzStrings );
430 sColorName = rNumFor.sColorName;
431 if ( pSc )
433 pColor = pSc->GetColor( sColorName ); // #121103# don't copy pointer between documents
435 else
437 pColor = rNumFor.pColor;
439 aNatNum = rNumFor.aNatNum;
442 void ImpSvNumFor::Save(SvStream& rStream) const
444 rStream << nAnzStrings;
445 aI.Save(rStream, nAnzStrings);
446 rStream.WriteUniOrByteString( sColorName, rStream.GetStreamCharSet() );
449 void ImpSvNumFor::Load(SvStream& rStream, ImpSvNumberformatScan& rSc,
450 OUString& rLoadedColorName )
452 sal_uInt16 nAnz;
453 rStream >> nAnz; //! noch nicht direkt nAnzStrings wg. Enlarge
454 Enlarge( nAnz );
455 aI.Load( rStream, nAnz );
456 sColorName = rStream.ReadUniOrByteString( rStream.GetStreamCharSet() );
457 rLoadedColorName = sColorName;
458 pColor = rSc.GetColor(sColorName);
461 bool ImpSvNumFor::HasNewCurrency() const
463 for ( sal_uInt16 j=0; j<nAnzStrings; j++ )
465 if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
467 return true;
470 return false;
473 bool ImpSvNumFor::GetNewCurrencySymbol( OUString& rSymbol,
474 OUString& rExtension ) const
476 for ( sal_uInt16 j=0; j<nAnzStrings; j++ )
478 if ( aI.nTypeArray[j] == NF_SYMBOLTYPE_CURRENCY )
480 rSymbol = aI.sStrArray[j];
481 if ( j < nAnzStrings-1 && aI.nTypeArray[j+1] == NF_SYMBOLTYPE_CURREXT )
483 rExtension = aI.sStrArray[j+1];
485 else
487 rExtension = "";
489 return true;
492 //! kein Erase an rSymbol, rExtension
493 return false;
496 void ImpSvNumFor::SaveNewCurrencyMap( SvStream& rStream ) const
498 sal_uInt16 j;
499 sal_uInt16 nCnt = 0;
500 for ( j=0; j<nAnzStrings; j++ )
502 switch ( aI.nTypeArray[j] )
504 case NF_SYMBOLTYPE_CURRENCY :
505 case NF_SYMBOLTYPE_CURRDEL :
506 case NF_SYMBOLTYPE_CURREXT :
507 nCnt++;
508 break;
511 rStream << nCnt;
512 for ( j=0; j<nAnzStrings; j++ )
514 switch ( aI.nTypeArray[j] )
516 case NF_SYMBOLTYPE_CURRENCY :
517 case NF_SYMBOLTYPE_CURRDEL :
518 case NF_SYMBOLTYPE_CURREXT :
519 rStream << j << aI.nTypeArray[j];
520 break;
525 void ImpSvNumFor::LoadNewCurrencyMap( SvStream& rStream )
527 sal_uInt16 nCnt;
528 rStream >> nCnt;
529 for ( sal_uInt16 j=0; j<nCnt; j++ )
531 sal_uInt16 nPos;
532 short nType;
533 rStream >> nPos >> nType;
534 if ( nPos < nAnzStrings )
536 aI.nTypeArray[nPos] = nType;
541 /***********************Funktionen SvNumberformat************************/
543 enum BracketFormatSymbolType
545 BRACKET_SYMBOLTYPE_FORMAT = -1, // subformat string
546 BRACKET_SYMBOLTYPE_COLOR = -2, // color
547 BRACKET_SYMBOLTYPE_ERROR = -3, // error
548 BRACKET_SYMBOLTYPE_DBNUM1 = -4, // DoubleByteNumber, represent numbers
549 BRACKET_SYMBOLTYPE_DBNUM2 = -5, // using CJK characters, Excel compatible.
550 BRACKET_SYMBOLTYPE_DBNUM3 = -6,
551 BRACKET_SYMBOLTYPE_DBNUM4 = -7,
552 BRACKET_SYMBOLTYPE_DBNUM5 = -8,
553 BRACKET_SYMBOLTYPE_DBNUM6 = -9,
554 BRACKET_SYMBOLTYPE_DBNUM7 = -10,
555 BRACKET_SYMBOLTYPE_DBNUM8 = -11,
556 BRACKET_SYMBOLTYPE_DBNUM9 = -12,
557 BRACKET_SYMBOLTYPE_LOCALE = -13,
558 BRACKET_SYMBOLTYPE_NATNUM0 = -14, // Our NativeNumber support, ASCII
559 BRACKET_SYMBOLTYPE_NATNUM1 = -15, // Our NativeNumber support, represent
560 BRACKET_SYMBOLTYPE_NATNUM2 = -16, // numbers using CJK, CTL, ...
561 BRACKET_SYMBOLTYPE_NATNUM3 = -17,
562 BRACKET_SYMBOLTYPE_NATNUM4 = -18,
563 BRACKET_SYMBOLTYPE_NATNUM5 = -19,
564 BRACKET_SYMBOLTYPE_NATNUM6 = -20,
565 BRACKET_SYMBOLTYPE_NATNUM7 = -21,
566 BRACKET_SYMBOLTYPE_NATNUM8 = -22,
567 BRACKET_SYMBOLTYPE_NATNUM9 = -23,
568 BRACKET_SYMBOLTYPE_NATNUM10 = -24,
569 BRACKET_SYMBOLTYPE_NATNUM11 = -25,
570 BRACKET_SYMBOLTYPE_NATNUM12 = -26,
571 BRACKET_SYMBOLTYPE_NATNUM13 = -27,
572 BRACKET_SYMBOLTYPE_NATNUM14 = -28,
573 BRACKET_SYMBOLTYPE_NATNUM15 = -29,
574 BRACKET_SYMBOLTYPE_NATNUM16 = -30,
575 BRACKET_SYMBOLTYPE_NATNUM17 = -31,
576 BRACKET_SYMBOLTYPE_NATNUM18 = -32,
577 BRACKET_SYMBOLTYPE_NATNUM19 = -33
580 SvNumberformat::SvNumberformat( ImpSvNumberformatScan& rSc, LanguageType eLge )
581 : rScan(rSc)
582 , nNewStandardDefined(0)
583 , bStarFlag( false )
585 maLocale.meLanguage = eLge;
588 void SvNumberformat::ImpCopyNumberformat( const SvNumberformat& rFormat )
590 sFormatstring = rFormat.sFormatstring;
591 eType = rFormat.eType;
592 maLocale = rFormat.maLocale;
593 fLimit1 = rFormat.fLimit1;
594 fLimit2 = rFormat.fLimit2;
595 eOp1 = rFormat.eOp1;
596 eOp2 = rFormat.eOp2;
597 bStandard = rFormat.bStandard;
598 bIsUsed = rFormat.bIsUsed;
599 sComment = rFormat.sComment;
600 nNewStandardDefined = rFormat.nNewStandardDefined;
602 // #121103# when copying between documents, get color pointers from own scanner
603 ImpSvNumberformatScan* pColorSc = ( &rScan != &rFormat.rScan ) ? &rScan : NULL;
605 for (sal_uInt16 i = 0; i < 4; i++)
607 NumFor[i].Copy(rFormat.NumFor[i], pColorSc);
611 SvNumberformat::SvNumberformat( SvNumberformat& rFormat )
612 : rScan(rFormat.rScan), bStarFlag( rFormat.bStarFlag )
614 ImpCopyNumberformat( rFormat );
617 SvNumberformat::SvNumberformat( SvNumberformat& rFormat, ImpSvNumberformatScan& rSc )
618 : rScan(rSc)
619 , bStarFlag( rFormat.bStarFlag )
621 ImpCopyNumberformat( rFormat );
624 static bool lcl_SvNumberformat_IsBracketedPrefix( short nSymbolType )
626 if ( nSymbolType > 0 )
628 return true; // conditions
630 switch ( nSymbolType )
632 case BRACKET_SYMBOLTYPE_COLOR :
633 case BRACKET_SYMBOLTYPE_DBNUM1 :
634 case BRACKET_SYMBOLTYPE_DBNUM2 :
635 case BRACKET_SYMBOLTYPE_DBNUM3 :
636 case BRACKET_SYMBOLTYPE_DBNUM4 :
637 case BRACKET_SYMBOLTYPE_DBNUM5 :
638 case BRACKET_SYMBOLTYPE_DBNUM6 :
639 case BRACKET_SYMBOLTYPE_DBNUM7 :
640 case BRACKET_SYMBOLTYPE_DBNUM8 :
641 case BRACKET_SYMBOLTYPE_DBNUM9 :
642 case BRACKET_SYMBOLTYPE_LOCALE :
643 case BRACKET_SYMBOLTYPE_NATNUM0 :
644 case BRACKET_SYMBOLTYPE_NATNUM1 :
645 case BRACKET_SYMBOLTYPE_NATNUM2 :
646 case BRACKET_SYMBOLTYPE_NATNUM3 :
647 case BRACKET_SYMBOLTYPE_NATNUM4 :
648 case BRACKET_SYMBOLTYPE_NATNUM5 :
649 case BRACKET_SYMBOLTYPE_NATNUM6 :
650 case BRACKET_SYMBOLTYPE_NATNUM7 :
651 case BRACKET_SYMBOLTYPE_NATNUM8 :
652 case BRACKET_SYMBOLTYPE_NATNUM9 :
653 case BRACKET_SYMBOLTYPE_NATNUM10 :
654 case BRACKET_SYMBOLTYPE_NATNUM11 :
655 case BRACKET_SYMBOLTYPE_NATNUM12 :
656 case BRACKET_SYMBOLTYPE_NATNUM13 :
657 case BRACKET_SYMBOLTYPE_NATNUM14 :
658 case BRACKET_SYMBOLTYPE_NATNUM15 :
659 case BRACKET_SYMBOLTYPE_NATNUM16 :
660 case BRACKET_SYMBOLTYPE_NATNUM17 :
661 case BRACKET_SYMBOLTYPE_NATNUM18 :
662 case BRACKET_SYMBOLTYPE_NATNUM19 :
663 return true;
665 return false;
669 OUString SvNumberformat::ImpObtainCalendarAndNumerals( OUStringBuffer & rString, sal_Int32 & nPos,
670 LanguageType & nLang, const LocaleType & aTmpLocale )
672 OUString sCalendar;
673 /* TODO: this could be enhanced to allow other possible locale dependent
674 * calendars and numerals. BUT only if our locale data allows it! For LCID
675 * numerals and calendars see
676 * http://office.microsoft.com/en-us/excel/HA010346351033.aspx */
677 if (MsLangId::getRealLanguage( aTmpLocale.meLanguage) == LANGUAGE_THAI)
679 // Numeral shape code "D" = Thai digits.
680 if (aTmpLocale.mnNumeralShape == 0xD)
682 rString.insert( nPos, "[NatNum1]");
684 // Calendar type code "07" = Thai Buddhist calendar, insert this after
685 // all prefixes have been consumed as it is actually a format modifier
686 // and not a prefix.
687 if (aTmpLocale.mnCalendarType == 0x07)
689 // Currently calendars are tied to the locale of the entire number
690 // format, e.g. [~buddhist] in en_US doesn't work.
691 // => Having different locales in sub formats does not work!
692 /* TODO: calendars could be tied to a sub format's NatNum info
693 * instead, or even better be available for any locale. Needs a
694 * different implementation of GetCal() and locale data calendars.
695 * */
696 // If this is not Thai yet, make it so.
697 if (MsLangId::getRealLanguage( maLocale.meLanguage) != LANGUAGE_THAI)
699 maLocale = aTmpLocale;
700 nLang = maLocale.meLanguage = LANGUAGE_THAI;
702 sCalendar="[~buddhist]";
705 return sCalendar;
709 SvNumberformat::SvNumberformat(OUString& rString,
710 ImpSvNumberformatScan* pSc,
711 ImpSvNumberInputScan* pISc,
712 sal_Int32& nCheckPos,
713 LanguageType& eLan,
714 bool bStan)
715 : rScan(*pSc)
716 , nNewStandardDefined(0)
717 , bStarFlag( false )
719 OUStringBuffer sBuff(rString);
721 // If the group (AKA thousand) separator is a Non-Breaking Space (French)
722 // replace all occurrences by a simple space.
723 // The tokens will be changed to the LocaleData separator again later on.
724 const sal_Unicode cNBSp = 0xA0;
725 const OUString& rThSep = GetFormatter().GetNumThousandSep();
726 if ( rThSep.getLength() == 1 && rThSep[0] == cNBSp )
728 sBuff.replace( cNBSp, ' ');
731 if (rScan.GetConvertMode())
733 maLocale.meLanguage = rScan.GetNewLnge();
734 eLan = maLocale.meLanguage; // Wechsel auch zurueckgeben
736 else
738 maLocale.meLanguage = eLan;
740 bStandard = bStan;
741 bIsUsed = false;
742 fLimit1 = 0.0;
743 fLimit2 = 0.0;
744 eOp1 = NUMBERFORMAT_OP_NO;
745 eOp2 = NUMBERFORMAT_OP_NO;
746 eType = NUMBERFORMAT_DEFINED;
748 bool bCancel = false;
749 bool bCondition = false;
750 short eSymbolType;
751 sal_Int32 nPos = 0;
752 sal_Int32 nPosOld;
753 nCheckPos = 0;
755 // Split into 4 sub formats
756 sal_uInt16 nIndex;
757 for ( nIndex = 0; nIndex < 4 && !bCancel; nIndex++ )
759 // Original language/country may have to be reestablished
760 if (rScan.GetConvertMode())
762 (rScan.GetNumberformatter())->ChangeIntl(rScan.GetTmpLnge());
764 OUString sInsertCalendar; // a calendar resulting from parsing LCID
765 OUString sStr;
766 nPosOld = nPos; // Start position of substring
767 // first get bracketed prefixes; e.g. conditions, color
770 eSymbolType = ImpNextSymbol(sBuff, nPos, sStr);
771 if (eSymbolType > 0) // condition
773 if ( nIndex == 0 && !bCondition )
775 bCondition = true;
776 eOp1 = (SvNumberformatLimitOps) eSymbolType;
778 else if ( nIndex == 1 && bCondition )
780 eOp2 = (SvNumberformatLimitOps) eSymbolType;
782 else // error
784 bCancel = true; // break for
785 nCheckPos = nPosOld;
787 if (!bCancel)
789 double fNumber;
790 sal_Int32 nAnzChars = ImpGetNumber(sBuff, nPos, sStr);
791 if (nAnzChars > 0)
793 short F_Type = NUMBERFORMAT_UNDEFINED;
794 if (!pISc->IsNumberFormat(sStr,F_Type,fNumber) ||
795 ( F_Type != NUMBERFORMAT_NUMBER &&
796 F_Type != NUMBERFORMAT_SCIENTIFIC) )
798 fNumber = 0.0;
799 nPos = nPos - nAnzChars;
800 sBuff.remove(nPos, nAnzChars);
801 sBuff.insert(nPos, (sal_Unicode)'0');
802 nPos++;
805 else
807 fNumber = 0.0;
808 sBuff.insert(nPos++,(sal_Unicode)'0');
810 if (nIndex == 0)
812 fLimit1 = fNumber;
814 else
816 fLimit2 = fNumber;
818 if ( sBuff[nPos] == ']' )
820 nPos++;
822 else
824 bCancel = true; // break for
825 nCheckPos = nPos;
828 nPosOld = nPos; // position before string
830 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
832 OUString sSymbol( sStr);
833 switch ( eSymbolType )
835 case BRACKET_SYMBOLTYPE_COLOR :
836 if ( NumFor[nIndex].GetColor() != NULL )
837 { // error, more than one color
838 bCancel = true; // break for
839 nCheckPos = nPosOld;
841 else
843 Color* pColor = pSc->GetColor( sStr);
844 NumFor[nIndex].SetColor( pColor, sStr);
845 if (pColor == NULL)
846 { // error
847 bCancel = true; // break for
848 nCheckPos = nPosOld;
851 break;
852 case BRACKET_SYMBOLTYPE_NATNUM0 :
853 case BRACKET_SYMBOLTYPE_NATNUM1 :
854 case BRACKET_SYMBOLTYPE_NATNUM2 :
855 case BRACKET_SYMBOLTYPE_NATNUM3 :
856 case BRACKET_SYMBOLTYPE_NATNUM4 :
857 case BRACKET_SYMBOLTYPE_NATNUM5 :
858 case BRACKET_SYMBOLTYPE_NATNUM6 :
859 case BRACKET_SYMBOLTYPE_NATNUM7 :
860 case BRACKET_SYMBOLTYPE_NATNUM8 :
861 case BRACKET_SYMBOLTYPE_NATNUM9 :
862 case BRACKET_SYMBOLTYPE_NATNUM10 :
863 case BRACKET_SYMBOLTYPE_NATNUM11 :
864 case BRACKET_SYMBOLTYPE_NATNUM12 :
865 case BRACKET_SYMBOLTYPE_NATNUM13 :
866 case BRACKET_SYMBOLTYPE_NATNUM14 :
867 case BRACKET_SYMBOLTYPE_NATNUM15 :
868 case BRACKET_SYMBOLTYPE_NATNUM16 :
869 case BRACKET_SYMBOLTYPE_NATNUM17 :
870 case BRACKET_SYMBOLTYPE_NATNUM18 :
871 case BRACKET_SYMBOLTYPE_NATNUM19 :
872 if ( NumFor[nIndex].GetNatNum().IsSet() )
874 bCancel = true; // break for
875 nCheckPos = nPosOld;
877 else
879 sStr = "NatNum";
880 //! eSymbolType is negative
881 sal_uInt8 nNum = (sal_uInt8)(0 - (eSymbolType - BRACKET_SYMBOLTYPE_NATNUM0));
882 sStr += OUString::valueOf( (sal_Int32)nNum );
883 NumFor[nIndex].SetNatNumNum( nNum, false );
885 break;
886 case BRACKET_SYMBOLTYPE_DBNUM1 :
887 case BRACKET_SYMBOLTYPE_DBNUM2 :
888 case BRACKET_SYMBOLTYPE_DBNUM3 :
889 case BRACKET_SYMBOLTYPE_DBNUM4 :
890 case BRACKET_SYMBOLTYPE_DBNUM5 :
891 case BRACKET_SYMBOLTYPE_DBNUM6 :
892 case BRACKET_SYMBOLTYPE_DBNUM7 :
893 case BRACKET_SYMBOLTYPE_DBNUM8 :
894 case BRACKET_SYMBOLTYPE_DBNUM9 :
895 if ( NumFor[nIndex].GetNatNum().IsSet() )
897 bCancel = true; // break for
898 nCheckPos = nPosOld;
900 else
902 sStr = "DBNum";
903 //! eSymbolType is negative
904 sal_uInt8 nNum = (sal_uInt8)(1 - (eSymbolType - BRACKET_SYMBOLTYPE_DBNUM1));
905 sStr += OUString((sal_Unicode)('0' + nNum));
906 NumFor[nIndex].SetNatNumNum( nNum, true );
908 break;
909 case BRACKET_SYMBOLTYPE_LOCALE :
910 if ( NumFor[nIndex].GetNatNum().GetLang() != LANGUAGE_DONTKNOW ||
911 sBuff[nPos-1] != ']' )
912 // Check also for ']' to avoid pulling in
913 // locale data for the preview string for not
914 // yet completed LCIDs in the dialog.
916 bCancel = true; // break for
917 nCheckPos = nPosOld;
919 else
921 sal_Int32 nTmp = 2;
922 LocaleType aTmpLocale( ImpGetLocaleType( sStr, nTmp));
923 if (aTmpLocale.meLanguage == LANGUAGE_DONTKNOW)
925 bCancel = true; // break for
926 nCheckPos = nPosOld;
928 else
930 // Only the first sub format's locale will be
931 // used as the format's overall locale.
932 // Sorts this also under the corresponding
933 // locale for the dialog.
934 // If we don't support the locale this would
935 // result in an unknown (empty) language
936 // listbox entry and the user would never see
937 // this format.
938 if (nIndex == 0 && (aTmpLocale.meLanguage == 0 ||
939 SvNumberFormatter::IsLocaleInstalled( aTmpLocale.meLanguage)))
941 maLocale = aTmpLocale;
942 eLan = aTmpLocale.meLanguage; // return to caller
943 /* TODO: fiddle with scanner to make this
944 * known? A change in the locale may affect
945 * separators and keywords. On the other
946 * hand they may have been entered as used
947 * in the originating locale, there's no
948 * way to predict other than analyzing the
949 * format code, we assume here the current
950 * context is used, which is most likely
951 * the case.
952 * */
954 sStr = "$-" + aTmpLocale.generateCode();
955 NumFor[nIndex].SetNatNumLang( MsLangId::getRealLanguage( aTmpLocale.meLanguage));
957 // "$-NNCCLLLL" Numerals and Calendar
958 if (sSymbol.getLength() > 6)
960 sInsertCalendar = ImpObtainCalendarAndNumerals( sBuff, nPos, eLan, aTmpLocale);
962 /* NOTE: there can be only one calendar
963 * inserted so the last one wins, though
964 * our own calendar modifiers support
965 * multiple calendars within one sub format
966 * code if at different positions. */
969 break;
971 if ( !bCancel )
973 if (sStr == sSymbol)
975 nPosOld = nPos;
977 else
979 sBuff.remove(nPosOld, nPos - nPosOld);
980 if (!sStr.isEmpty())
982 sBuff.insert(nPosOld, sStr);
983 nPos = nPosOld + sStr.getLength();
984 sBuff.insert(nPos, "]");
985 sBuff.insert(nPosOld, "[");
986 nPos += 2;
987 nPosOld = nPos; // position before string
989 else
991 nPos = nPosOld; // prefix removed for whatever reason
997 while ( !bCancel && lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) );
999 // The remaining format code string
1000 if ( !bCancel )
1002 if (eSymbolType == BRACKET_SYMBOLTYPE_FORMAT)
1004 if (nIndex == 1 && eOp1 == NUMBERFORMAT_OP_NO)
1006 eOp1 = NUMBERFORMAT_OP_GT; // undefined condition, default: > 0
1008 else if (nIndex == 2 && eOp2 == NUMBERFORMAT_OP_NO)
1010 eOp2 = NUMBERFORMAT_OP_LT; // undefined condition, default: < 0
1012 if (sStr.isEmpty())
1014 // empty sub format
1016 else
1018 if (!sInsertCalendar.isEmpty())
1020 sStr = sInsertCalendar + sStr;
1022 sal_Int32 nStrPos = pSc->ScanFormat( sStr);
1023 sal_uInt16 nAnz = pSc->GetAnzResStrings();
1024 if (nAnz == 0) // error
1026 nStrPos = 1;
1028 if (nStrPos == 0) // ok
1030 // e.g. Thai T speciality
1031 if (pSc->GetNatNumModifier() && !NumFor[nIndex].GetNatNum().IsSet())
1033 sStr = "[NatNum" + OUString::valueOf( sal_Int32(pSc->GetNatNumModifier())) + "]" + sStr;
1034 NumFor[nIndex].SetNatNumNum( pSc->GetNatNumModifier(), false );
1036 // #i53826# #i42727# For the Thai T speciality we need
1037 // to freeze the locale and immunize it against
1038 // conversions during exports, just in case we want to
1039 // save to Xcl. This disables the feature of being able
1040 // to convert a NatNum to another locale. You can't
1041 // have both.
1042 // FIXME: implement a specialized export conversion
1043 // that works on tokens (have to tokenize all first)
1044 // and doesn't use the format string and
1045 // PutandConvertEntry() to LANGUAGE_ENGLISH_US in
1046 // sc/source/filter/excel/xestyle.cxx
1047 // XclExpNumFmtBuffer::WriteFormatRecord().
1048 LanguageType eLanguage;
1049 if (NumFor[nIndex].GetNatNum().GetNatNum() == 1 &&
1050 ((eLanguage = MsLangId::getRealLanguage( eLan)) == LANGUAGE_THAI) &&
1051 NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW)
1053 sStr = "[$-" + OUString::valueOf( sal_Int32(eLanguage), 16 ).toAsciiUpperCase() + "]" + sStr;
1054 NumFor[nIndex].SetNatNumLang( eLanguage);
1056 sBuff.remove(nPosOld, nPos - nPosOld);
1057 sBuff.insert(nPosOld, sStr);
1058 nPos = nPosOld + sStr.getLength();
1059 if (nPos < sBuff.getLength())
1061 sBuff.insert(nPos, ";");
1062 nPos++;
1064 NumFor[nIndex].Enlarge(nAnz);
1065 pSc->CopyInfo(&(NumFor[nIndex].Info()), nAnz);
1066 // type check
1067 if (nIndex == 0)
1069 eType = (short) NumFor[nIndex].Info().eScannedType;
1071 else if (nIndex == 3)
1072 { // #77026# Everything recognized IS text
1073 NumFor[nIndex].Info().eScannedType = NUMBERFORMAT_TEXT;
1075 else if ( (short) NumFor[nIndex].Info().eScannedType != eType)
1077 eType = NUMBERFORMAT_DEFINED;
1080 else
1082 nCheckPos = nPosOld + nStrPos; // error in string
1083 bCancel = true; // break for
1087 else if (eSymbolType == BRACKET_SYMBOLTYPE_ERROR) // error
1089 nCheckPos = nPosOld;
1090 bCancel = true;
1092 else if ( lcl_SvNumberformat_IsBracketedPrefix( eSymbolType ) )
1094 nCheckPos = nPosOld + 1; // error, prefix in string
1095 bCancel = true; // break for
1098 if ( bCancel && !nCheckPos )
1100 nCheckPos = 1; // nCheckPos is used as an error condition
1102 if ( !bCancel )
1104 if ( NumFor[nIndex].GetNatNum().IsSet() &&
1105 NumFor[nIndex].GetNatNum().GetLang() == LANGUAGE_DONTKNOW )
1107 NumFor[nIndex].SetNatNumLang( eLan );
1110 if (sBuff.getLength() == nPos)
1112 if ( nIndex == 2 && eSymbolType == BRACKET_SYMBOLTYPE_FORMAT &&
1113 sBuff[nPos - 1] == ';' )
1115 // #83510# A 4th subformat explicitly specified to be empty
1116 // hides any text. Need the type here for HasTextFormat()
1117 NumFor[3].Info().eScannedType = NUMBERFORMAT_TEXT;
1119 bCancel = true;
1121 if ( NumFor[nIndex].GetNatNum().IsSet() )
1123 NumFor[nIndex].SetNatNumDate( (NumFor[nIndex].Info().eScannedType & NUMBERFORMAT_DATE) != 0 );
1127 if ( bCondition && !nCheckPos )
1129 if ( nIndex == 1 && NumFor[0].GetCount() == 0 &&
1130 sBuff[sBuff.getLength() - 1] != ';' )
1132 // No format code => GENERAL but not if specified empty
1133 OUString aAdd( pSc->GetStandardName() );
1134 if ( !pSc->ScanFormat( aAdd ) )
1136 sal_uInt16 nAnz = pSc->GetAnzResStrings();
1137 if ( nAnz )
1139 NumFor[0].Enlarge(nAnz);
1140 pSc->CopyInfo( &(NumFor[0].Info()), nAnz );
1141 sBuff.append(aAdd);
1145 else if ( nIndex == 1 && NumFor[nIndex].GetCount() == 0 &&
1146 sBuff[sBuff.getLength() - 1] != ';' &&
1147 (NumFor[0].GetCount() > 1 ||
1148 (NumFor[0].GetCount() == 1 &&
1149 NumFor[0].Info().nTypeArray[0] != NF_KEY_GENERAL)) )
1151 // No trailing second subformat => GENERAL but not if specified empty
1152 // and not if first subformat is GENERAL
1153 OUString aAdd( pSc->GetStandardName() );
1154 if ( !pSc->ScanFormat( aAdd ) )
1156 sal_uInt16 nAnz = pSc->GetAnzResStrings();
1157 if ( nAnz )
1159 NumFor[nIndex].Enlarge(nAnz);
1160 pSc->CopyInfo( &(NumFor[nIndex].Info()), nAnz );
1161 sBuff.append(";");
1162 sBuff.append(aAdd);
1166 else if ( nIndex == 2 && NumFor[nIndex].GetCount() == 0 &&
1167 sBuff[sBuff.getLength() - 1] != ';' &&
1168 eOp2 != NUMBERFORMAT_OP_NO )
1170 // No trailing third subformat => GENERAL but not if specified empty
1171 OUString aAdd( pSc->GetStandardName() );
1172 if ( !pSc->ScanFormat( aAdd ) )
1174 sal_uInt16 nAnz = pSc->GetAnzResStrings();
1175 if ( nAnz )
1177 NumFor[nIndex].Enlarge(nAnz);
1178 pSc->CopyInfo( &(NumFor[nIndex].Info()), nAnz );
1179 sBuff.append(";");
1180 sBuff.append(aAdd);
1185 rString = sBuff.makeStringAndClear();
1186 sFormatstring = rString;
1188 if (NumFor[2].GetCount() == 0 && // kein 3. Teilstring
1189 eOp1 == NUMBERFORMAT_OP_GT && eOp2 == NUMBERFORMAT_OP_NO &&
1190 fLimit1 == 0.0 && fLimit2 == 0.0)
1192 eOp1 = NUMBERFORMAT_OP_GE; // 0 zum ersten Format dazu
1197 SvNumberformat::~SvNumberformat()
1201 //---------------------------------------------------------------------------
1202 // Next_Symbol
1203 //---------------------------------------------------------------------------
1204 // Zerlegt die Eingabe in Symbole fuer die weitere
1205 // Verarbeitung (Turing-Maschine).
1206 //---------------------------------------------------------------------------
1207 // Ausgangs Zustand = SsStart
1208 //---------------+-------------------+-----------------------+---------------
1209 // Alter Zustand | gelesenes Zeichen | Aktion | Neuer Zustand
1210 //---------------+-------------------+-----------------------+---------------
1211 // SsStart | ; | Pos-- | SsGetString
1212 // | [ | Symbol += Zeichen | SsGetBracketed
1213 // | ] | Fehler | SsStop
1214 // | BLANK | |
1215 // | Sonst | Symbol += Zeichen | SsGetString
1216 //---------------+-------------------+-----------------------+---------------
1217 // SsGetString | ; | | SsStop
1218 // | Sonst | Symbol+=Zeichen |
1219 //---------------+-------------------+-----------------------+---------------
1220 // SsGetBracketed| <, > = | del [ |
1221 // | | Symbol += Zeichen | SsGetCon
1222 // | BLANK | |
1223 // | h, H, m, M, s, S | Symbol += Zeichen | SsGetTime
1224 // | sonst | del [ |
1225 // | | Symbol += Zeichen | SsGetPrefix
1226 //---------------+-------------------+-----------------------+---------------
1227 // SsGetTime | ] | Symbol += Zeichen | SsGetString
1228 // | h, H, m, M, s, S | Symbol += Zeichen, * | SsGetString
1229 // | sonst | del [; Symbol+=Zeichen| SsGetPrefix
1230 //---------------+-------------------+-----------------------+---------------
1231 // SsGetPrefix | ] | | SsStop
1232 // | sonst | Symbol += Zeichen |
1233 //---------------+-------------------+-----------------------+---------------
1234 // SsGetCon | >, = | Symbol+=Zeichen |
1235 // | ] | | SsStop
1236 // | sonst | Fehler | SsStop
1237 //---------------+-------------------+-----------------------+---------------
1238 // * : Sonderbedingung
1240 enum ScanState
1242 SsStop,
1243 SsStart,
1244 SsGetCon, // condition
1245 SsGetString, // format string
1246 SsGetPrefix, // color or NatNumN
1247 SsGetTime, // [HH] for time
1248 SsGetBracketed // any [...] not decided yet
1251 // read a string until ']' and delete spaces in input
1252 // static
1253 sal_Int32 SvNumberformat::ImpGetNumber(OUStringBuffer& rString,
1254 sal_Int32& nPos,
1255 OUString& sSymbol)
1257 sal_Int32 nStartPos = nPos;
1258 sal_Unicode cToken;
1259 sal_Int32 nLen = rString.getLength();
1260 OUStringBuffer sBuffSymbol;
1261 while ( nPos < nLen && ((cToken = rString[nPos]) != ']') )
1263 if (cToken == ' ')
1264 { // delete spaces
1265 rString.remove(nPos,1);
1266 nLen--;
1268 else
1270 nPos++;
1271 sBuffSymbol.append(cToken);
1274 sSymbol = sBuffSymbol.makeStringAndClear();
1275 return nPos - nStartPos;
1278 namespace {
1280 sal_Unicode toUniChar(sal_uInt8 n)
1282 sal_Char c;
1283 if (n < 10)
1285 c = '0' + n;
1287 else
1289 c = 'A' + n - 10;
1291 return sal_Unicode(c);
1294 bool IsCombiningSymbol( OUStringBuffer& rStringBuffer, sal_Int32 nPos )
1296 bool bRet = false;
1297 while (nPos >= 0)
1299 switch (rStringBuffer[nPos])
1301 case '*':
1302 case '\\':
1303 case '_':
1304 bRet = !bRet;
1305 --nPos;
1306 break;
1307 default:
1308 return bRet;
1311 return bRet;
1314 } // namespace
1316 OUString SvNumberformat::LocaleType::generateCode() const
1318 OUStringBuffer aBuf;
1319 #if 0
1320 // TODO: We may re-enable this later. Don't remove it! --Kohei
1321 if (mnNumeralShape)
1323 sal_uInt8 nVal = mnNumeralShape;
1324 for (sal_uInt8 i = 0; i < 2; ++i)
1326 sal_uInt8 n = (nVal & 0xF0) >> 4;
1327 if (n || aBuf.getLength())
1329 aBuf.append(toUniChar(n));
1331 nVal = nVal << 4;
1335 if (mnNumeralShape || mnCalendarType)
1337 sal_uInt8 nVal = mnCalendarType;
1338 for (sal_uInt8 i = 0; i < 2; ++i)
1340 sal_uInt8 n = (nVal & 0xF0) >> 4;
1341 if (n || aBuf.getLength())
1343 aBuf.append(toUniChar(n));
1345 nVal = nVal << 4;
1348 #endif
1350 sal_uInt16 n16 = static_cast<sal_uInt16>(meLanguage);
1351 for (sal_uInt8 i = 0; i < 4; ++i)
1353 sal_uInt8 n = static_cast<sal_uInt8>((n16 & 0xF000) >> 12);
1354 // Omit leading zeros for consistency.
1355 if (n || aBuf.getLength() || i == 3)
1357 aBuf.append(toUniChar(n));
1359 n16 = n16 << 4;
1362 return aBuf.makeStringAndClear();
1365 SvNumberformat::LocaleType::LocaleType()
1366 : mnNumeralShape(0)
1367 , mnCalendarType(0)
1368 , meLanguage(LANGUAGE_DONTKNOW)
1372 SvNumberformat::LocaleType::LocaleType(sal_uInt32 nRawNum)
1373 : mnNumeralShape(0)
1374 , mnCalendarType(0)
1375 , meLanguage(LANGUAGE_DONTKNOW)
1377 meLanguage = static_cast<LanguageType>(nRawNum & 0x0000FFFF);
1378 nRawNum = (nRawNum >> 16);
1379 mnCalendarType = static_cast<sal_uInt8>(nRawNum & 0xFF);
1380 nRawNum = (nRawNum >> 8);
1381 mnNumeralShape = static_cast<sal_uInt8>(nRawNum & 0xFF);
1384 // static
1385 SvNumberformat::LocaleType SvNumberformat::ImpGetLocaleType(const OUString& rString, sal_Int32& nPos )
1387 sal_uInt32 nNum = 0;
1388 sal_Unicode cToken = 0;
1389 sal_Int32 nStart = nPos;
1390 sal_Int32 nLen = rString.getLength();
1391 while ( nPos < nLen && (nPos - nStart < 8) && ((cToken = rString[nPos]) != ']') )
1393 if ( '0' <= cToken && cToken <= '9' )
1395 nNum *= 16;
1396 nNum += cToken - '0';
1398 else if ( 'a' <= cToken && cToken <= 'f' )
1400 nNum *= 16;
1401 nNum += cToken - 'a' + 10;
1403 else if ( 'A' <= cToken && cToken <= 'F' )
1405 nNum *= 16;
1406 nNum += cToken - 'A' + 10;
1408 else
1410 return LANGUAGE_DONTKNOW;
1412 ++nPos;
1415 return (cToken == ']' || nPos == nLen) ? LocaleType(nNum) : LocaleType();
1418 static bool lcl_matchKeywordAndGetNumber( const OUString & rString, const sal_Int32 nPos,
1419 const OUString & rKeyword, sal_Int32 & nNumber )
1421 if (0 <= nPos && nPos + rKeyword.getLength() < rString.getLength() && rString.matchIgnoreAsciiCase( rKeyword, nPos))
1423 nNumber = rString.copy( nPos + rKeyword.getLength()).toInt32();
1424 return true;
1426 else
1428 nNumber = 0;
1429 return false;
1433 short SvNumberformat::ImpNextSymbol(OUStringBuffer& rString,
1434 sal_Int32& nPos,
1435 OUString& sSymbol)
1437 short eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1438 sal_Unicode cToken;
1439 sal_Unicode cLetter = ' '; // Zwischenergebnis
1440 sal_Int32 nLen = rString.getLength();
1441 ScanState eState = SsStart;
1442 OUStringBuffer sBuffSymbol;
1444 const NfKeywordTable & rKeywords = rScan.GetKeywords();
1445 while (nPos < nLen && eState != SsStop)
1447 cToken = rString[nPos];
1448 nPos++;
1449 switch (eState)
1451 case SsStart:
1452 if (cToken == '[')
1454 eState = SsGetBracketed;
1455 sBuffSymbol.append(cToken);
1457 else if (cToken == ';')
1459 eState = SsGetString;
1460 nPos--;
1461 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1463 else if (cToken == ']')
1465 eState = SsStop;
1466 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1468 else if (cToken == ' ') // Skip Blanks
1470 nPos--;
1471 rString.remove(nPos, 1);
1472 nLen--;
1474 else
1476 sBuffSymbol.append(cToken);
1477 eState = SsGetString;
1478 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1480 break;
1481 case SsGetBracketed:
1482 switch (cToken)
1484 case '<':
1485 case '>':
1486 case '=':
1487 sBuffSymbol.stripStart((sal_Unicode)'[');
1488 sBuffSymbol.append(cToken);
1489 cLetter = cToken;
1490 eState = SsGetCon;
1491 switch (cToken)
1493 case '<':
1494 eSymbolType = NUMBERFORMAT_OP_LT;
1495 break;
1496 case '>':
1497 eSymbolType = NUMBERFORMAT_OP_GT;
1498 break;
1499 case '=':
1500 eSymbolType = NUMBERFORMAT_OP_EQ;
1501 break;
1502 default: break;
1504 break;
1505 case ' ':
1506 nPos--;
1507 rString.remove(nPos, 1);
1508 nLen--;
1509 break;
1510 case '$' :
1511 if ( rString[nPos] == '-' )
1513 // [$-xxx] locale
1514 sBuffSymbol.stripStart((sal_Unicode)'[');
1515 eSymbolType = BRACKET_SYMBOLTYPE_LOCALE;
1516 eState = SsGetPrefix;
1518 else
1519 { // currency as of SV_NUMBERFORMATTER_VERSION_NEW_CURR
1520 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1521 eState = SsGetString;
1523 sBuffSymbol.append(cToken);
1524 break;
1525 case '~' :
1526 // calendarID as of SV_NUMBERFORMATTER_VERSION_CALENDAR
1527 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1528 sBuffSymbol.append(cToken);
1529 eState = SsGetString;
1530 break;
1531 default:
1533 const OUString aNatNum("NATNUM");
1534 const OUString aDBNum("DBNUM");
1535 const OUString aBufStr( rString.toString());
1536 sal_Int32 nNatNumNum;
1537 sal_Int32 nDBNum;
1538 if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aNatNum, nNatNumNum) &&
1539 0 <= nNatNumNum && nNatNumNum <= 19 )
1541 sBuffSymbol.stripStart((sal_Unicode)'[');
1542 sBuffSymbol.append( aBufStr.copy( --nPos, aNatNum.getLength()+1 ));
1543 nPos += aNatNum.getLength()+1;
1544 //! SymbolType is negative
1545 eSymbolType = (short) (BRACKET_SYMBOLTYPE_NATNUM0 - nNatNumNum);
1546 eState = SsGetPrefix;
1548 else if ( lcl_matchKeywordAndGetNumber( aBufStr, nPos-1, aDBNum, nDBNum) &&
1549 '1' <= nDBNum && nDBNum <= '9' )
1551 sBuffSymbol.stripStart((sal_Unicode)'[');
1552 sBuffSymbol.append( aBufStr.copy( --nPos, aDBNum.getLength()+1 ));
1553 nPos += aDBNum.getLength()+1;
1554 //! SymbolType is negative
1555 eSymbolType = sal::static_int_cast< short >( BRACKET_SYMBOLTYPE_DBNUM1 - (nDBNum - '1'));
1556 eState = SsGetPrefix;
1558 else
1560 sal_Unicode cUpper = rChrCls().uppercase( aBufStr, nPos-1, 1)[0];
1561 if ( cUpper == rKeywords[NF_KEY_H][0] || // H
1562 cUpper == rKeywords[NF_KEY_MI][0] || // M
1563 cUpper == rKeywords[NF_KEY_S][0] ) // S
1565 sBuffSymbol.append(cToken);
1566 eState = SsGetTime;
1567 cLetter = cToken;
1569 else
1571 sBuffSymbol.stripStart((sal_Unicode)'[');
1572 sBuffSymbol.append(cToken);
1573 eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1574 eState = SsGetPrefix;
1579 break;
1580 case SsGetString:
1581 if (cToken == ';' && (nPos < 2 || !IsCombiningSymbol( rString, nPos-2)))
1583 eState = SsStop;
1585 else
1587 sBuffSymbol.append(cToken);
1589 break;
1590 case SsGetTime:
1591 if (cToken == ']')
1593 sBuffSymbol.append(cToken);
1594 eState = SsGetString;
1595 eSymbolType = BRACKET_SYMBOLTYPE_FORMAT;
1597 else
1599 sal_Unicode cUpper = rChrCls().uppercase(rString.toString(), nPos-1, 1)[0];
1600 if (cUpper == rKeywords[NF_KEY_H][0] || // H
1601 cUpper == rKeywords[NF_KEY_MI][0] || // M
1602 cUpper == rKeywords[NF_KEY_S][0] ) // S
1604 if (cLetter == cToken)
1606 sBuffSymbol.append(cToken);
1607 cLetter = ' ';
1609 else
1611 sBuffSymbol.stripStart((sal_Unicode)'[');
1612 sBuffSymbol.append(cToken);
1613 eState = SsGetPrefix;
1616 else
1618 sBuffSymbol.stripStart((sal_Unicode)'[');
1619 sBuffSymbol.append(cToken);
1620 eSymbolType = BRACKET_SYMBOLTYPE_COLOR;
1621 eState = SsGetPrefix;
1624 break;
1625 case SsGetCon:
1626 switch (cToken)
1628 case '<':
1629 eState = SsStop;
1630 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1631 break;
1632 case '>':
1633 if (cLetter == '<')
1635 sBuffSymbol.append(cToken);
1636 cLetter = ' ';
1637 eState = SsStop;
1638 eSymbolType = NUMBERFORMAT_OP_NE;
1640 else
1642 eState = SsStop;
1643 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1645 break;
1646 case '=':
1647 if (cLetter == '<')
1649 sBuffSymbol.append(cToken);
1650 cLetter = ' ';
1651 eSymbolType = NUMBERFORMAT_OP_LE;
1653 else if (cLetter == '>')
1655 sBuffSymbol.append(cToken);
1656 cLetter = ' ';
1657 eSymbolType = NUMBERFORMAT_OP_GE;
1659 else
1661 eState = SsStop;
1662 eSymbolType = BRACKET_SYMBOLTYPE_ERROR;
1664 break;
1665 case ' ':
1666 nPos--;
1667 rString.remove(nPos,1);
1668 nLen--;
1669 break;
1670 default:
1671 eState = SsStop;
1672 nPos--;
1673 break;
1675 break;
1676 case SsGetPrefix:
1677 if (cToken == ']')
1679 eState = SsStop;
1681 else
1683 sBuffSymbol.append(cToken);
1685 break;
1686 default:
1687 break;
1688 } // of switch
1689 } // of while
1690 sSymbol = sBuffSymbol.makeStringAndClear();
1691 return eSymbolType;
1694 NfHackConversion SvNumberformat::Load( SvStream& rStream,
1695 ImpSvNumMultipleReadHeader& rHdr,
1696 SvNumberFormatter* pHackConverter,
1697 ImpSvNumberInputScan& rISc )
1699 rHdr.StartEntry();
1700 sal_uInt16 nOp1, nOp2;
1701 sFormatstring = SvNumberformat::LoadString( rStream );
1702 sal_Bool bStreamStandard, bStreamUsed;
1703 rStream >> eType >> fLimit1 >> fLimit2
1704 >> nOp1 >> nOp2 >> bStreamStandard >> bStreamUsed;
1705 bStandard = bStreamStandard;
1706 bIsUsed = bStreamUsed;
1707 NfHackConversion eHackConversion = NF_CONVERT_NONE;
1708 bool bOldConvert = false;
1709 LanguageType eOldTmpLang = 0;
1710 LanguageType eOldNewLang = 0;
1711 if ( pHackConverter )
1713 // werden nur hierbei gebraucht
1714 bOldConvert = rScan.GetConvertMode();
1715 eOldTmpLang = rScan.GetTmpLnge();
1716 eOldNewLang = rScan.GetNewLnge();
1718 OUString aLoadedColorName;
1719 for (sal_uInt16 i = 0; i < 4; i++)
1721 NumFor[i].Load( rStream, rScan, aLoadedColorName );
1722 if ( pHackConverter && eHackConversion == NF_CONVERT_NONE )
1724 //! HACK! ER 29.07.97 13:52
1725 // leider wurde nicht gespeichert, was SYSTEM on Save wirklich war :-/
1726 // aber immerhin wird manchmal fuer einen Entry FARBE oder COLOR gespeichert..
1727 // System-German FARBE nach System-xxx COLOR umsetzen und vice versa,
1728 //! geht davon aus, dass onSave nur GERMAN und ENGLISH KeyWords in
1729 //! ImpSvNumberformatScan existierten
1730 if ( !aLoadedColorName.isEmpty() &&
1731 !NumFor[i].GetColor() &&
1732 aLoadedColorName != rScan.GetColorString() )
1734 if ( rScan.GetColorString() == "FARBE" )
1735 { // English -> German
1736 eHackConversion = NF_CONVERT_ENGLISH_GERMAN;
1737 rScan.GetNumberformatter()->ChangeIntl( LANGUAGE_ENGLISH_US );
1738 rScan.SetConvertMode( LANGUAGE_ENGLISH_US, LANGUAGE_GERMAN );
1740 else
1741 { // German -> English
1742 eHackConversion = NF_CONVERT_GERMAN_ENGLISH;
1743 rScan.GetNumberformatter()->ChangeIntl( LANGUAGE_GERMAN );
1744 rScan.SetConvertMode( LANGUAGE_GERMAN, LANGUAGE_ENGLISH_US );
1746 OUString aColorName = NumFor[i].GetColorName();
1747 const Color* pColor = rScan.GetColor( aColorName );
1748 if ( !pColor && aLoadedColorName == aColorName )
1750 eHackConversion = NF_CONVERT_NONE;
1752 rScan.GetNumberformatter()->ChangeIntl( LANGUAGE_SYSTEM );
1753 rScan.SetConvertMode( eOldTmpLang, eOldNewLang );
1754 rScan.SetConvertMode( bOldConvert );
1758 eOp1 = (SvNumberformatLimitOps) nOp1;
1759 eOp2 = (SvNumberformatLimitOps) nOp2;
1760 OUString aComment; // wird nach dem NewCurrency-Geraffel richtig gesetzt
1761 if ( rHdr.BytesLeft() )
1763 // ab SV_NUMBERFORMATTER_VERSION_NEWSTANDARD
1764 aComment = SvNumberformat::LoadString( rStream );
1765 rStream >> nNewStandardDefined;
1768 sal_Int32 nNewCurrencyEnd = -1;
1769 bool bNewCurrencyComment = ( aComment.getLength() > 1 && aComment[0] == cNewCurrencyMagic &&
1770 (nNewCurrencyEnd = aComment.indexOf( cNewCurrencyMagic, 1 )) >= 0 );
1771 bool bNewCurrencyLoaded = false;
1772 bool bNewCurrency = false;
1774 bool bGoOn = true;
1775 while ( rHdr.BytesLeft() && bGoOn )
1777 // as of SV_NUMBERFORMATTER_VERSION_NEW_CURR
1778 sal_uInt16 nId;
1779 sal_Bool bStreamCurr;
1780 rStream >> nId;
1781 switch ( nId )
1783 case nNewCurrencyVersionId :
1784 bNewCurrencyLoaded = true;
1785 rStream >> bStreamCurr;
1786 bNewCurrency = bStreamCurr;
1787 if ( bNewCurrency )
1789 for ( sal_uInt16 j=0; j<4; j++ )
1791 NumFor[j].LoadNewCurrencyMap( rStream );
1794 break;
1795 case nNewStandardFlagVersionId :
1796 rStream >> bStreamStandard; // the real standard flag
1797 bStandard = bStreamStandard;
1798 break;
1799 default:
1800 SAL_WARN( "svl.numbers", "SvNumberformat::Load: unknown header bytes left nId" );
1801 bGoOn = false; // stop reading unknown stream left over of newer versions
1802 // Would be nice to have multiple read/write headers instead
1803 // but old versions wouldn't know it, TLOT.
1806 rHdr.EndEntry();
1808 if ( bNewCurrencyLoaded )
1810 if ( bNewCurrency && bNewCurrencyComment )
1811 { // original Formatstring und Kommentar wiederherstellen
1812 sFormatstring = aComment.copy( 1, nNewCurrencyEnd-1 );
1813 if(nNewCurrencyEnd + 1 < aComment.getLength())
1815 aComment = aComment.copy(nNewCurrencyEnd + 1 );
1817 else
1819 aComment = "";
1823 else if ( bNewCurrencyComment )
1825 // neu, aber mit Version vor SV_NUMBERFORMATTER_VERSION_NEW_CURR gespeichert
1826 // original Formatstring und Kommentar wiederherstellen
1827 sFormatstring = aComment.copy( 1, nNewCurrencyEnd - 1 );
1828 if(nNewCurrencyEnd + 1 < aComment.getLength())
1830 aComment = aComment.copy(nNewCurrencyEnd + 1 );
1832 else
1834 aComment = "";
1836 // Zustaende merken
1837 short nDefined = ( eType & NUMBERFORMAT_DEFINED );
1838 sal_uInt16 nNewStandard = nNewStandardDefined;
1839 // neu parsen etc.
1840 OUString aStr( sFormatstring );
1841 sal_Int32 nCheckPos = 0;
1842 SvNumberformat* pFormat = new SvNumberformat( aStr, &rScan, &rISc,
1843 nCheckPos, maLocale.meLanguage, bStandard );
1844 DBG_ASSERT( !nCheckPos, "SvNumberformat::Load: NewCurrencyRescan nCheckPos" );
1845 ImpCopyNumberformat( *pFormat );
1846 delete pFormat;
1847 // Zustaende wiederherstellen
1848 eType |= nDefined;
1849 if ( nNewStandard )
1851 SetNewStandardDefined( nNewStandard );
1854 SetComment( aComment );
1856 if ( eHackConversion != NF_CONVERT_NONE )
1858 //! und weiter mit dem HACK!
1859 switch ( eHackConversion )
1861 case NF_CONVERT_ENGLISH_GERMAN :
1862 ConvertLanguage( *pHackConverter,
1863 LANGUAGE_ENGLISH_US, LANGUAGE_GERMAN, true );
1864 break;
1865 case NF_CONVERT_GERMAN_ENGLISH :
1866 ConvertLanguage( *pHackConverter,
1867 LANGUAGE_GERMAN, LANGUAGE_ENGLISH_US, true );
1868 break;
1869 default:
1870 SAL_WARN( "svl.numbers", "SvNumberformat::Load: eHackConversion unknown" );
1873 return eHackConversion;
1876 void SvNumberformat::ConvertLanguage( SvNumberFormatter& rConverter,
1877 LanguageType eConvertFrom,
1878 LanguageType eConvertTo, bool bSystem )
1880 sal_Int32 nCheckPos;
1881 sal_uInt32 nKey;
1882 short nType = eType;
1883 OUString aFormatString( sFormatstring );
1884 if ( bSystem )
1886 rConverter.PutandConvertEntrySystem( aFormatString, nCheckPos, nType,
1887 nKey, eConvertFrom, eConvertTo );
1889 else
1891 rConverter.PutandConvertEntry( aFormatString, nCheckPos, nType,
1892 nKey, eConvertFrom, eConvertTo );
1894 const SvNumberformat* pFormat = rConverter.GetEntry( nKey );
1895 DBG_ASSERT( pFormat, "SvNumberformat::ConvertLanguage: Conversion ohne Format" );
1896 if ( pFormat )
1898 ImpCopyNumberformat( *pFormat );
1899 // aus Formatter/Scanner uebernommene Werte zuruecksetzen
1900 if ( bSystem )
1902 maLocale.meLanguage = LANGUAGE_SYSTEM;
1904 // pColor zeigt noch auf Tabelle in temporaerem Formatter/Scanner
1905 for ( sal_uInt16 i = 0; i < 4; i++ )
1907 OUString aColorName = NumFor[i].GetColorName();
1908 Color* pColor = rScan.GetColor( aColorName );
1909 NumFor[i].SetColor( pColor, aColorName );
1914 // static
1915 OUString SvNumberformat::LoadString( SvStream& rStream )
1917 CharSet eStream = rStream.GetStreamCharSet();
1918 OString aStr = read_lenPrefixed_uInt8s_ToOString<sal_uInt16>(rStream);
1919 sal_Char cStream = NfCurrencyEntry::GetEuroSymbol( eStream );
1920 if (aStr.indexOf(cStream) < 0)
1922 // simple conversion to unicode
1923 return OStringToOUString(aStr, eStream);
1925 sal_Unicode cSource = OUString(&cStream, 1, eStream).toChar();
1926 sal_Unicode cTarget = NfCurrencyEntry::GetEuroSymbol();
1927 OUStringBuffer aBuf(OStringToOUString(aStr, eStream));
1928 aBuf.replace(cSource, cTarget);
1930 return aBuf.makeStringAndClear();
1933 void SvNumberformat::Save( SvStream& rStream, ImpSvNumMultipleWriteHeader& rHdr ) const
1935 OUString aFormatstring( sFormatstring );
1936 OUStringBuffer aComment( sComment.getLength() + sFormatstring.getLength() + 2 );
1938 bool bNewCurrency = HasNewCurrency();
1939 if ( bNewCurrency )
1941 // SV_NUMBERFORMATTER_VERSION_NEW_CURR im Kommentar speichern
1942 aComment.insert( 0, cNewCurrencyMagic );
1943 aComment.insert( 0, cNewCurrencyMagic );
1944 aComment.insert( 1, aFormatstring );
1945 Build50Formatstring( aFormatstring ); // alten Formatstring generieren
1948 // old SO5 versions do behave strange (no output) if standard flag is set
1949 // on formats not prepared for it (not having the following exact types)
1950 bool bOldStandard = bStandard;
1951 if ( bOldStandard )
1953 switch ( eType )
1955 case NUMBERFORMAT_NUMBER :
1956 case NUMBERFORMAT_DATE :
1957 case NUMBERFORMAT_TIME :
1958 case NUMBERFORMAT_DATETIME :
1959 case NUMBERFORMAT_PERCENT :
1960 case NUMBERFORMAT_SCIENTIFIC :
1961 // ok to save
1962 break;
1963 default:
1964 bOldStandard = false;
1968 rHdr.StartEntry();
1969 rStream.WriteUniOrByteString( aFormatstring, rStream.GetStreamCharSet() );
1970 rStream << eType << fLimit1 << fLimit2 << (sal_uInt16) eOp1 << (sal_uInt16) eOp2
1971 << sal_Bool(bOldStandard) << sal_Bool(bIsUsed);
1972 for (sal_uInt16 i = 0; i < 4; i++)
1974 NumFor[i].Save(rStream);
1976 // ab SV_NUMBERFORMATTER_VERSION_NEWSTANDARD
1977 rStream.WriteUniOrByteString( aComment.makeStringAndClear(), rStream.GetStreamCharSet() );
1978 rStream << nNewStandardDefined;
1979 // ab SV_NUMBERFORMATTER_VERSION_NEW_CURR
1980 rStream << nNewCurrencyVersionId;
1981 rStream << sal_Bool(bNewCurrency);
1982 if ( bNewCurrency )
1984 for ( sal_uInt16 j=0; j<4; j++ )
1986 NumFor[j].SaveNewCurrencyMap( rStream );
1990 // the real standard flag to load with versions >638 if different
1991 if ( bStandard != bOldStandard )
1993 rStream << nNewStandardFlagVersionId;
1994 rStream << (sal_Bool)bStandard;
1997 rHdr.EndEntry();
2000 bool SvNumberformat::HasNewCurrency() const
2002 for ( sal_uInt16 j=0; j<4; j++ )
2004 if ( NumFor[j].HasNewCurrency() )
2006 return true;
2009 return false;
2012 bool SvNumberformat::GetNewCurrencySymbol( OUString& rSymbol,
2013 OUString& rExtension ) const
2015 for ( sal_uInt16 j=0; j<4; j++ )
2017 if ( NumFor[j].GetNewCurrencySymbol( rSymbol, rExtension ) )
2019 return true;
2022 rSymbol = "";
2023 rExtension = "";
2024 return false;
2027 // static
2028 OUString SvNumberformat::StripNewCurrencyDelimiters( const OUString& rStr,
2029 bool bQuoteSymbol )
2031 OUString aTmp;
2032 OUString aSource(rStr);
2033 sal_Int32 nStartPos, nPos, nLen;
2034 nLen = aSource.getLength();
2035 nStartPos = 0;
2036 while ( (nPos = aSource.indexOf( "[$", nStartPos )) >= 0 )
2038 sal_Int32 nEnd;
2039 if ( (nEnd = GetQuoteEnd( aSource, nPos )) >= 0 )
2041 aTmp += aSource.copy( nStartPos, ++nEnd - nStartPos );
2042 nStartPos = nEnd;
2044 else
2046 aTmp += aSource.copy( nStartPos, nPos - nStartPos );
2047 nStartPos = nPos + 2;
2048 sal_Int32 nDash;
2049 nEnd = nStartPos - 1;
2052 nDash = aSource.indexOf( '-', ++nEnd );
2054 while ( (nEnd = GetQuoteEnd( aSource, nDash )) >= 0 );
2055 sal_Int32 nClose;
2056 nEnd = nStartPos - 1;
2059 nClose = aSource.indexOf( ']', ++nEnd );
2061 while ( (nEnd = GetQuoteEnd( aSource, nClose )) >= 0 );
2063 if(nClose < 0)
2065 /* there should always be a closing ]
2066 * but the old String class would have hidden
2067 * that. so be conservative too
2069 nClose = nLen;
2072 nPos = nClose;
2073 if(nDash >= 0 && nDash < nClose)
2075 nPos = nDash;
2077 if ( !bQuoteSymbol || aSource[ nStartPos ] == '"' )
2079 aTmp += aSource.copy( nStartPos, nPos - nStartPos );
2081 else
2083 aTmp += "\"";
2084 aTmp += aSource.copy( nStartPos, nPos - nStartPos );
2085 aTmp += "\"";
2087 nStartPos = nClose + 1;
2090 if ( nLen > nStartPos )
2092 aTmp += aSource.copy( nStartPos, nLen - nStartPos );
2094 return aTmp;
2097 void SvNumberformat::Build50Formatstring( OUString& rStr ) const
2099 rStr = StripNewCurrencyDelimiters( sFormatstring, true );
2102 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUStringBuffer& OutString)
2104 OUString sTemp;
2105 ImpGetOutputStandard(fNumber, sTemp);
2106 OutString = sTemp;
2109 void SvNumberformat::ImpGetOutputStandard(double& fNumber, OUString& OutString)
2111 sal_uInt16 nStandardPrec = rScan.GetStandardPrec();
2113 if ( fabs(fNumber) > 1.0E15 ) // #58531# war E16
2115 nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals
2116 OutString = ::rtl::math::doubleToUString( fNumber,
2117 rtl_math_StringFormat_E, nStandardPrec /*2*/,
2118 GetFormatter().GetNumDecimalSep()[0]);
2120 else
2122 ImpGetOutputStdToPrecision(fNumber, OutString, nStandardPrec);
2126 void SvNumberformat::ImpGetOutputStdToPrecision(double& rNumber, OUString& rOutString, sal_uInt16 nPrecision) const
2128 // Make sure the precision doesn't go over the maximum allowable precision.
2129 nPrecision = ::std::min(UPPER_PRECISION, nPrecision);
2131 #if 0
2133 // debugger test case for ANSI standard correctness
2134 OUString aTest;
2135 // expect 0.00123 OK
2136 aTest = ::rtl::math::doubleToUString( 0.001234567,
2137 rtl_math_StringFormat_G, 3, '.', true );
2138 // expect 123 OK
2139 aTest = ::rtl::math::doubleToUString( 123.4567,
2140 rtl_math_StringFormat_G, 3, '.', true );
2141 // expect 123.5 OK
2142 aTest = ::rtl::math::doubleToUString( 123.4567,
2143 rtl_math_StringFormat_G, 4, '.', true );
2144 // expect 1e+03 (as 999.6 rounded to 3 significant digits results in
2145 // 1000 with an exponent equal to significant digits)
2146 // Currently (24-Jan-2003) we do fail in this case and output 1000
2147 // instead, negligible.
2148 aTest = ::rtl::math::doubleToUString( 999.6,
2149 rtl_math_StringFormat_G, 3, '.', true );
2150 // expect what? result is 1.2e+004
2151 aTest = ::rtl::math::doubleToUString( 12345.6789,
2152 rtl_math_StringFormat_G, -3, '.', true );
2154 #endif
2156 // We decided to strip trailing zeros unconditionally, since binary
2157 // double-precision rounding error makes it impossible to determine e.g.
2158 // whether 844.10000000000002273737 is what the user has typed, or the
2159 // user has typed 844.1 but IEEE 754 represents it that way internally.
2161 rOutString = ::rtl::math::doubleToUString( rNumber,
2162 rtl_math_StringFormat_F, nPrecision /*2*/,
2163 GetFormatter().GetNumDecimalSep()[0], true );
2164 if (rOutString[0] == (sal_Unicode)'-' &&
2165 comphelper::string::getTokenCount(rOutString, '0') == rOutString.getLength())
2167 rOutString = comphelper::string::stripStart(rOutString, '-'); // nicht -0
2169 rOutString = impTransliterate(rOutString, NumFor[0].GetNatNum());
2172 void SvNumberformat::ImpGetOutputInputLine(double fNumber, OUString& OutString)
2174 bool bModified = false;
2175 if ( (eType & NUMBERFORMAT_PERCENT) && (fabs(fNumber) < _D_MAX_D_BY_100))
2177 if (fNumber == 0.0)
2179 OutString = "0%";
2180 return;
2182 fNumber *= 100;
2183 bModified = true;
2186 if (fNumber == 0.0)
2188 OutString = "0";
2189 return;
2192 OutString = ::rtl::math::doubleToUString( fNumber,
2193 rtl_math_StringFormat_Automatic,
2194 rtl_math_DecimalPlaces_Max,
2195 GetFormatter().GetNumDecimalSep()[0], true );
2197 if ( eType & NUMBERFORMAT_PERCENT && bModified)
2199 OutString += "%";
2201 return;
2204 short SvNumberformat::ImpCheckCondition(double& fNumber,
2205 double& fLimit,
2206 SvNumberformatLimitOps eOp)
2208 switch(eOp)
2210 case NUMBERFORMAT_OP_NO:
2211 return -1;
2212 case NUMBERFORMAT_OP_EQ:
2213 return (short) (fNumber == fLimit);
2214 case NUMBERFORMAT_OP_NE:
2215 return (short) (fNumber != fLimit);
2216 case NUMBERFORMAT_OP_LT:
2217 return (short) (fNumber < fLimit);
2218 case NUMBERFORMAT_OP_LE:
2219 return (short) (fNumber <= fLimit);
2220 case NUMBERFORMAT_OP_GT:
2221 return (short) (fNumber > fLimit);
2222 case NUMBERFORMAT_OP_GE:
2223 return (short) (fNumber >= fLimit);
2224 default:
2225 return -1;
2229 bool SvNumberformat::GetOutputString(const OUString& sString,
2230 OUString& OutString,
2231 Color** ppColor)
2233 OUStringBuffer sOutBuff;
2234 sal_uInt16 nIx;
2235 if (eType & NUMBERFORMAT_TEXT)
2237 nIx = 0;
2239 else if (NumFor[3].GetCount() > 0)
2241 nIx = 3;
2243 else
2245 *ppColor = NULL; // no change of color
2246 return false;
2248 *ppColor = NumFor[nIx].GetColor();
2249 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2250 bool bRes = false;
2251 if (rInfo.eScannedType == NUMBERFORMAT_TEXT)
2253 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
2254 for (sal_uInt16 i = 0; i < nAnz; i++)
2256 switch (rInfo.nTypeArray[i])
2258 case NF_SYMBOLTYPE_STAR:
2259 if( bStarFlag )
2261 sOutBuff.append((sal_Unicode) 0x1B);
2262 sOutBuff.append(rInfo.sStrArray[i][1]);
2263 bRes = true;
2265 break;
2266 case NF_SYMBOLTYPE_BLANK:
2267 InsertBlanks( sOutBuff, sOutBuff.getLength(),
2268 rInfo.sStrArray[i][1] );
2269 break;
2270 case NF_KEY_GENERAL : // #77026# "General" is the same as "@"
2271 case NF_SYMBOLTYPE_DEL :
2272 sOutBuff.append(sString);
2273 break;
2274 default:
2275 sOutBuff.append(rInfo.sStrArray[i]);
2279 OutString = sOutBuff.makeStringAndClear();
2280 return bRes;
2283 sal_uLong SvNumberformat::ImpGGT(sal_uLong x, sal_uLong y)
2285 if (y == 0)
2287 return x;
2289 else
2291 sal_uLong z = x%y;
2292 while (z)
2294 x = y;
2295 y = z;
2296 z = x%y;
2298 return y;
2302 sal_uLong SvNumberformat::ImpGGTRound(sal_uLong x, sal_uLong y)
2304 if (y == 0)
2306 return x;
2308 else
2310 sal_uLong z = x%y;
2311 while ((double)z/(double)y > D_EPS)
2313 x = y;
2314 y = z;
2315 z = x%y;
2317 return y;
2321 namespace {
2323 void lcl_GetOutputStringScientific(double fNumber, sal_uInt16 nCharCount,
2324 const SvNumberFormatter& rFormatter, OUString& rOutString)
2326 bool bSign = ::rtl::math::isSignBitSet(fNumber);
2328 // 1.000E+015 (one digit and the decimal point, and the five chars for the exponential part, totalling 7).
2329 sal_uInt16 nPrec = nCharCount > 7 ? nCharCount - 7 : 0;
2330 if (nPrec && bSign)
2332 // Make room for the negative sign.
2333 --nPrec;
2335 nPrec = ::std::min(nPrec, static_cast<sal_uInt16>(14)); // limit to 14 decimals.
2337 rOutString = ::rtl::math::doubleToUString(fNumber, rtl_math_StringFormat_E,
2338 nPrec, rFormatter.GetNumDecimalSep()[0]);
2341 sal_Int32 lcl_GetForcedDenominator(const ImpSvNumberformatInfo &rInfo, sal_uInt16 nAnz)
2343 sal_uInt16 i;
2344 OUString aDiv;
2345 for( i = 0; i < nAnz; i++ )
2347 if( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_FRAC_FDIV )
2349 aDiv += rInfo.sStrArray[i];
2352 return aDiv.toInt32();
2355 // TODO: More optimizations?
2356 void lcl_ForcedDenominator(sal_uLong &nFrac, sal_uLong &nDiv, sal_uLong nForcedDiv)
2358 double fFrac = (double)nFrac / (double)nDiv;
2359 double fMultiplier = (double)nForcedDiv / (double)nDiv;
2360 nFrac = (sal_uLong)( (double)nFrac * fMultiplier );
2362 double fFracNew = (double)nFrac / (double)nForcedDiv;
2363 double fFracNew1 = (double)(nFrac + 1) / (double)nForcedDiv;
2364 double fDiff = fFrac - fFracNew;
2365 if( fDiff > ( fFracNew1 - fFrac ) )
2367 nFrac++;
2369 nDiv = nForcedDiv;
2374 sal_Int32 SvNumberformat::GetForcedDenominatorForType( sal_uInt16 nNumFor ) const
2376 const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
2377 sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
2378 return lcl_GetForcedDenominator( rInfo, nAnz );
2381 bool SvNumberformat::GetOutputString(double fNumber, sal_uInt16 nCharCount, OUString& rOutString) const
2383 using namespace std;
2385 if (eType != NUMBERFORMAT_NUMBER)
2387 return false;
2389 double fTestNum = fNumber;
2390 bool bSign = ::rtl::math::isSignBitSet(fTestNum);
2391 if (bSign)
2393 fTestNum = -fTestNum;
2395 if (fTestNum < EXP_LOWER_BOUND)
2397 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2398 return true;
2401 double fExp = log10(fTestNum);
2402 // Values < 1.0 always have one digit before the decimal point.
2403 sal_uInt16 nDigitPre = fExp >= 0.0 ? static_cast<sal_uInt16>(ceil(fExp)) : 1;
2405 if (nDigitPre > 15)
2407 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2408 return true;
2411 sal_uInt16 nPrec = nCharCount >= nDigitPre ? nCharCount - nDigitPre : 0;
2412 if (nPrec && bSign)
2414 // Subtract the negative sign.
2415 --nPrec;
2417 if (nPrec)
2419 // Subtract the decimal point.
2420 --nPrec;
2422 ImpGetOutputStdToPrecision(fNumber, rOutString, nPrec);
2423 if (rOutString.getLength() > nCharCount)
2425 // String still wider than desired. Switch to scientific notation.
2426 lcl_GetOutputStringScientific(fNumber, nCharCount, GetFormatter(), rOutString);
2428 return true;
2431 bool SvNumberformat::GetOutputString(double fNumber,
2432 OUString& OutString,
2433 Color** ppColor)
2435 bool bRes = false;
2436 OUStringBuffer sBuff;
2437 OutString = "";
2438 *ppColor = NULL; // keine Farbaenderung
2439 if (eType & NUMBERFORMAT_LOGICAL)
2441 if (fNumber)
2443 OutString = rScan.GetTrueString();
2445 else
2447 OutString = rScan.GetFalseString();
2449 return false;
2451 if (eType & NUMBERFORMAT_TEXT)
2453 ImpGetOutputStandard(fNumber, sBuff);
2454 OutString = sBuff.makeStringAndClear();
2455 return false;
2457 bool bHadStandard = false;
2458 if (bStandard) // einzelne Standardformate
2460 if (rScan.GetStandardPrec() == SvNumberFormatter::INPUTSTRING_PRECISION) // alle Zahlformate InputLine
2462 ImpGetOutputInputLine(fNumber, OutString);
2463 return false;
2465 switch (eType)
2467 case NUMBERFORMAT_NUMBER: // Standardzahlformat
2468 if (rScan.GetStandardPrec() == SvNumberFormatter::UNLIMITED_PRECISION)
2470 bool bSign = ::rtl::math::isSignBitSet(fNumber);
2471 if (bSign)
2473 if (!(fNumber < 0.0))
2475 bSign = false;
2477 fNumber = -fNumber;
2480 OUString sTemp;
2481 ImpGetOutputStdToPrecision(fNumber, sTemp, 10); // Use 10 decimals for general 'unlimited' format.
2482 sBuff.append(sTemp);
2484 if (fNumber < EXP_LOWER_BOUND)
2486 sal_Int32 nLen = sBuff.getLength();
2487 if (!nLen)
2489 return false;
2491 // #i112250# With the 10-decimal limit, small numbers are formatted as "0".
2492 // Switch to scientific in that case, too:
2493 if (nLen > 11 || ((nLen == 1 && sBuff[0] == (sal_Unicode)'0') && fNumber != 0.0))
2495 sal_uInt16 nStandardPrec = rScan.GetStandardPrec();
2496 nStandardPrec = ::std::min(nStandardPrec, static_cast<sal_uInt16>(14)); // limits to 14 decimals
2497 sBuff = ::rtl::math::doubleToUString( fNumber,
2498 rtl_math_StringFormat_E, nStandardPrec /*2*/,
2499 GetFormatter().GetNumDecimalSep()[0], true);
2502 if (bSign)
2504 sBuff.insert(0, (sal_Unicode)'-');
2506 OutString = sBuff.makeStringAndClear();
2507 return false;
2509 ImpGetOutputStandard(fNumber, sBuff);
2510 bHadStandard = true;
2511 break;
2512 case NUMBERFORMAT_DATE:
2513 bRes |= ImpGetDateOutput(fNumber, 0, sBuff);
2514 bHadStandard = true;
2515 break;
2516 case NUMBERFORMAT_TIME:
2517 bRes |= ImpGetTimeOutput(fNumber, 0, sBuff);
2518 bHadStandard = true;
2519 break;
2520 case NUMBERFORMAT_DATETIME:
2521 bRes |= ImpGetDateTimeOutput(fNumber, 0, sBuff);
2522 bHadStandard = true;
2523 break;
2526 if ( !bHadStandard )
2528 sal_uInt16 nIx; // Index des Teilformats
2529 short nCheck = ImpCheckCondition(fNumber, fLimit1, eOp1);
2530 if (nCheck == -1 || nCheck == 1) // nur 1 String oder True
2532 nIx = 0;
2534 else
2536 nCheck = ImpCheckCondition(fNumber, fLimit2, eOp2);
2537 if (nCheck == -1 || nCheck == 1)
2539 nIx = 1;
2541 else
2543 nIx = 2;
2546 if (fNumber < 0.0 &&
2547 ((nIx == 0 && IsFirstSubformatRealNegative()) || // 1st, usually positive subformat
2548 (nIx == 1 && IsSecondSubformatRealNegative()))) // 2nd, usually negative subformat
2550 fNumber = -fNumber; // eliminate sign
2552 *ppColor = NumFor[nIx].GetColor();
2553 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2554 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
2555 if (nAnz == 0 && rInfo.eScannedType == NUMBERFORMAT_UNDEFINED)
2557 return false; // leer => nichts
2559 else if (nAnz == 0) // sonst Standard-Format
2561 ImpGetOutputStandard(fNumber, sBuff);
2562 OutString = sBuff.makeStringAndClear();
2563 return false;
2565 switch (rInfo.eScannedType)
2567 case NUMBERFORMAT_TEXT:
2568 case NUMBERFORMAT_DEFINED:
2569 for (sal_uInt16 i = 0; i < nAnz; i++)
2571 switch (rInfo.nTypeArray[i])
2573 case NF_SYMBOLTYPE_STAR:
2574 if( bStarFlag )
2576 sBuff.append((sal_Unicode) 0x1B);
2577 sBuff.append(rInfo.sStrArray[i][1]);
2578 bRes = true;
2580 break;
2581 case NF_SYMBOLTYPE_BLANK:
2582 InsertBlanks(sBuff, sBuff.getLength(),
2583 rInfo.sStrArray[i][1] );
2584 break;
2585 case NF_SYMBOLTYPE_STRING:
2586 case NF_SYMBOLTYPE_CURRENCY:
2587 sBuff.append(rInfo.sStrArray[i]);
2588 break;
2589 case NF_SYMBOLTYPE_THSEP:
2590 if (rInfo.nThousand == 0)
2592 sBuff.append(rInfo.sStrArray[i]);
2594 break;
2595 default:
2596 break;
2599 break;
2600 case NUMBERFORMAT_DATE:
2601 bRes |= ImpGetDateOutput(fNumber, nIx, sBuff);
2602 break;
2603 case NUMBERFORMAT_TIME:
2604 bRes |= ImpGetTimeOutput(fNumber, nIx, sBuff);
2605 break;
2606 case NUMBERFORMAT_DATETIME:
2607 bRes |= ImpGetDateTimeOutput(fNumber, nIx, sBuff);
2608 break;
2609 case NUMBERFORMAT_NUMBER:
2610 case NUMBERFORMAT_PERCENT:
2611 case NUMBERFORMAT_CURRENCY:
2612 bRes |= ImpGetNumberOutput(fNumber, nIx, sBuff);
2613 break;
2614 case NUMBERFORMAT_FRACTION:
2615 bRes |= ImpGetFractionOutput(fNumber, nIx, sBuff);
2616 break;
2617 case NUMBERFORMAT_SCIENTIFIC:
2618 bRes |= ImpGetScientificOutput(fNumber, nIx, sBuff);
2619 break;
2622 OutString = sBuff.makeStringAndClear();
2623 return bRes;
2626 bool SvNumberformat::ImpGetScientificOutput(double fNumber,
2627 sal_uInt16 nIx,
2628 OUStringBuffer& sStr)
2630 bool bRes = false;
2631 bool bSign = false;
2633 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2634 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
2636 if (fNumber < 0)
2638 if (nIx == 0) // nicht in hinteren
2640 bSign = true; // Formaten
2642 fNumber = -fNumber;
2645 sStr = ::rtl::math::doubleToUString( fNumber,
2646 rtl_math_StringFormat_E,
2647 rInfo.nCntPre + rInfo.nCntPost - 1, '.' );
2648 OUStringBuffer ExpStr;
2649 short nExpSign = 1;
2650 sal_Int32 nExPos = sStr.indexOf((sal_Unicode)'E');
2652 if ( nExPos >= 0 )
2654 // split into mantisse and exponent and get rid of "E+" or "E-"
2655 sal_Int32 nExpStart = nExPos + 1;
2657 switch ( sStr[ nExpStart ] )
2659 case '-' :
2660 nExpSign = -1;
2661 // fallthru
2662 case '+' :
2663 ++nExpStart;
2664 break;
2666 ExpStr = sStr.toString().copy( nExpStart ); // part following the "E+"
2667 sStr.truncate( nExPos );
2668 // cut any decimal delimiter
2669 sal_Int32 index = 0;
2671 while((index = sStr.indexOf((sal_Unicode)'.', index)) >= 0)
2673 sStr.remove(index, 1);
2675 if ( rInfo.nCntPre != 1 ) // rescale Exp
2677 sal_Int32 nExp = ExpStr.toString().toInt32() * nExpSign;
2679 nExp -= (sal_Int32)rInfo.nCntPre - 1;
2680 if ( nExp < 0 )
2682 nExpSign = -1;
2683 nExp = -nExp;
2685 else
2687 nExpSign = 1;
2689 ExpStr = OUString::valueOf( nExp );
2693 sal_uInt16 j = nAnz-1; // last symbol
2694 sal_Int32 k; // position in ExpStr
2695 sal_Int32 nZeros = 0; // erase leading zeros
2697 bRes |= ImpNumberFill(ExpStr, fNumber, k, j, nIx, NF_SYMBOLTYPE_EXP);
2699 while (nZeros < k && ExpStr[nZeros] == (sal_Unicode)'0')
2701 ++nZeros;
2703 if (nZeros)
2705 ExpStr.remove( 0, nZeros);
2708 bool bCont = true;
2710 if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_EXP)
2712 const OUString& rStr = rInfo.sStrArray[j];
2713 if (nExpSign == -1)
2715 ExpStr.insert(0, (sal_Unicode)'-');
2717 else if (rStr.getLength() > 1 && rStr[1] == (sal_Unicode)'+')
2719 ExpStr.insert(0, (sal_Unicode)'+');
2721 ExpStr.insert(0, rStr[0]);
2722 if ( j )
2724 j--;
2726 else
2728 bCont = false;
2731 // weiter Hauptzahl:
2732 if ( !bCont )
2734 sStr.truncate();
2736 else
2738 k = sStr.getLength(); // hinter letzter Ziffer
2739 bRes |= ImpNumberFillWithThousands(sStr, fNumber, k, j, nIx,
2740 rInfo.nCntPre + rInfo.nCntPost);
2742 if (bSign)
2744 sStr.insert(0, (sal_Unicode)'-');
2746 sStr.append(ExpStr);
2748 return bRes;
2751 bool SvNumberformat::ImpGetFractionOutput(double fNumber,
2752 sal_uInt16 nIx,
2753 OUStringBuffer& sBuff)
2755 bool bRes = false;
2756 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
2757 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
2758 OUStringBuffer sStr, sFrac, sDiv; // Strings, Wert fuer
2759 sal_uLong nFrac, nDiv; // Vorkommaanteil
2760 bool bSign = false; // Zaehler und Nenner
2762 if (fNumber < 0)
2764 if (nIx == 0) // nicht in hinteren
2765 bSign = true; // Formaten
2766 fNumber = -fNumber;
2769 double fNum = floor(fNumber); // Vorkommateil
2771 fNumber -= fNum; // Nachkommateil
2772 if (fNum > _D_MAX_U_LONG_ || rInfo.nCntExp > 9)
2773 // zu gross
2775 sBuff = rScan.GetErrorString();
2776 return false;
2778 if (rInfo.nCntExp == 0)
2780 SAL_WARN( "svl.numbers", "SvNumberformat:: Bruch, nCntExp == 0");
2781 sBuff.truncate();
2782 return false;
2785 sal_uLong nBasis = ((sal_uLong)floor( pow(10.0,rInfo.nCntExp))) - 1; // 9, 99, 999 ,...
2786 sal_uLong x0, y0, x1, y1;
2788 if (rInfo.nCntExp <= _MAX_FRACTION_PREC)
2790 bool bUpperHalf;
2792 if (fNumber > 0.5)
2794 bUpperHalf = true;
2795 fNumber -= (fNumber - 0.5) * 2.0;
2797 else
2799 bUpperHalf = false;
2801 // Einstieg in Farey-Serie
2802 // finden:
2803 x0 = (sal_uLong) floor(fNumber*nBasis); // z.B. 2/9 <= x < 3/9
2804 if (x0 == 0) // => x0 = 2
2806 y0 = 1;
2807 x1 = 1;
2808 y1 = nBasis;
2810 else if (x0 == (nBasis-1)/2) // (b-1)/2, 1/2
2811 { // geht (nBasis ungerade)
2812 y0 = nBasis;
2813 x1 = 1;
2814 y1 = 2;
2816 else if (x0 == 1)
2818 y0 = nBasis; // 1/n; 1/(n-1)
2819 x1 = 1;
2820 y1 = nBasis - 1;
2822 else
2824 y0 = nBasis; // z.B. 2/9 2/8
2825 x1 = x0;
2826 y1 = nBasis - 1;
2827 double fUg = (double) x0 / (double) y0;
2828 double fOg = (double) x1 / (double) y1;
2829 sal_uLong nGgt = ImpGGT(y0, x0); // x0/y0 kuerzen
2830 x0 /= nGgt;
2831 y0 /= nGgt; // Einschachteln:
2832 sal_uLong x2 = 0;
2833 sal_uLong y2 = 0;
2834 bool bStop = false;
2835 while (!bStop)
2837 #ifdef __GNUC__
2838 // #i21648# GCC over-optimizes something resulting
2839 // in wrong fTest values throughout the loops.
2840 volatile
2841 #endif
2842 double fTest = (double)x1/(double)y1;
2843 while (!bStop)
2845 while (fTest > fOg)
2847 x1--;
2848 fTest = (double)x1/(double)y1;
2850 while (fTest < fUg && y1 > 1)
2852 y1--;
2853 fTest = (double)x1/(double)y1;
2855 if (fTest <= fOg)
2857 fOg = fTest;
2858 bStop = true;
2860 else if (y1 == 1)
2862 bStop = true;
2864 } // of while
2865 nGgt = ImpGGT(y1, x1); // x1/y1 kuerzen
2866 x2 = x1 / nGgt;
2867 y2 = y1 / nGgt;
2868 if (x2*y0 - x0*y2 == 1 || y1 <= 1) // Test, ob x2/y2
2869 bStop = true; // naechste Farey-Zahl
2870 else
2872 y1--;
2873 bStop = false;
2875 } // of while
2876 x1 = x2;
2877 y1 = y2;
2878 } // of else
2880 double fup, flow;
2882 flow = (double)x0/(double)y0;
2883 fup = (double)x1/(double)y1;
2884 while (fNumber > fup)
2886 sal_uLong x2 = ((y0+nBasis)/y1)*x1 - x0; // naechste Farey-Zahl
2887 sal_uLong y2 = ((y0+nBasis)/y1)*y1 - y0;
2889 x0 = x1;
2890 y0 = y1;
2891 x1 = x2;
2892 y1 = y2;
2893 flow = fup;
2894 fup = (double)x1/(double)y1;
2896 if (fNumber - flow < fup - fNumber)
2898 nFrac = x0;
2899 nDiv = y0;
2901 else
2903 nFrac = x1;
2904 nDiv = y1;
2906 if (bUpperHalf) // Original restaur.
2908 if (nFrac == 0 && nDiv == 1) // 1/1
2910 fNum += 1.0;
2912 else
2914 nFrac = nDiv - nFrac;
2918 else // grosse Nenner
2919 { // 0,1234->123/1000
2920 sal_uLong nGgt;
2922 nDiv = 10000000;
2923 nFrac = ((sal_uLong)floor(0.5 + fNumber * 10000000.0));
2924 nGgt = ImpGGT(nDiv, nFrac);
2925 if (nGgt > 1)
2927 nDiv /= nGgt;
2928 nFrac /= nGgt;
2930 if (nDiv > nBasis)
2932 nGgt = ImpGGTRound(nDiv, nFrac);
2933 if (nGgt > 1)
2935 nDiv /= nGgt;
2936 nFrac /= nGgt;
2939 if (nDiv > nBasis)
2941 nDiv = nBasis;
2942 nFrac = ((sal_uLong)floor(0.5 + fNumber *
2943 pow(10.0,rInfo.nCntExp)));
2944 nGgt = ImpGGTRound(nDiv, nFrac);
2945 if (nGgt > 1)
2947 nDiv /= nGgt;
2948 nFrac /= nGgt;
2953 if( sal_Int32 nForcedDiv = lcl_GetForcedDenominator(NumFor[nIx].Info(), nAnz) )
2955 lcl_ForcedDenominator(nFrac, nDiv, nForcedDiv);
2956 if( nFrac >= nDiv )
2958 nFrac = nDiv = 0;
2959 fNum = fNum + 1.0;
2963 if (rInfo.nCntPre == 0) // unechter Bruch
2965 double fNum1 = fNum * (double)nDiv + (double)nFrac;
2967 if (fNum1 > _D_MAX_U_LONG_)
2969 sBuff = rScan.GetErrorString();
2970 return false;
2972 nFrac = (sal_uLong) floor(fNum1);
2974 else if (fNum == 0.0 && nFrac != 0)
2977 else
2979 char aBuf[100];
2980 sprintf( aBuf, "%.f", fNum ); // simple rounded integer (#100211# - checked)
2981 sStr.appendAscii( aBuf );
2982 impTransliterate(sStr, NumFor[nIx].GetNatNum());
2984 if (rInfo.nCntPre > 0 && nFrac == 0)
2986 sDiv.truncate();
2988 else
2990 sFrac = ImpIntToString( nIx, nFrac );
2991 sDiv = ImpIntToString( nIx, nDiv );
2994 sal_uInt16 j = nAnz-1; // letztes Symbol->rueckw.
2995 sal_Int32 k; // Nenner:
2997 bRes |= ImpNumberFill(sDiv, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRAC);
2999 bool bCont = true;
3000 if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRAC)
3002 if (rInfo.nCntPre > 0 && nFrac == 0)
3004 sDiv.insert(0, (sal_Unicode)' ');
3006 else
3008 sDiv.insert(0, rInfo.sStrArray[j][0]);
3010 if ( j )
3012 j--;
3014 else
3016 bCont = false;
3019 // weiter Zaehler:
3020 if ( !bCont )
3022 sFrac.truncate();
3024 else
3026 bRes |= ImpNumberFill(sFrac, fNumber, k, j, nIx, NF_SYMBOLTYPE_FRACBLANK);
3027 if (rInfo.nTypeArray[j] == NF_SYMBOLTYPE_FRACBLANK)
3029 sFrac.insert(0, rInfo.sStrArray[j]);
3030 if ( j )
3032 j--;
3034 else
3036 bCont = false;
3040 // weiter Hauptzahl
3041 if ( !bCont )
3043 sStr.truncate();
3045 else
3047 k = sStr.getLength(); // hinter letzter Ziffer
3048 bRes |= ImpNumberFillWithThousands(sStr, fNumber, k, j, nIx,
3049 rInfo.nCntPre);
3051 if (bSign && !(nFrac == 0 && fNum == 0.0))
3053 sBuff.insert(0, (sal_Unicode)'-'); // nicht -0
3055 sBuff.append(sStr);
3056 sBuff.append(sFrac);
3057 sBuff.append(sDiv);
3058 return bRes;
3061 bool SvNumberformat::ImpGetTimeOutput(double fNumber,
3062 sal_uInt16 nIx,
3063 OUStringBuffer& sBuff)
3065 using namespace ::com::sun::star::i18n;
3066 bool bCalendarSet = false;
3067 double fNumberOrig = fNumber;
3068 bool bRes = false;
3069 bool bSign = false;
3070 if (fNumber < 0.0)
3072 fNumber = -fNumber;
3073 if (nIx == 0)
3075 bSign = true;
3078 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3079 if (rInfo.bThousand) // []-Format
3081 if (fNumber > 1.0E10) // zu gross
3083 sBuff = rScan.GetErrorString();
3084 return false;
3087 else
3089 fNumber -= floor(fNumber); // sonst Datum abtrennen
3091 bool bInputLine;
3092 sal_Int32 nCntPost;
3093 if ( rScan.GetStandardPrec() == 300 &&
3094 0 < rInfo.nCntPost && rInfo.nCntPost < 7 )
3095 { // round at 7 decimals (+5 of 86400 == 12 significant digits)
3096 bInputLine = true;
3097 nCntPost = 7;
3099 else
3101 bInputLine = false;
3102 nCntPost = rInfo.nCntPost;
3104 if (bSign && !rInfo.bThousand) // kein []-Format
3106 fNumber = 1.0 - fNumber; // "Kehrwert"
3108 double fTime = fNumber * 86400.0;
3109 fTime = ::rtl::math::round( fTime, int(nCntPost) );
3110 if (bSign && fTime == 0.0)
3112 bSign = false; // nicht -00:00:00
3114 if( floor( fTime ) > _D_MAX_U_LONG_ )
3116 sBuff = rScan.GetErrorString();
3117 return false;
3119 sal_uLong nSeconds = (sal_uLong)floor( fTime );
3121 OUStringBuffer sSecStr( ::rtl::math::doubleToUString( fTime-nSeconds,
3122 rtl_math_StringFormat_F, int(nCntPost), '.'));
3123 sSecStr.stripStart((sal_Unicode)'0');
3124 sSecStr.stripStart((sal_Unicode)'.');
3125 if ( bInputLine )
3127 sSecStr.stripEnd((sal_Unicode)'0');
3128 for(sal_Int32 index = sSecStr.getLength(); index < rInfo.nCntPost; ++index)
3130 sSecStr.append((sal_Unicode)'0');
3132 impTransliterate(sSecStr, NumFor[nIx].GetNatNum());
3133 nCntPost = sSecStr.getLength();
3135 else
3137 impTransliterate(sSecStr, NumFor[nIx].GetNatNum());
3140 sal_Int32 nSecPos = 0; // Zum Ziffernweisen
3141 // abarbeiten
3142 sal_uLong nHour, nMin, nSec;
3143 if (!rInfo.bThousand) // kein [] Format
3145 nHour = (nSeconds/3600) % 24;
3146 nMin = (nSeconds%3600) / 60;
3147 nSec = nSeconds%60;
3149 else if (rInfo.nThousand == 3) // [ss]
3151 nHour = 0;
3152 nMin = 0;
3153 nSec = nSeconds;
3155 else if (rInfo.nThousand == 2) // [mm]:ss
3157 nHour = 0;
3158 nMin = nSeconds / 60;
3159 nSec = nSeconds % 60;
3161 else if (rInfo.nThousand == 1) // [hh]:mm:ss
3163 nHour = nSeconds / 3600;
3164 nMin = (nSeconds%3600) / 60;
3165 nSec = nSeconds%60;
3167 else
3169 // TODO What should these be set to?
3170 nHour = 0;
3171 nMin = 0;
3172 nSec = 0;
3175 sal_Unicode cAmPm = ' '; // a oder p
3176 if (rInfo.nCntExp) // AM/PM
3178 if (nHour == 0)
3180 nHour = 12;
3181 cAmPm = 'a';
3183 else if (nHour < 12)
3185 cAmPm = 'a';
3187 else
3189 cAmPm = 'p';
3190 if (nHour > 12)
3192 nHour -= 12;
3196 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
3197 for (sal_uInt16 i = 0; i < nAnz; i++)
3199 sal_Int32 nLen;
3200 switch (rInfo.nTypeArray[i])
3202 case NF_SYMBOLTYPE_STAR:
3203 if( bStarFlag )
3205 sBuff.append((sal_Unicode)0x1B);
3206 sBuff.append(rInfo.sStrArray[i][1]);
3207 bRes = true;
3209 break;
3210 case NF_SYMBOLTYPE_BLANK:
3211 InsertBlanks(sBuff, sBuff.getLength(),
3212 rInfo.sStrArray[i][1] );
3213 break;
3214 case NF_SYMBOLTYPE_STRING:
3215 case NF_SYMBOLTYPE_CURRENCY:
3216 case NF_SYMBOLTYPE_DATESEP:
3217 case NF_SYMBOLTYPE_TIMESEP:
3218 case NF_SYMBOLTYPE_TIME100SECSEP:
3219 sBuff.append(rInfo.sStrArray[i]);
3220 break;
3221 case NF_SYMBOLTYPE_DIGIT:
3222 nLen = ( bInputLine && i > 0 &&
3223 (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
3224 rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
3225 nCntPost : rInfo.sStrArray[i].getLength() );
3226 for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost; j++)
3228 sBuff.append(sSecStr[nSecPos]);
3229 nSecPos++;
3231 break;
3232 case NF_KEY_AMPM: // AM/PM
3233 if ( !bCalendarSet )
3235 double fDiff = DateTime(*(rScan.GetNullDate())) - GetCal().getEpochStart();
3236 fDiff += fNumberOrig;
3237 GetCal().setLocalDateTime( fDiff );
3238 bCalendarSet = true;
3240 if (cAmPm == 'a')
3242 sBuff.append(GetCal().getDisplayName(
3243 CalendarDisplayIndex::AM_PM, AmPmValue::AM, 0 ));
3245 else
3247 sBuff.append(GetCal().getDisplayName(
3248 CalendarDisplayIndex::AM_PM, AmPmValue::PM, 0 ));
3250 break;
3251 case NF_KEY_AP: // A/P
3252 if (cAmPm == 'a')
3254 sBuff.append((sal_Unicode)'a');
3256 else
3258 sBuff.append((sal_Unicode)'p');
3260 break;
3261 case NF_KEY_MI: // M
3262 sBuff.append(ImpIntToString( nIx, nMin ));
3263 break;
3264 case NF_KEY_MMI: // MM
3265 sBuff.append(ImpIntToString( nIx, nMin, 2 ));
3266 break;
3267 case NF_KEY_H: // H
3268 sBuff.append(ImpIntToString( nIx, nHour ));
3269 break;
3270 case NF_KEY_HH: // HH
3271 sBuff.append(ImpIntToString( nIx, nHour, 2 ));
3272 break;
3273 case NF_KEY_S: // S
3274 sBuff.append(ImpIntToString( nIx, nSec ));
3275 break;
3276 case NF_KEY_SS: // SS
3277 sBuff.append(ImpIntToString( nIx, nSec, 2 ));
3278 break;
3279 default:
3280 break;
3283 if (bSign && rInfo.bThousand)
3285 sBuff.insert(0, (sal_Unicode)'-');
3287 return bRes;
3291 /** If a day of month occurs within the format, the month name is in possessive
3292 genitive case if the day follows the month, and partitive case if the day
3293 precedes the month. If there is no day of month the nominative case (noun)
3294 is returned. Also if the month is immediately preceded or followed by a
3295 literal string other than space the nominative name is used, this prevents
3296 duplicated casing for MMMM\t\a and such in documents imported from (e.g.
3297 Finnish) Excel or older LibO/OOo releases.
3300 // IDEA: instead of eCodeType pass the index to nTypeArray and restrict
3301 // inspection of month name around that one, that would enable different month
3302 // cases in one format. Though probably the most rare use case ever..
3304 sal_Int32 SvNumberformat::ImpUseMonthCase( int & io_nState, const ImpSvNumFor& rNumFor, NfKeywordIndex eCodeType ) const
3306 using namespace ::com::sun::star::i18n;
3307 if (!io_nState)
3309 bool bMonthSeen = false;
3310 bool bDaySeen = false;
3311 const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3312 const sal_uInt16 nCount = rNumFor.GetCount();
3313 for (sal_uInt16 i = 0; i < nCount && io_nState == 0; ++i)
3315 sal_Int32 nLen;
3316 switch (rInfo.nTypeArray[i])
3318 case NF_KEY_D :
3319 case NF_KEY_DD :
3320 if (bMonthSeen)
3322 io_nState = 2;
3324 else
3326 bDaySeen = true;
3328 break;
3329 case NF_KEY_MMM:
3330 case NF_KEY_MMMM:
3331 case NF_KEY_MMMMM:
3332 if ((i < nCount-1 &&
3333 rInfo.nTypeArray[i+1] == NF_SYMBOLTYPE_STRING &&
3334 rInfo.sStrArray[i+1][0] != ' ') ||
3335 (i > 0 && rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING &&
3336 ((nLen = rInfo.sStrArray[i-1].getLength()) > 0) &&
3337 rInfo.sStrArray[i-1][nLen-1] != ' '))
3339 io_nState = 1;
3341 else if (bDaySeen)
3343 io_nState = 3;
3345 else
3347 bMonthSeen = true;
3349 break;
3352 if (io_nState == 0)
3354 io_nState = 1; // no day of month
3357 switch (io_nState)
3359 case 1:
3360 // no day of month or forced nominative
3361 switch (eCodeType)
3363 case NF_KEY_MMM:
3364 return CalendarDisplayCode::SHORT_MONTH_NAME;
3365 case NF_KEY_MMMM:
3366 return CalendarDisplayCode::LONG_MONTH_NAME;
3367 case NF_KEY_MMMMM:
3368 return CalendarDisplayCode::NARROW_MONTH_NAME;
3369 default:
3370 ; // nothing
3372 case 2:
3373 // day of month follows month (the month's 17th)
3374 switch (eCodeType)
3376 case NF_KEY_MMM:
3377 return CalendarDisplayCode::SHORT_GENITIVE_MONTH_NAME;
3378 case NF_KEY_MMMM:
3379 return CalendarDisplayCode::LONG_GENITIVE_MONTH_NAME;
3380 case NF_KEY_MMMMM:
3381 return CalendarDisplayCode::NARROW_GENITIVE_MONTH_NAME;
3382 default:
3383 ; // nothing
3385 case 3:
3386 // day of month precedes month (17 of month)
3387 switch (eCodeType)
3389 case NF_KEY_MMM:
3390 return CalendarDisplayCode::SHORT_PARTITIVE_MONTH_NAME;
3391 case NF_KEY_MMMM:
3392 return CalendarDisplayCode::LONG_PARTITIVE_MONTH_NAME;
3393 case NF_KEY_MMMMM:
3394 return CalendarDisplayCode::NARROW_PARTITIVE_MONTH_NAME;
3395 default:
3396 ; // nothing
3399 SAL_WARN( "svl.numbers", "ImpUseMonthCase: unhandled keyword index eCodeType");
3400 return CalendarDisplayCode::LONG_MONTH_NAME;
3404 bool SvNumberformat::ImpIsOtherCalendar( const ImpSvNumFor& rNumFor ) const
3406 if ( GetCal().getUniqueID() != Gregorian::get() )
3408 return false;
3410 const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3411 const sal_uInt16 nAnz = rNumFor.GetCount();
3412 sal_uInt16 i;
3413 for ( i = 0; i < nAnz; i++ )
3415 switch ( rInfo.nTypeArray[i] )
3417 case NF_SYMBOLTYPE_CALENDAR :
3418 return false;
3419 case NF_KEY_EC :
3420 case NF_KEY_EEC :
3421 case NF_KEY_R :
3422 case NF_KEY_RR :
3423 case NF_KEY_AAA :
3424 case NF_KEY_AAAA :
3425 return true;
3428 return false;
3431 void SvNumberformat::SwitchToOtherCalendar( OUString& rOrgCalendar,
3432 double& fOrgDateTime ) const
3434 CalendarWrapper& rCal = GetCal();
3435 const OUString &rGregorian = Gregorian::get();
3436 if ( rCal.getUniqueID() == rGregorian )
3438 using namespace ::com::sun::star::i18n;
3439 ::com::sun::star::uno::Sequence< OUString > xCals = rCal.getAllCalendars(
3440 rLoc().getLanguageTag().getLocale() );
3441 sal_Int32 nCnt = xCals.getLength();
3442 if ( nCnt > 1 )
3444 for ( sal_Int32 j=0; j < nCnt; j++ )
3446 if ( xCals[j] != rGregorian )
3448 if ( !rOrgCalendar.getLength() )
3450 rOrgCalendar = rCal.getUniqueID();
3451 fOrgDateTime = rCal.getDateTime();
3453 rCal.loadCalendar( xCals[j], rLoc().getLanguageTag().getLocale() );
3454 rCal.setDateTime( fOrgDateTime );
3455 break; // for
3462 void SvNumberformat::SwitchToGregorianCalendar( const OUString& rOrgCalendar,
3463 double fOrgDateTime ) const
3465 CalendarWrapper& rCal = GetCal();
3466 const OUString &rGregorian = Gregorian::get();
3467 if ( rOrgCalendar.getLength() && rCal.getUniqueID() != rGregorian )
3469 rCal.loadCalendar( rGregorian, rLoc().getLanguageTag().getLocale() );
3470 rCal.setDateTime( fOrgDateTime );
3474 bool SvNumberformat::ImpFallBackToGregorianCalendar( OUString& rOrgCalendar, double& fOrgDateTime )
3476 using namespace ::com::sun::star::i18n;
3477 CalendarWrapper& rCal = GetCal();
3478 const OUString &rGregorian = Gregorian::get();
3479 if ( rCal.getUniqueID() != rGregorian )
3481 sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3482 if ( nVal == 0 && rCal.getLoadedCalendar().Eras[0].ID == "Dummy" )
3484 if ( !rOrgCalendar.getLength() )
3486 rOrgCalendar = rCal.getUniqueID();
3487 fOrgDateTime = rCal.getDateTime();
3489 else if ( rOrgCalendar == rGregorian )
3491 rOrgCalendar = "";
3493 rCal.loadCalendar( rGregorian, rLoc().getLanguageTag().getLocale() );
3494 rCal.setDateTime( fOrgDateTime );
3495 return true;
3498 return false;
3502 #ifdef THE_FUTURE
3503 /* XXX NOTE: even if the ImpSwitchToSpecifiedCalendar method is currently
3504 * unused please don't remove it, it would be needed by
3505 * SwitchToSpecifiedCalendar(), see comment in
3506 * ImpSvNumberInputScan::GetDateRef() */
3508 bool SvNumberformat::ImpSwitchToSpecifiedCalendar( OUString& rOrgCalendar,
3509 double& fOrgDateTime,
3510 const ImpSvNumFor& rNumFor ) const
3512 const ImpSvNumberformatInfo& rInfo = rNumFor.Info();
3513 const sal_uInt16 nAnz = rNumFor.GetCount();
3514 for ( sal_uInt16 i = 0; i < nAnz; i++ )
3516 if ( rInfo.nTypeArray[i] == NF_SYMBOLTYPE_CALENDAR )
3518 CalendarWrapper& rCal = GetCal();
3519 if ( !rOrgCalendar.getLength() )
3521 rOrgCalendar = rCal.getUniqueID();
3522 fOrgDateTime = rCal.getDateTime();
3524 rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLocale() );
3525 rCal.setDateTime( fOrgDateTime );
3526 return true;
3529 return false;
3531 #endif
3533 // static
3534 void SvNumberformat::ImpAppendEraG( OUStringBuffer& OutString,
3535 const CalendarWrapper& rCal,
3536 sal_Int16 nNatNum )
3538 using namespace ::com::sun::star::i18n;
3539 if ( rCal.getUniqueID() == "gengou" )
3541 sal_Unicode cEra;
3542 sal_Int16 nVal = rCal.getValue( CalendarFieldIndex::ERA );
3543 switch ( nVal )
3545 case 1:
3546 cEra = 'M';
3547 break;
3548 case 2:
3549 cEra = 'T';
3550 break;
3551 case 3:
3552 cEra = 'S';
3553 break;
3554 case 4:
3555 cEra = 'H';
3556 break;
3557 default:
3558 cEra = '?';
3559 break;
3561 OutString.append(cEra);
3563 else
3565 OutString.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3569 bool SvNumberformat::ImpIsIso8601( const ImpSvNumFor& rNumFor )
3571 bool bIsIso = false;
3572 if ((eType & NUMBERFORMAT_DATE) == NUMBERFORMAT_DATE)
3574 enum State
3576 eNone,
3577 eAtYear,
3578 eAtSep1,
3579 eAtMonth,
3580 eAtSep2,
3581 eNotIso
3583 State eState = eNone;
3584 short const * const pType = rNumFor.Info().nTypeArray;
3585 sal_uInt16 nAnz = rNumFor.GetCount();
3586 for (sal_uInt16 i=0; i < nAnz && !bIsIso && eState != eNotIso; ++i)
3588 switch ( pType[i] )
3590 case NF_KEY_YY: // two digits not strictly ISO 8601
3591 case NF_KEY_YYYY:
3592 if (eState != eNone)
3594 eState = eNotIso;
3596 else
3598 eState = eAtYear;
3600 break;
3601 case NF_KEY_M: // single digit not strictly ISO 8601
3602 case NF_KEY_MM:
3603 if (eState != eAtSep1)
3605 eState = eNotIso;
3607 else
3609 eState = eAtMonth;
3611 break;
3612 case NF_KEY_D: // single digit not strictly ISO 8601
3613 case NF_KEY_DD:
3614 if (eState != eAtSep2)
3616 eState = eNotIso;
3618 else
3620 bIsIso = true;
3622 break;
3623 case NF_SYMBOLTYPE_STRING:
3624 case NF_SYMBOLTYPE_DATESEP:
3625 if (comphelper::string::equals(rNumFor.Info().sStrArray[i], '-'))
3627 if (eState == eAtYear)
3629 eState = eAtSep1;
3631 else if (eState == eAtMonth)
3633 eState = eAtSep2;
3635 else
3637 eState = eNotIso;
3640 else
3642 eState = eNotIso;
3644 break;
3645 default:
3646 eState = eNotIso;
3650 else
3652 SAL_WARN( "svl.numbers", "SvNumberformat::ImpIsIso8601: no date" );
3654 return bIsIso;
3657 bool SvNumberformat::ImpGetDateOutput(double fNumber,
3658 sal_uInt16 nIx,
3659 OUStringBuffer& sBuff)
3661 using namespace ::com::sun::star::i18n;
3662 bool bRes = false;
3664 CalendarWrapper& rCal = GetCal();
3665 double fDiff = DateTime(*(rScan.GetNullDate())) - rCal.getEpochStart();
3666 fNumber += fDiff;
3667 rCal.setLocalDateTime( fNumber );
3668 int nUseMonthCase = 0; // not decided yet
3669 OUString aOrgCalendar; // empty => not changed yet
3671 double fOrgDateTime(0.0);
3672 bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx] );
3673 if ( bOtherCalendar )
3675 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3677 if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ) )
3679 bOtherCalendar = false;
3681 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3682 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
3683 sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
3684 OUString aYear;
3686 for (sal_uInt16 i = 0; i < nAnz; i++)
3688 switch (rInfo.nTypeArray[i])
3690 case NF_SYMBOLTYPE_CALENDAR :
3691 if ( !aOrgCalendar.getLength() )
3693 aOrgCalendar = rCal.getUniqueID();
3694 fOrgDateTime = rCal.getDateTime();
3696 rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
3697 rCal.setDateTime( fOrgDateTime );
3698 ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3699 break;
3700 case NF_SYMBOLTYPE_STAR:
3701 if( bStarFlag )
3703 sBuff.append((sal_Unicode) 0x1B);
3704 sBuff.append(rInfo.sStrArray[i][1]);
3705 bRes = true;
3707 break;
3708 case NF_SYMBOLTYPE_BLANK:
3709 InsertBlanks( sBuff, sBuff.getLength(), rInfo.sStrArray[i][1] );
3710 break;
3711 case NF_SYMBOLTYPE_STRING:
3712 case NF_SYMBOLTYPE_CURRENCY:
3713 case NF_SYMBOLTYPE_DATESEP:
3714 case NF_SYMBOLTYPE_TIMESEP:
3715 case NF_SYMBOLTYPE_TIME100SECSEP:
3716 sBuff.append(rInfo.sStrArray[i]);
3717 break;
3718 case NF_KEY_M: // M
3719 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_MONTH, nNatNum ));
3720 break;
3721 case NF_KEY_MM: // MM
3722 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_MONTH, nNatNum ));
3723 break;
3724 case NF_KEY_MMM: // MMM
3725 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3726 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3727 nNatNum));
3728 break;
3729 case NF_KEY_MMMM: // MMMM
3730 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3731 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3732 nNatNum));
3733 break;
3734 case NF_KEY_MMMMM: // MMMMM
3735 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
3736 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
3737 nNatNum));
3738 break;
3739 case NF_KEY_Q: // Q
3740 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
3741 break;
3742 case NF_KEY_QQ: // QQ
3743 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
3744 break;
3745 case NF_KEY_D: // D
3746 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum ));
3747 break;
3748 case NF_KEY_DD: // DD
3749 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
3750 break;
3751 case NF_KEY_DDD: // DDD
3752 if ( bOtherCalendar )
3754 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3756 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
3757 if ( bOtherCalendar )
3759 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3761 break;
3762 case NF_KEY_DDDD: // DDDD
3763 if ( bOtherCalendar )
3765 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3767 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3768 if ( bOtherCalendar )
3770 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3772 break;
3773 case NF_KEY_YY: // YY
3774 if ( bOtherCalendar )
3776 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3778 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3779 if ( bOtherCalendar )
3781 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3783 break;
3784 case NF_KEY_YYYY: // YYYY
3785 if ( bOtherCalendar )
3787 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3789 aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
3790 if (aYear.getLength() < 4)
3792 using namespace comphelper::string;
3793 // Ensure that year consists of at least 4 digits, so it
3794 // can be distinguished from 2 digits display and edited
3795 // without suddenly being hit by the 2-digit year magic.
3796 OUStringBuffer aBuf;
3797 padToLength(aBuf, 4 - aYear.getLength(), sal_Unicode('0'));
3798 impTransliterate(aBuf, NumFor[nIx].GetNatNum());
3799 aBuf.append(aYear);
3800 sBuff.append(aBuf);
3802 else
3804 sBuff.append(aYear);
3806 if ( bOtherCalendar )
3808 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3810 break;
3811 case NF_KEY_EC: // E
3812 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
3813 break;
3814 case NF_KEY_EEC: // EE
3815 case NF_KEY_R: // R
3816 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
3817 break;
3818 case NF_KEY_NN: // NN
3819 case NF_KEY_AAA: // AAA
3820 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
3821 break;
3822 case NF_KEY_NNN: // NNN
3823 case NF_KEY_AAAA: // AAAA
3824 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3825 break;
3826 case NF_KEY_NNNN: // NNNN
3827 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
3828 sBuff.append(rLoc().getLongDateDayOfWeekSep());
3829 break;
3830 case NF_KEY_WW : // WW
3831 sBuff.append(ImpIntToString( nIx,
3832 rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
3833 break;
3834 case NF_KEY_G: // G
3835 ImpAppendEraG(sBuff, rCal, nNatNum );
3836 break;
3837 case NF_KEY_GG: // GG
3838 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
3839 break;
3840 case NF_KEY_GGG: // GGG
3841 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
3842 break;
3843 case NF_KEY_RR: // RR => GGGEE
3844 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
3845 break;
3848 if ( aOrgCalendar.getLength() )
3850 rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() ); // restore calendar
3852 return bRes;
3855 bool SvNumberformat::ImpGetDateTimeOutput(double fNumber,
3856 sal_uInt16 nIx,
3857 OUStringBuffer& sBuff)
3859 using namespace ::com::sun::star::i18n;
3860 bool bRes = false;
3862 CalendarWrapper& rCal = GetCal();
3863 double fDiff = DateTime(*(rScan.GetNullDate())) - rCal.getEpochStart();
3864 fNumber += fDiff;
3866 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
3867 bool bInputLine;
3868 sal_Int32 nCntPost;
3869 if ( rScan.GetStandardPrec() == 300 &&
3870 0 < rInfo.nCntPost && rInfo.nCntPost < 7 )
3872 // round at 7 decimals (+5 of 86400 == 12 significant digits)
3873 bInputLine = true;
3874 nCntPost = 7;
3876 else
3878 bInputLine = false;
3879 nCntPost = rInfo.nCntPost;
3881 double fTime = (fNumber - floor( fNumber )) * 86400.0;
3882 fTime = ::rtl::math::round( fTime, int(nCntPost) );
3883 if (fTime >= 86400.0)
3885 // result of fNumber==x.999999999... rounded up, use correct date/time
3886 fTime -= 86400.0;
3887 fNumber = floor( fNumber + 0.5) + fTime;
3889 rCal.setLocalDateTime( fNumber );
3891 int nUseMonthCase = 0; // not decided yet
3892 OUString aOrgCalendar; // empty => not changed yet
3893 double fOrgDateTime(0.0);
3894 bool bOtherCalendar = ImpIsOtherCalendar( NumFor[nIx] );
3895 if ( bOtherCalendar )
3897 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
3899 if ( ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime ) )
3901 bOtherCalendar = false;
3903 sal_Int16 nNatNum = NumFor[nIx].GetNatNum().GetNatNum();
3905 sal_uLong nSeconds = (sal_uLong)floor( fTime );
3906 OUStringBuffer sSecStr( ::rtl::math::doubleToUString( fTime-nSeconds,
3907 rtl_math_StringFormat_F, int(nCntPost), '.'));
3908 sSecStr.stripStart((sal_Unicode)'0');
3909 sSecStr.stripStart((sal_Unicode)'.');
3910 if ( bInputLine )
3912 sSecStr.stripEnd((sal_Unicode)'0');
3913 for(sal_Int32 index = sSecStr.getLength(); index < rInfo.nCntPost; ++index)
3915 sSecStr.append((sal_Unicode)'0');
3917 impTransliterate(sSecStr, NumFor[nIx].GetNatNum());
3918 nCntPost = sSecStr.getLength();
3920 else
3922 impTransliterate(sSecStr, NumFor[nIx].GetNatNum());
3925 sal_Int32 nSecPos = 0; // Zum Ziffernweisen
3926 // abarbeiten
3927 sal_uLong nHour, nMin, nSec;
3928 if (!rInfo.bThousand) // [] Format
3930 nHour = (nSeconds/3600) % 24;
3931 nMin = (nSeconds%3600) / 60;
3932 nSec = nSeconds%60;
3934 else if (rInfo.nThousand == 3) // [ss]
3936 nHour = 0;
3937 nMin = 0;
3938 nSec = nSeconds;
3940 else if (rInfo.nThousand == 2) // [mm]:ss
3942 nHour = 0;
3943 nMin = nSeconds / 60;
3944 nSec = nSeconds % 60;
3946 else if (rInfo.nThousand == 1) // [hh]:mm:ss
3948 nHour = nSeconds / 3600;
3949 nMin = (nSeconds%3600) / 60;
3950 nSec = nSeconds%60;
3952 else
3954 nHour = 0; // TODO What should these values be?
3955 nMin = 0;
3956 nSec = 0;
3958 sal_Unicode cAmPm = ' '; // a oder p
3959 if (rInfo.nCntExp) // AM/PM
3961 if (nHour == 0)
3963 nHour = 12;
3964 cAmPm = 'a';
3966 else if (nHour < 12)
3968 cAmPm = 'a';
3970 else
3972 cAmPm = 'p';
3973 if (nHour > 12)
3975 nHour -= 12;
3979 const sal_uInt16 nAnz = NumFor[nIx].GetCount();
3980 sal_Int32 nLen;
3981 OUString aYear;
3982 for (sal_uInt16 i = 0; i < nAnz; i++)
3984 switch (rInfo.nTypeArray[i])
3986 case NF_SYMBOLTYPE_CALENDAR :
3987 if ( !aOrgCalendar.getLength() )
3989 aOrgCalendar = rCal.getUniqueID();
3990 fOrgDateTime = rCal.getDateTime();
3992 rCal.loadCalendar( rInfo.sStrArray[i], rLoc().getLanguageTag().getLocale() );
3993 rCal.setDateTime( fOrgDateTime );
3994 ImpFallBackToGregorianCalendar( aOrgCalendar, fOrgDateTime );
3995 break;
3996 case NF_SYMBOLTYPE_STAR:
3997 if( bStarFlag )
3999 sBuff.append((sal_Unicode) 0x1B);
4000 sBuff.append(rInfo.sStrArray[i][1]);
4001 bRes = true;
4003 break;
4004 case NF_SYMBOLTYPE_BLANK:
4005 InsertBlanks( sBuff, sBuff.getLength(),
4006 rInfo.sStrArray[i][1] );
4007 break;
4008 case NF_SYMBOLTYPE_STRING:
4009 case NF_SYMBOLTYPE_CURRENCY:
4010 case NF_SYMBOLTYPE_DATESEP:
4011 case NF_SYMBOLTYPE_TIMESEP:
4012 case NF_SYMBOLTYPE_TIME100SECSEP:
4013 sBuff.append(rInfo.sStrArray[i]);
4014 break;
4015 case NF_SYMBOLTYPE_DIGIT:
4016 nLen = ( bInputLine && i > 0 &&
4017 (rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_STRING ||
4018 rInfo.nTypeArray[i-1] == NF_SYMBOLTYPE_TIME100SECSEP) ?
4019 nCntPost : rInfo.sStrArray[i].getLength() );
4020 for (sal_Int32 j = 0; j < nLen && nSecPos < nCntPost; j++)
4022 sBuff.append(sSecStr[ nSecPos ]);
4023 nSecPos++;
4025 break;
4026 case NF_KEY_AMPM: // AM/PM
4027 if (cAmPm == 'a')
4029 sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
4030 AmPmValue::AM, 0 ));
4032 else
4034 sBuff.append(rCal.getDisplayName( CalendarDisplayIndex::AM_PM,
4035 AmPmValue::PM, 0 ));
4037 break;
4038 case NF_KEY_AP: // A/P
4039 if (cAmPm == 'a')
4041 sBuff.append((sal_Unicode)'a');
4043 else
4045 sBuff.append((sal_Unicode)'p');
4047 break;
4048 case NF_KEY_MI: // M
4049 sBuff.append(ImpIntToString( nIx, nMin ));
4050 break;
4051 case NF_KEY_MMI: // MM
4052 sBuff.append(ImpIntToString( nIx, nMin, 2 ));
4053 break;
4054 case NF_KEY_H: // H
4055 sBuff.append(ImpIntToString( nIx, nHour ));
4056 break;
4057 case NF_KEY_HH: // HH
4058 sBuff.append(ImpIntToString( nIx, nHour, 2 ));
4059 break;
4060 case NF_KEY_S: // S
4061 sBuff.append(ImpIntToString( nIx, nSec ));
4062 break;
4063 case NF_KEY_SS: // SS
4064 sBuff.append(ImpIntToString( nIx, nSec, 2 ));
4065 break;
4066 case NF_KEY_M: // M
4067 sBuff.append(rCal.getDisplayString(
4068 CalendarDisplayCode::SHORT_MONTH, nNatNum ));
4069 break;
4070 case NF_KEY_MM: // MM
4071 sBuff.append(rCal.getDisplayString(
4072 CalendarDisplayCode::LONG_MONTH, nNatNum ));
4073 break;
4074 case NF_KEY_MMM: // MMM
4075 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4076 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4077 nNatNum));
4078 break;
4079 case NF_KEY_MMMM: // MMMM
4080 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4081 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4082 nNatNum));
4083 break;
4084 case NF_KEY_MMMMM: // MMMMM
4085 sBuff.append(rCal.getDisplayString( ImpUseMonthCase( nUseMonthCase, NumFor[nIx],
4086 static_cast<NfKeywordIndex>(rInfo.nTypeArray[i])),
4087 nNatNum));
4088 break;
4089 case NF_KEY_Q: // Q
4090 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_QUARTER, nNatNum ));
4091 break;
4092 case NF_KEY_QQ: // QQ
4093 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_QUARTER, nNatNum ));
4094 break;
4095 case NF_KEY_D: // D
4096 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY, nNatNum ));
4097 break;
4098 case NF_KEY_DD: // DD
4099 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY, nNatNum ));
4100 break;
4101 case NF_KEY_DDD: // DDD
4102 if ( bOtherCalendar )
4104 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4106 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
4107 if ( bOtherCalendar )
4109 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4111 break;
4112 case NF_KEY_DDDD: // DDDD
4113 if ( bOtherCalendar )
4115 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4117 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4118 if ( bOtherCalendar )
4120 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4122 break;
4123 case NF_KEY_YY: // YY
4124 if ( bOtherCalendar )
4126 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4128 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
4129 if ( bOtherCalendar )
4131 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4133 break;
4134 case NF_KEY_YYYY: // YYYY
4135 if ( bOtherCalendar )
4137 SwitchToGregorianCalendar( aOrgCalendar, fOrgDateTime );
4139 aYear = rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum );
4140 if (aYear.getLength() < 4)
4142 using namespace comphelper::string;
4143 // Ensure that year consists of at least 4 digits, so it
4144 // can be distinguished from 2 digits display and edited
4145 // without suddenly being hit by the 2-digit year magic.
4146 OUStringBuffer aBuf;
4147 padToLength(aBuf, 4 - aYear.getLength(), sal_Unicode('0'));
4148 impTransliterate(aBuf, NumFor[nIx].GetNatNum());
4149 aBuf.append(aYear);
4150 sBuff.append(aBuf);
4152 else
4154 sBuff.append(aYear);
4156 if ( bOtherCalendar )
4158 SwitchToOtherCalendar( aOrgCalendar, fOrgDateTime );
4160 break;
4161 case NF_KEY_EC: // E
4162 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_YEAR, nNatNum ));
4163 break;
4164 case NF_KEY_EEC: // EE
4165 case NF_KEY_R: // R
4166 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR, nNatNum ));
4167 break;
4168 case NF_KEY_NN: // NN
4169 case NF_KEY_AAA: // AAA
4170 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_DAY_NAME, nNatNum ));
4171 break;
4172 case NF_KEY_NNN: // NNN
4173 case NF_KEY_AAAA: // AAAA
4174 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4175 break;
4176 case NF_KEY_NNNN: // NNNN
4177 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_DAY_NAME, nNatNum ));
4178 sBuff.append(rLoc().getLongDateDayOfWeekSep());
4179 break;
4180 case NF_KEY_WW : // WW
4181 sBuff.append(ImpIntToString( nIx, rCal.getValue( CalendarFieldIndex::WEEK_OF_YEAR )));
4182 break;
4183 case NF_KEY_G: // G
4184 ImpAppendEraG( sBuff, rCal, nNatNum );
4185 break;
4186 case NF_KEY_GG: // GG
4187 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::SHORT_ERA, nNatNum ));
4188 break;
4189 case NF_KEY_GGG: // GGG
4190 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_ERA, nNatNum ));
4191 break;
4192 case NF_KEY_RR: // RR => GGGEE
4193 sBuff.append(rCal.getDisplayString( CalendarDisplayCode::LONG_YEAR_AND_ERA, nNatNum ));
4194 break;
4197 if ( aOrgCalendar.getLength() )
4199 rCal.loadCalendar( aOrgCalendar, rLoc().getLanguageTag().getLocale() ); // restore calendar
4201 return bRes;
4204 bool SvNumberformat::ImpGetNumberOutput(double fNumber,
4205 sal_uInt16 nIx,
4206 OUStringBuffer& sStr)
4208 bool bRes = false;
4209 bool bSign;
4210 if (fNumber < 0.0)
4212 if (nIx == 0) // nicht in hinteren
4214 bSign = true; // Formaten
4216 else
4218 bSign = false;
4220 fNumber = -fNumber;
4222 else
4224 bSign = false;
4225 if ( ::rtl::math::isSignBitSet( fNumber ) )
4227 fNumber = -fNumber; // yes, -0.0 is possible, eliminate '-'
4230 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4231 if (rInfo.eScannedType == NUMBERFORMAT_PERCENT)
4233 if (fNumber < _D_MAX_D_BY_100)
4235 fNumber *= 100.0;
4237 else
4239 sStr = rScan.GetErrorString();
4240 return false;
4243 sal_uInt16 i, j;
4244 sal_Int32 k;
4245 bool bInteger = false;
4246 if ( rInfo.nThousand != FLAG_STANDARD_IN_FORMAT )
4248 // special formatting only if no GENERAL keyword in format code
4249 const sal_uInt16 nThousand = rInfo.nThousand;
4250 long nPrecExp;
4251 for (i = 0; i < nThousand; i++)
4253 if (fNumber > _D_MIN_M_BY_1000)
4255 fNumber /= 1000.0;
4257 else
4259 fNumber = 0.0;
4262 if (fNumber > 0.0)
4264 nPrecExp = GetPrecExp( fNumber );
4266 else
4268 nPrecExp = 0;
4270 if (rInfo.nCntPost) // NachkommaStellen
4272 if ((rInfo.nCntPost + nPrecExp) > 15 && nPrecExp < 15)
4274 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 15-nPrecExp, '.');
4275 for (long l = 15-nPrecExp; l < (long) rInfo.nCntPost; l++)
4277 sStr.append((sal_Unicode)'0');
4280 else
4282 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, rInfo.nCntPost, '.' );
4284 sStr.stripStart((sal_Unicode)'0'); // fuehrende Nullen weg
4286 else if (fNumber == 0.0) // Null
4288 // nothing to be done here, keep empty string sStr,
4289 // ImpNumberFillWithThousands does the rest
4291 else // Integer
4293 sStr = ::rtl::math::doubleToUString( fNumber, rtl_math_StringFormat_F, 0, '.');
4294 sStr.stripStart((sal_Unicode)'0'); // fuehrende Nullen weg
4296 sal_Int32 nPoint = sStr.indexOf((sal_Unicode)'.' );
4297 if ( nPoint >= 0)
4299 register const sal_Unicode* p = sStr.getStr() + nPoint;
4300 while ( *++p == '0' )
4302 if ( !*p )
4304 bInteger = true;
4306 sStr.remove( nPoint, 1 ); // . herausnehmen
4308 if (bSign && (sStr.getLength() == 0 ||
4309 comphelper::string::getTokenCount(sStr.toString(), '0') == sStr.getLength()+1)) // nur 00000
4311 bSign = false; // nicht -0.00
4313 } // End of != FLAG_STANDARD_IN_FORMAT
4315 // von hinten nach vorn
4316 // editieren:
4317 k = sStr.getLength(); // hinter letzter Ziffer
4318 j = NumFor[nIx].GetCount()-1; // letztes Symbol
4319 // Nachkommastellen:
4320 if (rInfo.nCntPost > 0)
4322 bool bTrailing = true; // ob Endnullen?
4323 bool bFilled = false; // ob aufgefuellt wurde ?
4324 short nType;
4325 while (j > 0 && // rueckwaerts
4326 (nType = rInfo.nTypeArray[j]) != NF_SYMBOLTYPE_DECSEP)
4328 switch ( nType )
4330 case NF_SYMBOLTYPE_STAR:
4331 if( bStarFlag )
4333 sStr.insert(k, rInfo.sStrArray[j][1]);
4334 sStr.insert(k, (sal_Unicode) 0x1B);
4335 bRes = true;
4337 break;
4338 case NF_SYMBOLTYPE_BLANK:
4339 /*k = */ InsertBlanks(sStr, k, rInfo.sStrArray[j][1] );
4340 break;
4341 case NF_SYMBOLTYPE_STRING:
4342 case NF_SYMBOLTYPE_CURRENCY:
4343 case NF_SYMBOLTYPE_PERCENT:
4344 sStr.insert(k, rInfo.sStrArray[j]);
4345 break;
4346 case NF_SYMBOLTYPE_THSEP:
4347 if (rInfo.nThousand == 0)
4349 sStr.insert(k, rInfo.sStrArray[j]);
4351 break;
4352 case NF_SYMBOLTYPE_DIGIT:
4354 const OUString& rStr = rInfo.sStrArray[j];
4355 const sal_Unicode* p1 = rStr.getStr();
4356 register const sal_Unicode* p = p1 + rStr.getLength();
4357 while ( p1 < p-- )
4359 const sal_Unicode c = *p;
4360 k--;
4361 if ( sStr[k] != (sal_Unicode)'0' )
4363 bTrailing = false;
4365 if (bTrailing)
4367 if ( c == '0' )
4369 bFilled = true;
4371 else if ( c == '-' )
4373 if ( bInteger )
4375 sStr[ k ] = (sal_Unicode)'-';
4377 bFilled = true;
4379 else if ( c == '?' )
4381 sStr[ k ] = (sal_Unicode)' ';
4382 bFilled = true;
4384 else if ( !bFilled ) // #
4386 sStr.remove(k,1);
4389 } // of for
4390 break;
4391 } // of case digi
4392 case NF_KEY_CCC: // CCC-Waehrung
4393 sStr.insert(k, rScan.GetCurAbbrev());
4394 break;
4395 case NF_KEY_GENERAL: // Standard im String
4397 OUStringBuffer sNum;
4398 ImpGetOutputStandard(fNumber, sNum);
4399 sNum.stripStart((sal_Unicode)'-');
4400 sStr.insert(k, sNum.makeStringAndClear());
4401 break;
4403 default:
4404 break;
4405 } // of switch
4406 j--;
4407 } // of while
4408 } // of Nachkomma
4410 bRes |= ImpNumberFillWithThousands(sStr, fNumber, k, j, nIx, // ggfs Auffuellen mit .
4411 rInfo.nCntPre);
4412 if ( rInfo.nCntPost > 0 )
4414 const OUString& rDecSep = GetFormatter().GetNumDecimalSep();
4415 sal_Int32 nLen = rDecSep.getLength();
4416 if ( sStr.getLength() > nLen && ( sStr.indexOf( rDecSep, sStr.getLength() - nLen) == sStr.getLength() - nLen) )
4418 sStr.truncate( sStr.getLength() - nLen ); // no decimals => strip DecSep
4421 if (bSign)
4423 sStr.insert(0, (sal_Unicode)'-');
4425 impTransliterate(sStr, NumFor[nIx].GetNatNum());
4426 return bRes;
4429 bool SvNumberformat::ImpNumberFillWithThousands( OUStringBuffer& sBuff, // number string
4430 double& rNumber, // number
4431 sal_Int32 k, // position within string
4432 sal_uInt16 j, // symbol index within format code
4433 sal_uInt16 nIx, // subformat index
4434 sal_Int32 nDigCnt) // count of integer digits in format
4436 bool bRes = false;
4437 sal_Int32 nLeadingStringChars = 0; // inserted StringChars before number
4438 sal_Int32 nDigitCount = 0; // count of integer digits from the right
4439 bool bStop = false;
4440 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4441 // no normal thousands separators if number divided by thousands
4442 bool bDoThousands = (rInfo.nThousand == 0);
4443 utl::DigitGroupingIterator aGrouping( GetFormatter().GetLocaleData()->getDigitGrouping());
4445 while (!bStop) // backwards
4447 if (j == 0)
4449 bStop = true;
4451 switch (rInfo.nTypeArray[j])
4453 case NF_SYMBOLTYPE_DECSEP:
4454 aGrouping.reset();
4455 // fall thru
4456 case NF_SYMBOLTYPE_STRING:
4457 case NF_SYMBOLTYPE_CURRENCY:
4458 case NF_SYMBOLTYPE_PERCENT:
4459 sBuff.insert(k, rInfo.sStrArray[j]);
4460 if ( k == 0 )
4462 nLeadingStringChars = nLeadingStringChars + rInfo.sStrArray[j].getLength();
4464 break;
4465 case NF_SYMBOLTYPE_STAR:
4466 if( bStarFlag )
4468 sBuff.insert(k, rInfo.sStrArray[j][1]);
4469 sBuff.insert(k, (sal_Unicode) 0x1B);
4470 bRes = true;
4472 break;
4473 case NF_SYMBOLTYPE_BLANK:
4474 /*k = */ InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4475 break;
4476 case NF_SYMBOLTYPE_THSEP:
4477 // #i7284# #102685# Insert separator also if number is divided
4478 // by thousands and the separator is specified somewhere in
4479 // between and not only at the end.
4480 // #i12596# But do not insert if it's a parenthesized negative
4481 // format like (#,)
4482 // In fact, do not insert if divided and regex [0#,],[^0#] and
4483 // no other digit symbol follows (which was already detected
4484 // during scan of format code, otherwise there would be no
4485 // division), else do insert. Same in ImpNumberFill() below.
4486 if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4488 bDoThousands = ((j == 0) ||
4489 (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4490 rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4491 (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4493 if ( bDoThousands )
4495 if (k > 0)
4497 sBuff.insert(k, rInfo.sStrArray[j]);
4499 else if (nDigitCount < nDigCnt)
4501 // Leading '#' displays nothing (e.g. no leading
4502 // separator for numbers <1000 with #,##0 format).
4503 // Leading '?' displays blank.
4504 // Everything else, including nothing, displays the
4505 // separator.
4506 sal_Unicode cLeader = 0;
4507 if (j > 0 && rInfo.nTypeArray[j-1] == NF_SYMBOLTYPE_DIGIT)
4509 const OUString& rStr = rInfo.sStrArray[j-1];
4510 sal_Int32 nLen = rStr.getLength();
4511 if (nLen)
4513 cLeader = rStr[ nLen - 1 ];
4516 switch (cLeader)
4518 case '#':
4519 ; // nothing
4520 break;
4521 case '?':
4522 // erAck: 2008-04-03T16:24+0200
4523 // Actually this currently isn't executed
4524 // because the format scanner in the context of
4525 // "?," doesn't generate a group separator but
4526 // a literal ',' character instead that is
4527 // inserted unconditionally. Should be changed
4528 // on some occasion.
4529 sBuff.insert(k, (sal_Unicode)' ');
4530 break;
4531 default:
4532 sBuff.insert(k, rInfo.sStrArray[j]);
4535 aGrouping.advance();
4537 break;
4538 case NF_SYMBOLTYPE_DIGIT:
4540 const OUString& rStr = rInfo.sStrArray[j];
4541 const sal_Unicode* p1 = rStr.getStr();
4542 register const sal_Unicode* p = p1 + rStr.getLength();
4543 while ( p1 < p-- )
4545 nDigitCount++;
4546 if (k > 0)
4548 k--;
4550 else
4552 switch (*p)
4554 case '0':
4555 sBuff.insert(0, (sal_Unicode)'0');
4556 break;
4557 case '?':
4558 sBuff.insert(0, (sal_Unicode)' ');
4559 break;
4562 if (nDigitCount == nDigCnt && k > 0)
4564 // more digits than specified
4565 ImpDigitFill(sBuff, 0, k, nIx, nDigitCount, aGrouping);
4568 break;
4570 case NF_KEY_CCC: // CCC currency
4571 sBuff.insert(k, rScan.GetCurAbbrev());
4572 break;
4573 case NF_KEY_GENERAL: // "General" in string
4575 OUStringBuffer sNum;
4576 ImpGetOutputStandard(rNumber, sNum);
4577 sNum.stripStart((sal_Unicode)'-');
4578 sBuff.insert(k, sNum.makeStringAndClear());
4579 break;
4581 default:
4582 break;
4583 } // switch
4584 j--; // next format code string
4585 } // while
4587 k = k + nLeadingStringChars; // MSC converts += to int and then warns, so ...
4588 if (k > nLeadingStringChars)
4590 ImpDigitFill(sBuff, nLeadingStringChars, k, nIx, nDigitCount, aGrouping);
4592 return bRes;
4595 void SvNumberformat::ImpDigitFill(OUStringBuffer& sStr, // number string
4596 sal_Int32 nStart, // start of digits
4597 sal_Int32 & k, // position within string
4598 sal_uInt16 nIx, // subformat index
4599 sal_Int32 & nDigitCount, // count of integer digits from the right so far
4600 utl::DigitGroupingIterator & rGrouping ) // current grouping
4602 if (NumFor[nIx].Info().bThousand) // only if grouping
4603 { // fill in separators
4604 const OUString& rThousandSep = GetFormatter().GetNumThousandSep();
4605 while (k > nStart)
4607 if (nDigitCount == rGrouping.getPos())
4609 sStr.insert( k, rThousandSep );
4610 rGrouping.advance();
4612 nDigitCount++;
4613 k--;
4616 else // simply skip
4618 k = nStart;
4622 bool SvNumberformat::ImpNumberFill( OUStringBuffer& sBuff, // number string
4623 double& rNumber, // number for "General" format
4624 sal_Int32& k, // position within string
4625 sal_uInt16& j, // symbol index within format code
4626 sal_uInt16 nIx, // subformat index
4627 short eSymbolType ) // type of stop condition
4629 bool bRes = false;
4630 const ImpSvNumberformatInfo& rInfo = NumFor[nIx].Info();
4631 // no normal thousands separators if number divided by thousands
4632 bool bDoThousands = (rInfo.nThousand == 0);
4633 short nType;
4635 k = sBuff.getLength(); // behind last digit
4637 while (j > 0 && (nType = rInfo.nTypeArray[j]) != eSymbolType )
4638 { // rueckwaerts:
4639 switch ( nType )
4641 case NF_SYMBOLTYPE_STAR:
4642 if( bStarFlag )
4644 sBuff.insert(k, rInfo.sStrArray[j][1]);
4645 sBuff.insert(k, sal_Unicode(0x1B));
4646 bRes = true;
4648 break;
4649 case NF_SYMBOLTYPE_BLANK:
4650 k = InsertBlanks(sBuff, k, rInfo.sStrArray[j][1] );
4651 break;
4652 case NF_SYMBOLTYPE_THSEP:
4653 // Same as in ImpNumberFillWithThousands() above, do not insert
4654 // if divided and regex [0#,],[^0#] and no other digit symbol
4655 // follows (which was already detected during scan of format
4656 // code, otherwise there would be no division), else do insert.
4657 if ( !bDoThousands && j < NumFor[nIx].GetCount()-1 )
4659 bDoThousands = ((j == 0) ||
4660 (rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_DIGIT &&
4661 rInfo.nTypeArray[j-1] != NF_SYMBOLTYPE_THSEP) ||
4662 (rInfo.nTypeArray[j+1] == NF_SYMBOLTYPE_DIGIT));
4664 if ( bDoThousands && k > 0 )
4666 sBuff.insert(k, rInfo.sStrArray[j]);
4668 break;
4669 case NF_SYMBOLTYPE_DIGIT:
4671 const OUString& rStr = rInfo.sStrArray[j];
4672 const sal_Unicode* p1 = rStr.getStr();
4673 register const sal_Unicode* p = p1 + rStr.getLength();
4674 while ( p1 < p-- )
4676 if (k > 0)
4678 k--;
4680 else
4682 switch (*p)
4684 case '0':
4685 sBuff.insert(0, (sal_Unicode)'0');
4686 break;
4687 case '?':
4688 sBuff.insert(0, (sal_Unicode)' ');
4689 break;
4694 break;
4695 case NF_KEY_CCC: // CCC-Waehrung
4696 sBuff.insert(k, rScan.GetCurAbbrev());
4697 break;
4698 case NF_KEY_GENERAL: // Standard im String
4700 OUStringBuffer sNum;
4701 ImpGetOutputStandard(rNumber, sNum);
4702 sNum.stripStart((sal_Unicode)'-');
4703 sBuff.insert(k, sNum.makeStringAndClear());
4705 break;
4706 case NF_SYMBOLTYPE_FRAC_FDIV: // Do Nothing
4707 break;
4709 default:
4710 sBuff.insert(k, rInfo.sStrArray[j]);
4711 break;
4712 } // of switch
4713 j--; // naechster String
4714 } // of while
4715 return bRes;
4718 void SvNumberformat::GetFormatSpecialInfo(bool& bThousand,
4719 bool& IsRed,
4720 sal_uInt16& nPrecision,
4721 sal_uInt16& nAnzLeading) const
4723 // as before: take info from nNumFor=0 for whole format (for dialog etc.)
4725 short nDummyType;
4726 GetNumForInfo( 0, nDummyType, bThousand, nPrecision, nAnzLeading );
4728 // "negative in red" is only useful for the whole format
4730 const Color* pColor = NumFor[1].GetColor();
4731 if (fLimit1 == 0.0 && fLimit2 == 0.0 && pColor
4732 && (*pColor == rScan.GetRedColor()))
4734 IsRed = true;
4736 else
4738 IsRed = false;
4742 void SvNumberformat::GetNumForInfo( sal_uInt16 nNumFor, short& rScannedType,
4743 bool& bThousand, sal_uInt16& nPrecision, sal_uInt16& nAnzLeading ) const
4745 // take info from a specified sub-format (for XML export)
4747 if ( nNumFor > 3 )
4749 return; // invalid
4752 const ImpSvNumberformatInfo& rInfo = NumFor[nNumFor].Info();
4753 rScannedType = rInfo.eScannedType;
4754 bThousand = rInfo.bThousand;
4755 nPrecision = rInfo.nCntPost;
4756 if (bStandard && rInfo.eScannedType == NUMBERFORMAT_NUMBER)
4758 // StandardFormat
4759 nAnzLeading = 1;
4761 else
4763 nAnzLeading = 0;
4764 bool bStop = false;
4765 sal_uInt16 i = 0;
4766 const sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
4767 while (!bStop && i < nAnz)
4769 short nType = rInfo.nTypeArray[i];
4770 if ( nType == NF_SYMBOLTYPE_DIGIT)
4772 const sal_Unicode* p = rInfo.sStrArray[i].getStr();
4773 while ( *p == '#' )
4775 p++;
4777 while ( *p++ == '0' )
4779 nAnzLeading++;
4782 else if (nType == NF_SYMBOLTYPE_DECSEP || nType == NF_SYMBOLTYPE_EXP)
4784 bStop = true;
4786 i++;
4791 const OUString* SvNumberformat::GetNumForString( sal_uInt16 nNumFor, sal_uInt16 nPos,
4792 bool bString /* = false */ ) const
4794 if ( nNumFor > 3 )
4796 return NULL;
4798 sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
4799 if ( !nAnz )
4801 return NULL;
4803 if ( nPos == 0xFFFF )
4805 nPos = nAnz - 1;
4806 if ( bString )
4807 { // rueckwaerts
4808 short* pType = NumFor[nNumFor].Info().nTypeArray + nPos;
4809 while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) &&
4810 (*pType != NF_SYMBOLTYPE_CURRENCY) )
4812 pType--;
4813 nPos--;
4815 if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
4817 return NULL;
4821 else if ( nPos > nAnz - 1 )
4823 return NULL;
4825 else if ( bString )
4827 // vorwaerts
4828 short* pType = NumFor[nNumFor].Info().nTypeArray + nPos;
4829 while ( nPos < nAnz && (*pType != NF_SYMBOLTYPE_STRING) &&
4830 (*pType != NF_SYMBOLTYPE_CURRENCY) )
4832 pType++;
4833 nPos++;
4835 if ( nPos >= nAnz || ((*pType != NF_SYMBOLTYPE_STRING) &&
4836 (*pType != NF_SYMBOLTYPE_CURRENCY)) )
4838 return NULL;
4841 return &NumFor[nNumFor].Info().sStrArray[nPos];
4844 short SvNumberformat::GetNumForType( sal_uInt16 nNumFor, sal_uInt16 nPos,
4845 bool bString /* = false */ ) const
4847 if ( nNumFor > 3 )
4849 return 0;
4851 sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
4852 if ( !nAnz )
4854 return 0;
4856 if ( nPos == 0xFFFF )
4858 nPos = nAnz - 1;
4859 if ( bString )
4861 // rueckwaerts
4862 short* pType = NumFor[nNumFor].Info().nTypeArray + nPos;
4863 while ( nPos > 0 && (*pType != NF_SYMBOLTYPE_STRING) &&
4864 (*pType != NF_SYMBOLTYPE_CURRENCY) )
4866 pType--;
4867 nPos--;
4869 if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
4871 return 0;
4875 else if ( nPos > nAnz - 1 )
4877 return 0;
4879 else if ( bString )
4881 // vorwaerts
4882 short* pType = NumFor[nNumFor].Info().nTypeArray + nPos;
4883 while ( nPos < nAnz && (*pType != NF_SYMBOLTYPE_STRING) &&
4884 (*pType != NF_SYMBOLTYPE_CURRENCY) )
4886 pType++;
4887 nPos++;
4889 if ( (*pType != NF_SYMBOLTYPE_STRING) && (*pType != NF_SYMBOLTYPE_CURRENCY) )
4891 return 0;
4894 return NumFor[nNumFor].Info().nTypeArray[nPos];
4897 bool SvNumberformat::IsNegativeWithoutSign() const
4899 if ( IsSecondSubformatRealNegative() )
4901 const OUString* pStr = GetNumForString( 1, 0, true );
4902 if ( pStr )
4904 return !HasStringNegativeSign( *pStr );
4907 return false;
4910 bool SvNumberformat::IsNegativeInBracket() const
4912 sal_uInt16 nAnz = NumFor[1].GetCount();
4913 if (!nAnz)
4915 return false;
4917 OUString *tmpStr = NumFor[1].Info().sStrArray;
4918 using comphelper::string::equals;
4919 return (equals(tmpStr[0], '(') && equals(tmpStr[nAnz-1], ')'));
4922 bool SvNumberformat::HasPositiveBracketPlaceholder() const
4924 sal_uInt16 nAnz = NumFor[0].GetCount();
4925 OUString *tmpStr = NumFor[0].Info().sStrArray;
4926 return (tmpStr[nAnz-1].equalsAscii( "_)" ));
4929 DateFormat SvNumberformat::GetDateOrder() const
4931 if ( (eType & NUMBERFORMAT_DATE) == NUMBERFORMAT_DATE )
4933 short const * const pType = NumFor[0].Info().nTypeArray;
4934 sal_uInt16 nAnz = NumFor[0].GetCount();
4935 for ( sal_uInt16 j=0; j<nAnz; j++ )
4937 switch ( pType[j] )
4939 case NF_KEY_D :
4940 case NF_KEY_DD :
4941 return DMY;
4942 case NF_KEY_M :
4943 case NF_KEY_MM :
4944 case NF_KEY_MMM :
4945 case NF_KEY_MMMM :
4946 case NF_KEY_MMMMM :
4947 return MDY;
4948 case NF_KEY_YY :
4949 case NF_KEY_YYYY :
4950 case NF_KEY_EC :
4951 case NF_KEY_EEC :
4952 case NF_KEY_R :
4953 case NF_KEY_RR :
4954 return YMD;
4958 else
4960 SAL_WARN( "svl.numbers", "SvNumberformat::GetDateOrder: no date" );
4962 return rLoc().getDateFormat();
4965 sal_uInt32 SvNumberformat::GetExactDateOrder() const
4967 sal_uInt32 nRet = 0;
4968 if ( (eType & NUMBERFORMAT_DATE) != NUMBERFORMAT_DATE )
4970 SAL_WARN( "svl.numbers", "SvNumberformat::GetExactDateOrder: no date" );
4971 return nRet;
4973 short const * const pType = NumFor[0].Info().nTypeArray;
4974 sal_uInt16 nAnz = NumFor[0].GetCount();
4975 int nShift = 0;
4976 for ( sal_uInt16 j=0; j<nAnz && nShift < 3; j++ )
4978 switch ( pType[j] )
4980 case NF_KEY_D :
4981 case NF_KEY_DD :
4982 nRet = (nRet << 8) | 'D';
4983 ++nShift;
4984 break;
4985 case NF_KEY_M :
4986 case NF_KEY_MM :
4987 case NF_KEY_MMM :
4988 case NF_KEY_MMMM :
4989 case NF_KEY_MMMMM :
4990 nRet = (nRet << 8) | 'M';
4991 ++nShift;
4992 break;
4993 case NF_KEY_YY :
4994 case NF_KEY_YYYY :
4995 case NF_KEY_EC :
4996 case NF_KEY_EEC :
4997 case NF_KEY_R :
4998 case NF_KEY_RR :
4999 nRet = (nRet << 8) | 'Y';
5000 ++nShift;
5001 break;
5004 return nRet;
5007 void SvNumberformat::GetConditions( SvNumberformatLimitOps& rOper1, double& rVal1,
5008 SvNumberformatLimitOps& rOper2, double& rVal2 ) const
5010 rOper1 = eOp1;
5011 rOper2 = eOp2;
5012 rVal1 = fLimit1;
5013 rVal2 = fLimit2;
5016 Color* SvNumberformat::GetColor( sal_uInt16 nNumFor ) const
5018 if ( nNumFor > 3 )
5020 return NULL;
5022 return NumFor[nNumFor].GetColor();
5025 static void lcl_SvNumberformat_AddLimitStringImpl( OUString& rStr,
5026 SvNumberformatLimitOps eOp,
5027 double fLimit, const String& rDecSep )
5029 if ( eOp != NUMBERFORMAT_OP_NO )
5031 switch ( eOp )
5033 case NUMBERFORMAT_OP_EQ :
5034 rStr = "[=";
5035 break;
5036 case NUMBERFORMAT_OP_NE :
5037 rStr = "[<>";
5038 break;
5039 case NUMBERFORMAT_OP_LT :
5040 rStr = "[<";
5041 break;
5042 case NUMBERFORMAT_OP_LE :
5043 rStr = "[<=";
5044 break;
5045 case NUMBERFORMAT_OP_GT :
5046 rStr = "[>";
5047 break;
5048 case NUMBERFORMAT_OP_GE :
5049 rStr = "[>=";
5050 break;
5051 default:
5052 SAL_WARN( "svl.numbers", "unsupported number format" );
5053 break;
5055 rStr += ::rtl::math::doubleToUString( fLimit,
5056 rtl_math_StringFormat_Automatic, rtl_math_DecimalPlaces_Max,
5057 rDecSep.GetChar(0), true);
5058 rStr += "]";
5062 OUString SvNumberformat::GetMappedFormatstring( const NfKeywordTable& rKeywords,
5063 const LocaleDataWrapper& rLocWrp,
5064 bool bDontQuote ) const
5066 OUStringBuffer aStr;
5067 bool bDefault[4];
5068 // 1 subformat matches all if no condition specified,
5069 bDefault[0] = ( NumFor[1].GetCount() == 0 && eOp1 == NUMBERFORMAT_OP_NO );
5070 // with 2 subformats [>=0];[<0] is implied if no condition specified
5071 bDefault[1] = ( !bDefault[0] && NumFor[2].GetCount() == 0 &&
5072 eOp1 == NUMBERFORMAT_OP_GE && fLimit1 == 0.0 &&
5073 eOp2 == NUMBERFORMAT_OP_NO && fLimit2 == 0.0 );
5074 // with 3 or more subformats [>0];[<0];[=0] is implied if no condition specified,
5075 // note that subformats may be empty (;;;) and NumFor[2].GetnAnz()>0 is not checked.
5076 bDefault[2] = ( !bDefault[0] && !bDefault[1] &&
5077 eOp1 == NUMBERFORMAT_OP_GT && fLimit1 == 0.0 &&
5078 eOp2 == NUMBERFORMAT_OP_LT && fLimit2 == 0.0 );
5079 bool bDefaults = bDefault[0] || bDefault[1] || bDefault[2];
5080 // from now on bDefault[] values are used to append empty subformats at the end
5081 bDefault[3] = false;
5082 if ( !bDefaults )
5084 // conditions specified
5085 if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 == NUMBERFORMAT_OP_NO )
5087 bDefault[0] = bDefault[1] = true; // [];x
5089 else if ( eOp1 != NUMBERFORMAT_OP_NO && eOp2 != NUMBERFORMAT_OP_NO &&
5090 NumFor[2].GetCount() == 0 )
5092 bDefault[0] = bDefault[1] = bDefault[2] = bDefault[3] = true; // [];[];;
5094 // nothing to do if conditions specified for every subformat
5096 else if ( bDefault[0] )
5098 bDefault[0] = false; // a single unconditional subformat is never delimited
5100 else
5102 if ( bDefault[2] && NumFor[2].GetCount() == 0 && NumFor[1].GetCount() > 0 )
5104 bDefault[3] = true; // special cases x;x;; and ;x;;
5106 for ( int i=0; i<3 && !bDefault[i]; ++i )
5108 bDefault[i] = true;
5111 int nSem = 0; // needed ';' delimiters
5112 int nSub = 0; // subformats delimited so far
5113 for ( int n=0; n<4; n++ )
5115 if ( n > 0 )
5117 nSem++;
5119 OUString aPrefix;
5120 bool LCIDInserted = false;
5122 if ( !bDefaults )
5124 switch ( n )
5126 case 0 :
5127 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp1,
5128 fLimit1, rLocWrp.getNumDecimalSep() );
5129 break;
5130 case 1 :
5131 lcl_SvNumberformat_AddLimitStringImpl( aPrefix, eOp2,
5132 fLimit2, rLocWrp.getNumDecimalSep() );
5133 break;
5137 const OUString& rColorName = NumFor[n].GetColorName();
5138 if ( !rColorName.isEmpty() )
5140 const NfKeywordTable & rKey = rScan.GetKeywords();
5141 for ( int j = NF_KEY_FIRSTCOLOR; j <= NF_KEY_LASTCOLOR; j++ )
5143 if ( rKey[j] == rColorName )
5145 aPrefix += "[";
5146 aPrefix += rKeywords[j];
5147 aPrefix += "]";
5148 break; // for
5153 const SvNumberNatNum& rNum = NumFor[n].GetNatNum();
5155 sal_uInt16 nAnz = NumFor[n].GetCount();
5156 if ( nSem && (nAnz || !aPrefix.isEmpty()) )
5158 for ( ; nSem; --nSem )
5160 aStr.append( ';' );
5162 for ( ; nSub <= n; ++nSub )
5164 bDefault[nSub] = false;
5168 if ( !aPrefix.isEmpty() )
5170 aStr.append( aPrefix );
5172 if ( nAnz )
5174 const short* pType = NumFor[n].Info().nTypeArray;
5175 const OUString* pStr = NumFor[n].Info().sStrArray;
5176 for ( sal_uInt16 j=0; j<nAnz; j++ )
5178 if ( 0 <= pType[j] && pType[j] < NF_KEYWORD_ENTRIES_COUNT )
5180 aStr.append( rKeywords[pType[j]] );
5181 if( NF_KEY_NNNN == pType[j] )
5183 aStr.append( rLocWrp.getLongDateDayOfWeekSep() );
5186 else
5188 switch ( pType[j] )
5190 case NF_SYMBOLTYPE_DECSEP :
5191 aStr.append( rLocWrp.getNumDecimalSep() );
5192 break;
5193 case NF_SYMBOLTYPE_THSEP :
5194 aStr.append( rLocWrp.getNumThousandSep() );
5195 break;
5196 case NF_SYMBOLTYPE_DATESEP :
5197 aStr.append( rLocWrp.getDateSep() );
5198 break;
5199 case NF_SYMBOLTYPE_TIMESEP :
5200 aStr.append( rLocWrp.getTimeSep() );
5201 break;
5202 case NF_SYMBOLTYPE_TIME100SECSEP :
5203 aStr.append( rLocWrp.getTime100SecSep() );
5204 break;
5205 case NF_SYMBOLTYPE_STRING :
5206 if( bDontQuote )
5208 aStr.append( pStr[j] );
5210 else if ( pStr[j].getLength() == 1 )
5212 aStr.append( '\\' );
5213 aStr.append( pStr[j] );
5215 else
5217 aStr.append( '"' );
5218 aStr.append( pStr[j] );
5219 aStr.append( '"' );
5221 break;
5222 case NF_SYMBOLTYPE_CALDEL :
5223 if ( pStr[j+1].equalsAscii("buddhist") )
5225 aStr.insert( 0, "[$-" );
5226 if ( rNum.IsSet() && rNum.GetNatNum() == 1 &&
5227 MsLangId::getRealLanguage( rNum.GetLang() ) ==
5228 LANGUAGE_THAI )
5230 aStr.insert( 3, "D07041E]" ); // date in Thai digit, Buddhist era
5232 else
5234 aStr.insert( 3, "107041E]" ); // date in Arabic digit, Buddhist era
5236 j = j+2;
5238 LCIDInserted = true;
5239 break;
5240 default:
5241 aStr.append( pStr[j] );
5246 // The Thai T NatNum modifier during Xcl export.
5247 if (rNum.IsSet() && rNum.GetNatNum() == 1 &&
5248 rKeywords[NF_KEY_THAI_T].equalsAscii( "T") &&
5249 MsLangId::getRealLanguage( rNum.GetLang()) ==
5250 LANGUAGE_THAI && !LCIDInserted )
5253 aStr.insert( 0, "[$-D00041E]" ); // number in Thai digit
5256 for ( ; nSub<4 && bDefault[nSub]; ++nSub )
5257 { // append empty subformats
5258 aStr.append( (sal_Unicode)';' );
5260 return aStr.makeStringAndClear();
5263 OUString SvNumberformat::ImpGetNatNumString( const SvNumberNatNum& rNum,
5264 sal_Int32 nVal, sal_uInt16 nMinDigits ) const
5266 OUString aStr;
5267 if ( nMinDigits )
5269 if ( nMinDigits == 2 )
5271 // speed up the most common case
5272 if ( 0 <= nVal && nVal < 10 )
5274 sal_Unicode aBuf[2];
5275 aBuf[0] = '0';
5276 aBuf[1] = '0' + nVal;
5277 aStr = OUString(aBuf, SAL_N_ELEMENTS(aBuf));
5279 else
5281 aStr = OUString::valueOf( nVal );
5284 else
5286 OUString aValStr( OUString::valueOf( nVal ) );
5287 if ( aValStr.getLength() >= nMinDigits )
5289 aStr = aValStr;
5291 else
5293 OUStringBuffer aBuf;
5294 for(sal_Int32 index = 0; index < nMinDigits - aValStr.getLength(); ++index)
5296 aBuf.append((sal_Unicode)'0');
5298 aBuf.append(aValStr);
5299 aStr = aBuf.makeStringAndClear();
5303 else
5305 aStr = OUString::valueOf( nVal );
5307 return impTransliterate(aStr, rNum);
5310 OUString SvNumberformat::impTransliterateImpl(const OUString& rStr,
5311 const SvNumberNatNum& rNum ) const
5313 com::sun::star::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
5314 return GetFormatter().GetNatNum()->getNativeNumberString( rStr,
5315 aLocale, rNum.GetNatNum() );
5318 void SvNumberformat::impTransliterateImpl(OUStringBuffer& rStr,
5319 const SvNumberNatNum& rNum ) const
5321 com::sun::star::lang::Locale aLocale( LanguageTag( rNum.GetLang() ).getLocale() );
5323 OUString sTemp(rStr.makeStringAndClear());
5324 sTemp = GetFormatter().GetNatNum()->getNativeNumberString( sTemp, aLocale, rNum.GetNatNum() );
5325 rStr.append(sTemp);
5328 void SvNumberformat::GetNatNumXml( com::sun::star::i18n::NativeNumberXmlAttributes& rAttr,
5329 sal_uInt16 nNumFor ) const
5331 if ( nNumFor <= 3 )
5333 const SvNumberNatNum& rNum = NumFor[nNumFor].GetNatNum();
5334 if ( rNum.IsSet() )
5336 com::sun::star::lang::Locale aLocale(
5337 LanguageTag( rNum.GetLang() ).getLocale() );
5338 rAttr = GetFormatter().GetNatNum()->convertToXmlAttributes(
5339 aLocale, rNum.GetNatNum() );
5341 else
5343 rAttr = com::sun::star::i18n::NativeNumberXmlAttributes();
5346 else
5348 rAttr = com::sun::star::i18n::NativeNumberXmlAttributes();
5352 // static
5353 bool SvNumberformat::HasStringNegativeSign( const OUString& rStr )
5355 // fuer Sign muss '-' am Anfang oder am Ende des TeilStrings sein (Blanks ignored)
5356 sal_Int32 nLen = rStr.getLength();
5357 if ( !nLen )
5359 return false;
5361 const sal_Unicode* const pBeg = rStr.getStr();
5362 const sal_Unicode* const pEnd = pBeg + nLen;
5363 register const sal_Unicode* p = pBeg;
5365 { // Anfang
5366 if ( *p == (sal_Unicode)'-' )
5368 return true;
5371 while ( *p == (sal_Unicode)' ' && ++p < pEnd );
5373 p = pEnd - 1;
5376 { // Ende
5377 if ( *p == (sal_Unicode)'-' )
5379 return true;
5382 while ( *p == (sal_Unicode)' ' && pBeg < --p );
5383 return false;
5386 // static
5387 bool SvNumberformat::IsInQuote( const OUString& rStr, sal_Int32 nPos,
5388 sal_Unicode cQuote, sal_Unicode cEscIn, sal_Unicode cEscOut )
5390 sal_Int32 nLen = rStr.getLength();
5391 if ( nPos >= nLen )
5393 return false;
5395 register const sal_Unicode* p0 = rStr.getStr();
5396 register const sal_Unicode* p = p0;
5397 register const sal_Unicode* p1 = p0 + nPos;
5398 bool bQuoted = false;
5399 while ( p <= p1 )
5401 if ( *p == cQuote )
5403 if ( p == p0 )
5405 bQuoted = true;
5407 else if ( bQuoted )
5409 if ( *(p-1) != cEscIn )
5411 bQuoted = false;
5414 else
5416 if ( *(p-1) != cEscOut )
5418 bQuoted = true;
5422 p++;
5424 return bQuoted;
5427 // static
5428 sal_Int32 SvNumberformat::GetQuoteEnd( const OUString& rStr, sal_Int32 nPos,
5429 sal_Unicode cQuote, sal_Unicode cEscIn,
5430 sal_Unicode cEscOut )
5432 sal_Int32 nLen = rStr.getLength();
5433 if ( nPos >= nLen )
5435 return -1;
5437 if ( !IsInQuote( rStr, nPos, cQuote, cEscIn, cEscOut ) )
5439 if ( rStr[ nPos ] == cQuote )
5441 return nPos; // schliessendes cQuote
5443 return -1;
5445 register const sal_Unicode* p0 = rStr.getStr();
5446 register const sal_Unicode* p = p0 + nPos;
5447 register const sal_Unicode* p1 = p0 + nLen;
5448 while ( p < p1 )
5450 if ( *p == cQuote && p > p0 && *(p-1) != cEscIn )
5452 return sal::static_int_cast< sal_Int32 >(p - p0);
5454 p++;
5456 return nLen; // String Ende
5459 sal_uInt16 SvNumberformat::ImpGetNumForStringElementCount( sal_uInt16 nNumFor ) const
5461 sal_uInt16 nCnt = 0;
5462 sal_uInt16 nAnz = NumFor[nNumFor].GetCount();
5463 short const * const pType = NumFor[nNumFor].Info().nTypeArray;
5464 for ( sal_uInt16 j=0; j<nAnz; ++j )
5466 switch ( pType[j] )
5468 case NF_SYMBOLTYPE_STRING:
5469 case NF_SYMBOLTYPE_CURRENCY:
5470 case NF_SYMBOLTYPE_DATESEP:
5471 case NF_SYMBOLTYPE_TIMESEP:
5472 case NF_SYMBOLTYPE_TIME100SECSEP:
5473 case NF_SYMBOLTYPE_PERCENT:
5474 ++nCnt;
5475 break;
5478 return nCnt;
5481 const CharClass& SvNumberformat::rChrCls() const
5483 return rScan.GetChrCls();
5486 const LocaleDataWrapper& SvNumberformat::rLoc() const
5488 return rScan.GetLoc();
5491 CalendarWrapper& SvNumberformat::GetCal() const
5493 return rScan.GetCal();
5496 const SvNumberFormatter& SvNumberformat::GetFormatter() const
5498 return *rScan.GetNumberformatter();
5501 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */