2 // System.Globalization.NumberFormatInfo.cs
5 // Derek Holden (dholden@draper.com)
6 // Bob Smith (bob@thestuff.net)
7 // Mohammad DAMT (mdamt@cdl2000.com)
10 // (C) Bob Smith http://www.thestuff.net
11 // (c) 2003, PT Cakram Datalingga Duaribu http://www.cdl2000.com
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 // NumberFormatInfo. One can only assume it is the class gotten
39 // back from a GetFormat() method from an IFormatProvider /
40 // IFormattable implementer. There are some discrepencies with the
41 // ECMA spec and the SDK docs, surprisingly. See my conversation
42 // with myself on it at:
43 // http://lists.ximian.com/archives/public/mono-list/2001-July/000794.html
45 // Other than that this is totally ECMA compliant.
48 namespace System
.Globalization
{
51 public sealed class NumberFormatInfo
: ICloneable
, IFormatProvider
{
52 private bool isReadOnly
;
53 // used for temporary storage. Used in InitPatterns ()
54 string decimalFormats
;
55 string currencyFormats
;
56 string percentFormats
;
57 string digitPattern
= "#";
58 string zeroPattern
= "0";
60 // Currency Related Format Info
61 private int currencyDecimalDigits
;
62 private string currencyDecimalSeparator
;
63 private string currencyGroupSeparator
;
64 private int[] currencyGroupSizes
;
65 private int currencyNegativePattern
;
66 private int currencyPositivePattern
;
67 private string currencySymbol
;
69 private string nanSymbol
;
70 private string negativeInfinitySymbol
;
71 private string negativeSign
;
73 // Number Related Format Info
74 private int numberDecimalDigits
;
75 private string numberDecimalSeparator
;
76 private string numberGroupSeparator
;
77 private int[] numberGroupSizes
;
78 private int numberNegativePattern
;
80 // Percent Related Format Info
81 private int percentDecimalDigits
;
82 private string percentDecimalSeparator
;
83 private string percentGroupSeparator
;
84 private int[] percentGroupSizes
;
85 private int percentNegativePattern
;
86 private int percentPositivePattern
;
87 private string percentSymbol
;
89 private string perMilleSymbol
;
90 private string positiveInfinitySymbol
;
91 private string positiveSign
;
93 string ansiCurrencySymbol
; // TODO, MS.NET serializes this.
94 int m_dataItem
; // Unused, but MS.NET serializes this.
95 bool m_useUserOverride
; // Unused, but MS.NET serializes this.
96 bool validForParseAsNumber
; // Unused, but MS.NET serializes this.
97 bool validForParseAsCurrency
; // Unused, but MS.NET serializes this.
99 internal NumberFormatInfo (int lcid
)
101 //FIXME: should add more LCID
102 // CultureInfo uses this one also.
108 // The Invariant Culture Info ID.
112 // Currency Related Format Info
113 currencyDecimalDigits
= 2;
114 currencyDecimalSeparator
= ".";
115 currencyGroupSeparator
= ",";
116 currencyGroupSizes
= new int[1] { 3 }
;
117 currencyNegativePattern
= 0;
118 currencyPositivePattern
= 0;
119 currencySymbol
= "$";
122 negativeInfinitySymbol
= "-Infinity";
125 // Number Related Format Info
126 numberDecimalDigits
= 2;
127 numberDecimalSeparator
= ".";
128 numberGroupSeparator
= ",";
129 numberGroupSizes
= new int[1] { 3 }
;
130 numberNegativePattern
= 1;
132 // Percent Related Format Info
133 percentDecimalDigits
= 2;
134 percentDecimalSeparator
= ".";
135 percentGroupSeparator
= ",";
136 percentGroupSizes
= new int[1] { 3 }
;
137 percentNegativePattern
= 0;
138 percentPositivePattern
= 0;
141 perMilleSymbol
= "\u2030";
142 positiveInfinitySymbol
= "Infinity";
148 public NumberFormatInfo () : this (0x007f)
152 // this is called by mono/mono/metadata/locales.c
155 string [] partOne
, partTwo
;
156 string [] posNeg
= decimalFormats
.Split (new char [1] {';'}
, 2);
158 if (posNeg
.Length
== 2) {
160 partOne
= posNeg
[0].Split (new char [1] {'.'}
, 2);
162 if (partOne
.Length
== 2) {
163 // assumed same for both positive and negative
164 // decimal digit side
165 numberDecimalDigits
= 0;
166 for (int i
= 0; i
< partOne
[1].Length
; i
++) {
167 if (partOne
[1][i
] == digitPattern
[0]) {
168 numberDecimalDigits
++;
173 // decimal grouping side
174 partTwo
= partOne
[0].Split (',');
175 if (partTwo
.Length
> 1) {
176 numberGroupSizes
= new int [partTwo
.Length
- 1];
177 for (int i
= 0; i
< numberGroupSizes
.Length
; i
++) {
178 string pat
= partTwo
[i
+ 1];
179 numberGroupSizes
[i
] = pat
.Length
;
182 numberGroupSizes
= new int [1] { 0 }
;
185 if (posNeg
[1].StartsWith ("(") && posNeg
[1].EndsWith (")")) {
186 numberNegativePattern
= 0;
187 } else if (posNeg
[1].StartsWith ("- ")) {
188 numberNegativePattern
= 2;
189 } else if (posNeg
[1].StartsWith ("-")) {
190 numberNegativePattern
= 1;
191 } else if (posNeg
[1].EndsWith (" -")) {
192 numberNegativePattern
= 4;
193 } else if (posNeg
[1].EndsWith ("-")) {
194 numberNegativePattern
= 3;
196 numberNegativePattern
= 1;
201 posNeg
= currencyFormats
.Split (new char [1] {';'}
, 2);
202 if (posNeg
.Length
== 2) {
203 partOne
= posNeg
[0].Split (new char [1] {'.'}
, 2);
205 if (partOne
.Length
== 2) {
206 // assumed same for both positive and negative
207 // decimal digit side
208 currencyDecimalDigits
= 0;
209 for (int i
= 0; i
< partOne
[1].Length
; i
++) {
210 if (partOne
[1][i
] == zeroPattern
[0])
211 currencyDecimalDigits
++;
216 // decimal grouping side
217 partTwo
= partOne
[0].Split (',');
218 if (partTwo
.Length
> 1) {
219 currencyGroupSizes
= new int [partTwo
.Length
- 1];
220 for (int i
= 0; i
< currencyGroupSizes
.Length
; i
++) {
221 string pat
= partTwo
[i
+ 1];
222 currencyGroupSizes
[i
] = pat
.Length
;
225 currencyGroupSizes
= new int [1] { 0 }
;
228 if (posNeg
[1].StartsWith ("(\u00a4 ") && posNeg
[1].EndsWith (")")) {
229 currencyNegativePattern
= 14;
230 } else if (posNeg
[1].StartsWith ("(\u00a4") && posNeg
[1].EndsWith (")")) {
231 currencyNegativePattern
= 0;
232 } else if (posNeg
[1].StartsWith ("\u00a4 ") && posNeg
[1].EndsWith ("-")) {
233 currencyNegativePattern
= 11;
234 } else if (posNeg
[1].StartsWith ("\u00a4") && posNeg
[1].EndsWith ("-")) {
235 currencyNegativePattern
= 3;
236 } else if (posNeg
[1].StartsWith ("(") && posNeg
[1].EndsWith (" \u00a4")) {
237 currencyNegativePattern
= 15;
238 } else if (posNeg
[1].StartsWith ("(") && posNeg
[1].EndsWith ("\u00a4")) {
239 currencyNegativePattern
= 4;
240 } else if (posNeg
[1].StartsWith ("-") && posNeg
[1].EndsWith (" \u00a4")) {
241 currencyNegativePattern
= 8;
242 } else if (posNeg
[1].StartsWith ("-") && posNeg
[1].EndsWith ("\u00a4")) {
243 currencyNegativePattern
= 5;
244 } else if (posNeg
[1].StartsWith ("-\u00a4 ")) {
245 currencyNegativePattern
= 9;
246 } else if (posNeg
[1].StartsWith ("-\u00a4")) {
247 currencyNegativePattern
= 1;
248 } else if (posNeg
[1].StartsWith ("\u00a4 -")) {
249 currencyNegativePattern
= 12;
250 } else if (posNeg
[1].StartsWith ("\u00a4-")) {
251 currencyNegativePattern
= 2;
252 } else if (posNeg
[1].EndsWith (" \u00a4-")) {
253 currencyNegativePattern
= 10;
254 } else if (posNeg
[1].EndsWith ("\u00a4-")) {
255 currencyNegativePattern
= 7;
256 } else if (posNeg
[1].EndsWith ("- \u00a4")) {
257 currencyNegativePattern
= 13;
258 } else if (posNeg
[1].EndsWith ("-\u00a4")) {
259 currencyNegativePattern
= 6;
261 currencyNegativePattern
= 0;
264 if (posNeg
[0].StartsWith ("\u00a4 ")) {
265 currencyPositivePattern
= 2;
266 } else if (posNeg
[0].StartsWith ("\u00a4")) {
267 currencyPositivePattern
= 0;
268 } else if (posNeg
[0].EndsWith (" \u00a4")) {
269 currencyPositivePattern
= 3;
270 } else if (posNeg
[0].EndsWith ("\u00a4")) {
271 currencyPositivePattern
= 1;
273 currencyPositivePattern
= 0;
278 // we don't have percentNegativePattern in CLDR so
279 // the percentNegativePattern are just guesses
280 if (percentFormats
.StartsWith ("%")) {
281 percentPositivePattern
= 2;
282 percentNegativePattern
= 2;
283 } else if (percentFormats
.EndsWith (" %")) {
284 percentPositivePattern
= 0;
285 percentNegativePattern
= 0;
286 } else if (percentFormats
.EndsWith ("%")) {
287 percentPositivePattern
= 1;
288 percentNegativePattern
= 1;
290 percentPositivePattern
= 0;
291 percentNegativePattern
= 0;
294 partOne
= percentFormats
.Split (new char [1] {'.'}
, 2);
296 if (partOne
.Length
== 2) {
297 // assumed same for both positive and negative
298 // decimal digit side
299 percentDecimalDigits
= 0;
300 for (int i
= 0; i
< partOne
[1].Length
; i
++) {
301 if (partOne
[1][i
] == digitPattern
[0])
302 percentDecimalDigits
++;
307 // percent grouping side
308 partTwo
= partOne
[0].Split (',');
309 if (partTwo
.Length
> 1) {
310 percentGroupSizes
= new int [partTwo
.Length
- 1];
311 for (int i
= 0; i
< percentGroupSizes
.Length
; i
++) {
312 string pat
= partTwo
[i
+ 1];
313 percentGroupSizes
[i
] = pat
.Length
;
316 percentGroupSizes
= new int [1] { 0 }
;
322 // =========== Currency Format Properties =========== //
324 public int CurrencyDecimalDigits
{
326 return currencyDecimalDigits
;
330 if (value < 0 || value > 99)
331 throw new ArgumentOutOfRangeException
332 ("The value specified for the property is less than 0 or greater than 99");
335 throw new InvalidOperationException
336 ("The current instance is read-only and a set operation was attempted");
338 currencyDecimalDigits
= value;
342 public string CurrencyDecimalSeparator
{
344 return currencyDecimalSeparator
;
349 throw new ArgumentNullException
350 ("The value specified for the property is a null reference");
353 throw new InvalidOperationException
354 ("The current instance is read-only and a set operation was attempted");
356 currencyDecimalSeparator
= value;
361 public string CurrencyGroupSeparator
{
363 return currencyGroupSeparator
;
368 throw new ArgumentNullException
369 ("The value specified for the property is a null reference");
372 throw new InvalidOperationException
373 ("The current instance is read-only and a set operation was attempted");
375 currencyGroupSeparator
= value;
379 public int[] CurrencyGroupSizes
{
381 return (int []) currencyGroupSizes
.Clone ();
385 if (value == null || value.Length
== 0)
386 throw new ArgumentNullException
387 ("The value specified for the property is a null reference");
390 throw new InvalidOperationException
391 ("The current instance is read-only and a set operation was attempted");
393 // All elements except last need to be in range 1 - 9, last can be 0.
394 int last
= value.Length
- 1;
396 for (int i
= 0; i
< last
; i
++)
397 if (value[i
] < 1 || value[i
] > 9)
398 throw new ArgumentOutOfRangeException
399 ("One of the elements in the array specified is not between 1 and 9");
401 if (value[last
] < 0 || value[last
] > 9)
402 throw new ArgumentOutOfRangeException
403 ("Last element in the array specified is not between 0 and 9");
405 currencyGroupSizes
= (int[]) value.Clone();
409 public int CurrencyNegativePattern
{
411 // See ECMA NumberFormatInfo page 8
412 return currencyNegativePattern
;
416 if (value < 0 || value > 15)
417 throw new ArgumentOutOfRangeException
418 ("The value specified for the property is less than 0 or greater than 15");
421 throw new InvalidOperationException
422 ("The current instance is read-only and a set operation was attempted");
424 currencyNegativePattern
= value;
428 public int CurrencyPositivePattern
{
430 // See ECMA NumberFormatInfo page 11
431 return currencyPositivePattern
;
435 if (value < 0 || value > 3)
436 throw new ArgumentOutOfRangeException
437 ("The value specified for the property is less than 0 or greater than 3");
440 throw new InvalidOperationException
441 ("The current instance is read-only and a set operation was attempted");
443 currencyPositivePattern
= value;
447 public string CurrencySymbol
{
449 return currencySymbol
;
454 throw new ArgumentNullException
455 ("The value specified for the property is a null reference");
458 throw new InvalidOperationException
459 ("The current instance is read-only and a set operation was attempted");
461 currencySymbol
= value;
465 // =========== Static Read-Only Properties =========== //
467 public static NumberFormatInfo CurrentInfo
{
469 NumberFormatInfo nfi
= (NumberFormatInfo
) System
.Threading
.Thread
.CurrentThread
.CurrentCulture
.NumberFormat
;
470 nfi
.isReadOnly
= true;
475 public static NumberFormatInfo InvariantInfo
{
477 // This uses invariant info, which is same as in the constructor
478 NumberFormatInfo nfi
= new NumberFormatInfo ();
479 nfi
.NumberNegativePattern
= 1;
480 nfi
.isReadOnly
= true;
485 public bool IsReadOnly
{
493 public string NaNSymbol
{
500 throw new ArgumentNullException
501 ("The value specified for the property is a null reference");
504 throw new InvalidOperationException
505 ("The current instance is read-only and a set operation was attempted");
511 public string NegativeInfinitySymbol
{
513 return negativeInfinitySymbol
;
518 throw new ArgumentNullException
519 ("The value specified for the property is a null reference");
522 throw new InvalidOperationException
523 ("The current instance is read-only and a set operation was attempted");
525 negativeInfinitySymbol
= value;
529 public string NegativeSign
{
536 throw new ArgumentNullException
537 ("The value specified for the property is a null reference");
540 throw new InvalidOperationException
541 ("The current instance is read-only and a set operation was attempted");
543 negativeSign
= value;
547 // =========== Number Format Properties =========== //
549 public int NumberDecimalDigits
{
551 return numberDecimalDigits
;
555 if (value < 0 || value > 99)
556 throw new ArgumentOutOfRangeException
557 ("The value specified for the property is less than 0 or greater than 99");
560 throw new InvalidOperationException
561 ("The current instance is read-only and a set operation was attempted");
563 numberDecimalDigits
= value;
567 public string NumberDecimalSeparator
{
569 return numberDecimalSeparator
;
574 throw new ArgumentNullException
575 ("The value specified for the property is a null reference");
578 throw new InvalidOperationException
579 ("The current instance is read-only and a set operation was attempted");
581 numberDecimalSeparator
= value;
586 public string NumberGroupSeparator
{
588 return numberGroupSeparator
;
593 throw new ArgumentNullException
594 ("The value specified for the property is a null reference");
597 throw new InvalidOperationException
598 ("The current instance is read-only and a set operation was attempted");
600 numberGroupSeparator
= value;
604 public int[] NumberGroupSizes
{
606 return (int []) numberGroupSizes
.Clone ();
610 if (value == null || value.Length
== 0)
611 throw new ArgumentNullException
612 ("The value specified for the property is a null reference");
615 throw new InvalidOperationException
616 ("The current instance is read-only and a set operation was attempted");
618 // All elements except last need to be in range 1 - 9, last can be 0.
619 int last
= value.Length
- 1;
621 for (int i
= 0; i
< last
; i
++)
622 if (value[i
] < 1 || value[i
] > 9)
623 throw new ArgumentOutOfRangeException
624 ("One of the elements in the array specified is not between 1 and 9");
626 if (value[last
] < 0 || value[last
] > 9)
627 throw new ArgumentOutOfRangeException
628 ("Last element in the array specified is not between 0 and 9");
630 numberGroupSizes
= (int[]) value.Clone();
634 public int NumberNegativePattern
{
636 // See ECMA NumberFormatInfo page 27
637 return numberNegativePattern
;
641 if (value < 0 || value > 4)
642 throw new ArgumentOutOfRangeException
643 ("The value specified for the property is less than 0 or greater than 15");
646 throw new InvalidOperationException
647 ("The current instance is read-only and a set operation was attempted");
649 numberNegativePattern
= value;
653 // =========== Percent Format Properties =========== //
655 public int PercentDecimalDigits
{
657 return percentDecimalDigits
;
661 if (value < 0 || value > 99)
662 throw new ArgumentOutOfRangeException
663 ("The value specified for the property is less than 0 or greater than 99");
666 throw new InvalidOperationException
667 ("The current instance is read-only and a set operation was attempted");
669 percentDecimalDigits
= value;
673 public string PercentDecimalSeparator
{
675 return percentDecimalSeparator
;
680 throw new ArgumentNullException
681 ("The value specified for the property is a null reference");
684 throw new InvalidOperationException
685 ("The current instance is read-only and a set operation was attempted");
687 percentDecimalSeparator
= value;
692 public string PercentGroupSeparator
{
694 return percentGroupSeparator
;
699 throw new ArgumentNullException
700 ("The value specified for the property is a null reference");
703 throw new InvalidOperationException
704 ("The current instance is read-only and a set operation was attempted");
706 percentGroupSeparator
= value;
710 public int[] PercentGroupSizes
{
712 return (int []) percentGroupSizes
.Clone ();
716 if (value == null || value.Length
== 0)
717 throw new ArgumentNullException
718 ("The value specified for the property is a null reference");
721 throw new InvalidOperationException
722 ("The current instance is read-only and a set operation was attempted");
724 if (this == CultureInfo
.CurrentCulture
.NumberFormat
)
725 throw new Exception ("HERE the value was modified");
726 // All elements except last need to be in range 1 - 9, last can be 0.
727 int last
= value.Length
- 1;
729 for (int i
= 0; i
< last
; i
++)
730 if (value[i
] < 1 || value[i
] > 9)
731 throw new ArgumentOutOfRangeException
732 ("One of the elements in the array specified is not between 1 and 9");
734 if (value[last
] < 0 || value[last
] > 9)
735 throw new ArgumentOutOfRangeException
736 ("Last element in the array specified is not between 0 and 9");
738 percentGroupSizes
= (int[]) value.Clone();
742 public int PercentNegativePattern
{
744 // See ECMA NumberFormatInfo page 8
745 return percentNegativePattern
;
749 if (value < 0 || value > 2)
750 throw new ArgumentOutOfRangeException
751 ("The value specified for the property is less than 0 or greater than 15");
754 throw new InvalidOperationException
755 ("The current instance is read-only and a set operation was attempted");
757 percentNegativePattern
= value;
761 public int PercentPositivePattern
{
763 // See ECMA NumberFormatInfo page 11
764 return percentPositivePattern
;
768 if (value < 0 || value > 2)
769 throw new ArgumentOutOfRangeException
770 ("The value specified for the property is less than 0 or greater than 3");
773 throw new InvalidOperationException
774 ("The current instance is read-only and a set operation was attempted");
776 percentPositivePattern
= value;
780 public string PercentSymbol
{
782 return percentSymbol
;
787 throw new ArgumentNullException
788 ("The value specified for the property is a null reference");
791 throw new InvalidOperationException
792 ("The current instance is read-only and a set operation was attempted");
794 percentSymbol
= value;
798 public string PerMilleSymbol
{
800 return perMilleSymbol
;
805 throw new ArgumentNullException
806 ("The value specified for the property is a null reference");
809 throw new InvalidOperationException
810 ("The current instance is read-only and a set operation was attempted");
812 perMilleSymbol
= value;
816 public string PositiveInfinitySymbol
{
818 return positiveInfinitySymbol
;
823 throw new ArgumentNullException
824 ("The value specified for the property is a null reference");
827 throw new InvalidOperationException
828 ("The current instance is read-only and a set operation was attempted");
830 positiveInfinitySymbol
= value;
834 public string PositiveSign
{
841 throw new ArgumentNullException
842 ("The value specified for the property is a null reference");
845 throw new InvalidOperationException
846 ("The current instance is read-only and a set operation was attempted");
848 positiveSign
= value;
852 public object GetFormat (Type formatType
)
854 // work around http://bugzilla.ximian.com/show_bug.cgi?id=55978
855 // the comparison fails because formatType likely comes from another domain
856 //return (formatType == typeof (NumberFormatInfo)) ? this : null;
860 public object Clone ()
862 NumberFormatInfo clone
= (NumberFormatInfo
) MemberwiseClone();
863 // clone is not read only
864 clone
.isReadOnly
= false;
868 public static NumberFormatInfo
ReadOnly (NumberFormatInfo nfi
)
870 NumberFormatInfo copy
= (NumberFormatInfo
)nfi
.Clone();
871 copy
.isReadOnly
= true;
875 public static NumberFormatInfo
GetInstance(IFormatProvider provider
)
877 if (provider
!= null) {
878 NumberFormatInfo nfi
;
879 nfi
= (NumberFormatInfo
)provider
.GetFormat(typeof(NumberFormatInfo
));