Merge remote branch 'upstream/master'
[mono.git] / mcs / class / corlib / System.Globalization / NumberFormatInfo.cs
1 //
2 // System.Globalization.NumberFormatInfo.cs
3 //
4 // Author:
5 //   Derek Holden (dholden@draper.com)
6 //   Bob Smith    (bob@thestuff.net)
7 //   Mohammad DAMT (mdamt@cdl2000.com)
8 //
9 // (C) Derek Holden
10 // (C) Bob Smith     http://www.thestuff.net
11 // (c) 2003, PT Cakram Datalingga Duaribu   http://www.cdl2000.com
12 //
13
14 //
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
16 //
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:
24 // 
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
27 // 
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.
35 //
36
37 //
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
44 // 
45 // Other than that this is totally ECMA compliant.
46 //
47
48 using System.Runtime.InteropServices;
49
50 namespace System.Globalization {
51
52         [ComVisible (true)]
53         [Serializable]
54         public sealed class NumberFormatInfo : ICloneable, IFormatProvider {
55
56 /* Keep in sync with object-internals.h */
57                 
58 #pragma warning disable 649
59                 private bool isReadOnly;
60                 // used for temporary storage. Used in InitPatterns ()
61                 string decimalFormats;
62                 string currencyFormats;
63                 string percentFormats;
64                 string digitPattern = "#";
65                 string zeroPattern = "0";
66                 
67                 // Currency Related Format Info
68                 private int currencyDecimalDigits;
69                 private string currencyDecimalSeparator;
70                 private string currencyGroupSeparator;
71                 private int[] currencyGroupSizes;
72                 private int currencyNegativePattern;
73                 private int currencyPositivePattern;
74                 private string currencySymbol;
75
76                 private string nanSymbol;
77                 private string negativeInfinitySymbol;
78                 private string negativeSign;
79
80                 // Number Related Format Info
81                 private int numberDecimalDigits;
82                 private string numberDecimalSeparator;
83                 private string numberGroupSeparator;
84                 private int[] numberGroupSizes;
85                 private int numberNegativePattern;
86
87                 // Percent Related Format Info
88                 private int percentDecimalDigits;
89                 private string percentDecimalSeparator;
90                 private string percentGroupSeparator;
91                 private int[] percentGroupSizes;
92                 private int percentNegativePattern;
93                 private int percentPositivePattern;
94                 private string percentSymbol;
95
96                 private string perMilleSymbol;
97                 private string positiveInfinitySymbol;
98                 private string positiveSign;
99 #pragma warning restore 649
100                 
101 #pragma warning disable 169
102                 string ansiCurrencySymbol;      // TODO, MS.NET serializes this.
103                 int m_dataItem; // Unused, but MS.NET serializes this.
104                 bool m_useUserOverride; // Unused, but MS.NET serializes this.
105                 bool validForParseAsNumber; // Unused, but MS.NET serializes this.
106                 bool validForParseAsCurrency; // Unused, but MS.NET serializes this.
107 #pragma warning restore 169
108                 
109                 string[] nativeDigits = invariantNativeDigits;
110                 int digitSubstitution = 1; // DigitShapes.None.
111
112                 static readonly string [] invariantNativeDigits = new string [] {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9"};
113
114                 internal NumberFormatInfo (int lcid, bool read_only)
115                 {
116                         isReadOnly = read_only;
117
118                         //FIXME: should add more LCID
119                         // CultureInfo uses this one also.
120                         if (lcid != 0x007F)
121                                 lcid = 0x007F;
122
123                         // The Invariant Culture Info ID.
124                         if (lcid == 0x007f) {
125                                 // Currency Related Format Info
126                                 currencyDecimalDigits =       2;
127                                 currencyDecimalSeparator =    ".";
128                                 currencyGroupSeparator =      ",";
129                                 currencyGroupSizes =          new int[1] { 3 };
130                                 currencyNegativePattern =     0;
131                                 currencyPositivePattern =     0;
132                                 currencySymbol =              "\u00a4";
133                                 
134                                 nanSymbol =                   "NaN";
135                                 negativeInfinitySymbol =      "-Infinity";
136                                 negativeSign =                "-";
137                                 
138                                 // Number Related Format Info
139                                 numberDecimalDigits =         2;
140                                 numberDecimalSeparator =      ".";
141                                 numberGroupSeparator =        ",";
142                                 numberGroupSizes =            new int[1] { 3 };
143                                 numberNegativePattern =       1;
144                                 
145                                 // Percent Related Format Info
146                                 percentDecimalDigits =        2;
147                                 percentDecimalSeparator =     ".";
148                                 percentGroupSeparator =       ",";
149                                 percentGroupSizes =           new int[1] { 3 };
150                                 percentNegativePattern =      0;
151                                 percentPositivePattern =      0;
152                                 percentSymbol=                "%";
153                                 
154                                 perMilleSymbol =              "\u2030";
155                                 positiveInfinitySymbol =      "Infinity";
156                                 positiveSign =                "+";
157                         }
158                 }
159
160                 internal NumberFormatInfo (bool read_only) : this (0x007f, read_only)
161                 {
162                 }
163                 
164                 public NumberFormatInfo () : this (false)
165                 {
166                 }
167
168                 // this is called by mono/mono/metadata/locales.c
169 #pragma warning disable 169             
170                 void InitPatterns ()
171                 {
172                         string [] partOne, partTwo;
173                         string [] posNeg = decimalFormats.Split (new char [1] {';'}, 2);
174                         
175                         if (posNeg.Length == 2) {
176                                 
177                                 partOne = posNeg [0].Split (new char [1] {'.'}, 2);
178                                                                                         
179                                 if (partOne.Length == 2) {
180                                         // assumed same for both positive and negative
181                                         // decimal digit side
182                                         numberDecimalDigits = 0;                                        
183                                         for (int i = 0; i < partOne [1].Length; i ++) {                                         
184                                                 if (partOne [1][i] == digitPattern [0]) {
185                                                         numberDecimalDigits ++;                                                 
186                                                 } else
187                                                         break;                                          
188                                         }
189
190                                         // decimal grouping side
191                                         partTwo = partOne [0].Split (',');
192                                         if (partTwo.Length > 1) {
193                                                 numberGroupSizes = new int [partTwo.Length - 1];
194                                                 for (int i = 0; i < numberGroupSizes.Length; i ++) {
195                                                         string pat = partTwo [i + 1];
196                                                         numberGroupSizes [i] = pat.Length;
197                                                 }
198                                         } else {
199                                                 numberGroupSizes = new int [1] { 0 };
200                                         }
201
202                                         if (posNeg [1].StartsWith ("(") && posNeg [1].EndsWith (")")) {
203                                                 numberNegativePattern = 0;
204                                         } else if (posNeg [1].StartsWith ("- ")) {
205                                                 numberNegativePattern = 2;
206                                         } else if (posNeg [1].StartsWith ("-")) {
207                                                 numberNegativePattern = 1;
208                                         } else if (posNeg [1].EndsWith (" -")) {
209                                                 numberNegativePattern = 4;
210                                         } else if (posNeg [1].EndsWith ("-")) {
211                                                 numberNegativePattern = 3;
212                                         } else {
213                                                 numberNegativePattern = 1;
214                                         }
215                                 }
216                         }
217
218                         posNeg = currencyFormats.Split (new char [1] {';'}, 2);                 
219                         if (posNeg.Length == 2) {
220                                 partOne = posNeg [0].Split (new char [1] {'.'}, 2);
221                                 
222                                 if (partOne.Length == 2) {
223                                         // assumed same for both positive and negative
224                                         // decimal digit side
225                                         currencyDecimalDigits = 0;
226                                         for (int i = 0; i < partOne [1].Length; i ++) {
227                                                 if (partOne [1][i] == zeroPattern [0])
228                                                         currencyDecimalDigits ++;
229                                                 else
230                                                         break;
231                                         }
232
233                                         // decimal grouping side
234                                         partTwo = partOne [0].Split (',');
235                                         if (partTwo.Length > 1) {                                               
236                                                 currencyGroupSizes = new int [partTwo.Length - 1];
237                                                 for (int i = 0; i < currencyGroupSizes.Length; i ++) {
238                                                         string pat = partTwo [i + 1];
239                                                         currencyGroupSizes [i] = pat.Length;
240                                                 }
241                                         } else {
242                                                 currencyGroupSizes = new int [1] { 0 };
243                                         }
244
245                                         if (posNeg [1].StartsWith ("(\u00a4 ") && posNeg [1].EndsWith (")")) {
246                                                 currencyNegativePattern = 14;
247                                         } else if (posNeg [1].StartsWith ("(\u00a4") && posNeg [1].EndsWith (")")) {
248                                                 currencyNegativePattern = 0;
249                                         } else if (posNeg [1].StartsWith ("\u00a4 ") && posNeg [1].EndsWith ("-")) {
250                                                 currencyNegativePattern = 11;
251                                         } else if (posNeg [1].StartsWith ("\u00a4") && posNeg [1].EndsWith ("-")) {
252                                                 currencyNegativePattern = 3;
253                                         } else if (posNeg [1].StartsWith ("(") && posNeg [1].EndsWith (" \u00a4")) {
254                                                 currencyNegativePattern = 15;
255                                         } else if (posNeg [1].StartsWith ("(") && posNeg [1].EndsWith ("\u00a4")) {
256                                                 currencyNegativePattern = 4;
257                                         } else if (posNeg [1].StartsWith ("-") && posNeg [1].EndsWith (" \u00a4")) {
258                                                 currencyNegativePattern = 8;
259                                         } else if (posNeg [1].StartsWith ("-") && posNeg [1].EndsWith ("\u00a4")) {
260                                                 currencyNegativePattern = 5;
261                                         } else if (posNeg [1].StartsWith ("-\u00a4 ")) {
262                                                 currencyNegativePattern = 9;
263                                         } else if (posNeg [1].StartsWith ("-\u00a4")) {
264                                                 currencyNegativePattern = 1;
265                                         } else if (posNeg [1].StartsWith ("\u00a4 -")) {
266                                                 currencyNegativePattern = 12;
267                                         } else if (posNeg [1].StartsWith ("\u00a4-")) {
268                                                 currencyNegativePattern = 2;
269                                         } else if (posNeg [1].EndsWith (" \u00a4-")) {
270                                                 currencyNegativePattern = 10;
271                                         } else if (posNeg [1].EndsWith ("\u00a4-")) {
272                                                 currencyNegativePattern = 7;
273                                         } else if (posNeg [1].EndsWith ("- \u00a4")) {
274                                                 currencyNegativePattern = 13;
275                                         } else if (posNeg [1].EndsWith ("-\u00a4")) {
276                                                 currencyNegativePattern = 6;
277                                         } else {
278                                                 currencyNegativePattern = 0;
279                                         }
280                                         
281                                         if (posNeg [0].StartsWith ("\u00a4 ")) {
282                                                 currencyPositivePattern = 2;
283                                         } else if (posNeg [0].StartsWith ("\u00a4")) {
284                                                 currencyPositivePattern = 0;
285                                         } else if (posNeg [0].EndsWith (" \u00a4")) {
286                                                 currencyPositivePattern = 3;
287                                         } else if (posNeg [0].EndsWith ("\u00a4")) {
288                                                 currencyPositivePattern = 1; 
289                                         } else {
290                                                 currencyPositivePattern = 0;
291                                         }
292                                 }
293                         }
294
295                         // we don't have percentNegativePattern in CLDR so 
296                         // the percentNegativePattern are just guesses
297                         if (percentFormats.StartsWith ("%")) {
298                                 percentPositivePattern = 2;
299                                 percentNegativePattern = 2;
300                         } else if (percentFormats.EndsWith (" %")) {
301                                 percentPositivePattern = 0;
302                                 percentNegativePattern = 0;
303                         } else if (percentFormats.EndsWith ("%")) {
304                                 percentPositivePattern = 1;
305                                 percentNegativePattern = 1;
306                         } else {
307                                 percentPositivePattern = 0;
308                                 percentNegativePattern = 0;
309                         }
310
311                         partOne = percentFormats.Split (new char [1] {'.'}, 2);
312                         
313                         if (partOne.Length == 2) {
314                                 // assumed same for both positive and negative
315                                 // decimal digit side
316                                 percentDecimalDigits = 0;
317                                 for (int i = 0; i < partOne [1].Length; i ++) {
318                                         if (partOne [1][i] == digitPattern [0])
319                                                 percentDecimalDigits ++;
320                                         else
321                                                 break;
322                                 }
323
324                                 // percent grouping side
325                                 partTwo = partOne [0].Split (',');
326                                 if (partTwo.Length > 1) {
327                                         percentGroupSizes = new int [partTwo.Length - 1];
328                                         for (int i = 0; i < percentGroupSizes.Length; i ++) {
329                                                 string pat = partTwo [i + 1];
330                                                 percentGroupSizes [i] = pat.Length;
331                                         }
332                                 } else {
333                                         percentGroupSizes = new int [1] { 0 };
334                                 }
335                         }
336                         
337                 }
338 #pragma warning restore 169
339
340                 // =========== Currency Format Properties =========== //
341
342                 public int CurrencyDecimalDigits {
343                         get {
344                                 return currencyDecimalDigits;
345                         }
346                         
347                         set {
348                                 if (value < 0 || value > 99) 
349                                         throw new ArgumentOutOfRangeException
350                                         ("The value specified for the property is less than 0 or greater than 99");
351                                 
352                                 if (isReadOnly)
353                                         throw new InvalidOperationException
354                                         ("The current instance is read-only and a set operation was attempted");
355
356                                 currencyDecimalDigits = value;
357                         }
358                 }
359
360                 public string CurrencyDecimalSeparator {
361                         get {
362                                 return currencyDecimalSeparator;
363                         }
364                         
365                         set {
366                                 if (value == null) 
367                                         throw new ArgumentNullException
368                                         ("The value specified for the property is a null reference");
369                                 
370                                 if (isReadOnly)
371                                         throw new InvalidOperationException
372                                         ("The current instance is read-only and a set operation was attempted");
373                                 
374                                 currencyDecimalSeparator = value;
375                         }
376                 }
377
378
379                 public string CurrencyGroupSeparator {
380                         get {
381                                 return currencyGroupSeparator;
382                         }
383                         
384                         set {
385                                 if (value == null) 
386                                         throw new ArgumentNullException
387                                         ("The value specified for the property is a null reference");
388                         
389                                 if (isReadOnly)
390                                         throw new InvalidOperationException
391                                         ("The current instance is read-only and a set operation was attempted");        
392                                 
393                                 currencyGroupSeparator = value;
394                         }
395                 }
396
397                 public int[] CurrencyGroupSizes {
398                         get {
399                                 return (int []) RawCurrencyGroupSizes.Clone ();
400                         }
401                         
402                         set {
403                                 RawCurrencyGroupSizes = value;
404                         }
405                 }
406
407                 internal int[] RawCurrencyGroupSizes {
408                         get {
409                                 return currencyGroupSizes;
410                         }
411                         
412                         set {
413                                 if (value == null) 
414                                         throw new ArgumentNullException
415                                         ("The value specified for the property is a null reference");
416                                 
417                                 if (isReadOnly)
418                                         throw new InvalidOperationException
419                                         ("The current instance is read-only and a set operation was attempted");
420                                 
421                                 if (value.Length == 0) {
422                                         currencyGroupSizes = new int [0];
423                                         return;
424                                 }
425                                 
426                                 // All elements except last need to be in range 1 - 9, last can be 0.
427                                 int last = value.Length - 1;
428
429                                 for (int i = 0; i < last; i++)
430                                         if (value[i] < 1 || value[i] > 9)
431                                                 throw new ArgumentOutOfRangeException
432                                                 ("One of the elements in the array specified is not between 1 and 9");
433
434                                 if (value[last] < 0 || value[last] > 9)
435                                         throw new ArgumentOutOfRangeException
436                                         ("Last element in the array specified is not between 0 and 9");
437                                 
438                                 currencyGroupSizes = (int[]) value.Clone();
439                         }
440                 }
441
442                 public int CurrencyNegativePattern {
443                         get {
444                                 // See ECMA NumberFormatInfo page 8
445                                 return currencyNegativePattern;
446                         }
447                         
448                         set {
449                                 if (value < 0 || value > 15) 
450                                         throw new ArgumentOutOfRangeException
451                                         ("The value specified for the property is less than 0 or greater than 15");
452                                 
453                                 if (isReadOnly)
454                                         throw new InvalidOperationException
455                                         ("The current instance is read-only and a set operation was attempted");
456
457                                 currencyNegativePattern = value;
458                         }
459                 }
460
461                 public int CurrencyPositivePattern {
462                         get {
463                                 // See ECMA NumberFormatInfo page 11 
464                                 return currencyPositivePattern;
465                         }
466                         
467                         set {
468                                 if (value < 0 || value > 3) 
469                                         throw new ArgumentOutOfRangeException
470                                         ("The value specified for the property is less than 0 or greater than 3");
471                                 
472                                 if (isReadOnly)
473                                         throw new InvalidOperationException
474                                         ("The current instance is read-only and a set operation was attempted");
475
476                                 currencyPositivePattern = value;
477                         }
478                 }
479
480                 public string CurrencySymbol {
481                         get {
482                                 return currencySymbol;
483                         }
484                         
485                         set {
486                                 if (value == null) 
487                                         throw new ArgumentNullException
488                                         ("The value specified for the property is a null reference");
489                         
490                                 if (isReadOnly)
491                                         throw new InvalidOperationException
492                                         ("The current instance is read-only and a set operation was attempted");        
493                                 
494                                 currencySymbol = value;
495                         }
496                 }
497
498                 // =========== Static Read-Only Properties =========== //
499
500                 public static NumberFormatInfo CurrentInfo {
501                         get {
502                                 NumberFormatInfo nfi = (NumberFormatInfo) System.Threading.Thread.CurrentThread.CurrentCulture.NumberFormat;
503                                 nfi.isReadOnly = true;
504                                 return nfi;
505                         }                      
506                 }
507
508                 public static NumberFormatInfo InvariantInfo {
509                         get {
510                                 // This uses invariant info, which is same as in the constructor
511                                 NumberFormatInfo nfi = new NumberFormatInfo (true);
512                                 return nfi;
513                         }
514                 }
515
516                 public bool IsReadOnly {
517                         get {
518                                 return isReadOnly;
519                         }
520                 }
521
522
523
524                 public string NaNSymbol {
525                         get {
526                                 return nanSymbol;
527                         }
528                         
529                         set {
530                                 if (value == null) 
531                                         throw new ArgumentNullException
532                                         ("The value specified for the property is a null reference");
533                         
534                                 if (isReadOnly)
535                                         throw new InvalidOperationException
536                                         ("The current instance is read-only and a set operation was attempted");        
537                                 
538                                 nanSymbol = value;
539                         }
540                 }
541                 
542 #if !NET_2_1
543                 [MonoNotSupported ("We don't have native digit info")]
544                 [ComVisible (false)]
545                 public string [] NativeDigits {
546                         get { return nativeDigits; }
547                         set {
548                                 if (value == null)
549                                         throw new ArgumentNullException ("value");
550                                 if (value.Length != 10)
551                                         throw new ArgumentException ("Argument array length must be 10");
552                                 foreach (string s in value)
553                                         if (String.IsNullOrEmpty (s))
554                                                 throw new ArgumentException ("Argument array contains one or more null strings");
555                                 nativeDigits = value;
556                         }
557                 }
558
559                 [MonoNotSupported ("We don't have native digit info")]
560                 [ComVisible (false)]
561                 public DigitShapes DigitSubstitution {
562                         get { return (DigitShapes) digitSubstitution; }
563                         set { digitSubstitution = (int) value; }
564                 }
565 #endif
566                 
567                 public string NegativeInfinitySymbol {
568                         get {
569                                 return negativeInfinitySymbol;
570                         }
571                         
572                         set {
573                                 if (value == null) 
574                                         throw new ArgumentNullException
575                                         ("The value specified for the property is a null reference");
576                         
577                                 if (isReadOnly)
578                                         throw new InvalidOperationException
579                                         ("The current instance is read-only and a set operation was attempted");        
580                                 
581                                 negativeInfinitySymbol = value;
582                         }
583                 }
584
585                 public string NegativeSign {
586                         get {
587                                 return negativeSign;
588                         }
589                         
590                         set {
591                                 if (value == null) 
592                                         throw new ArgumentNullException
593                                         ("The value specified for the property is a null reference");
594                         
595                                 if (isReadOnly)
596                                         throw new InvalidOperationException
597                                         ("The current instance is read-only and a set operation was attempted");        
598                                 
599                                 negativeSign = value;
600                         }
601                 }
602                 
603                 // =========== Number Format Properties =========== //
604
605                 public int NumberDecimalDigits {
606                         get {
607                                 return numberDecimalDigits;
608                         }
609                         
610                         set {
611                                 if (value < 0 || value > 99) 
612                                         throw new ArgumentOutOfRangeException
613                                         ("The value specified for the property is less than 0 or greater than 99");
614                                 
615                                 if (isReadOnly)
616                                         throw new InvalidOperationException
617                                         ("The current instance is read-only and a set operation was attempted");
618
619                                 numberDecimalDigits = value;
620                         }
621                 }               
622
623                 public string NumberDecimalSeparator {
624                         get {
625                                 return numberDecimalSeparator;
626                         }
627                         
628                         set {
629                                 if (value == null) 
630                                         throw new ArgumentNullException
631                                         ("The value specified for the property is a null reference");
632                                 
633                                 if (isReadOnly)
634                                         throw new InvalidOperationException
635                                         ("The current instance is read-only and a set operation was attempted");
636                                 
637                                 numberDecimalSeparator = value;
638                         }
639                 }
640
641
642                 public string NumberGroupSeparator {
643                         get {
644                                 return numberGroupSeparator;
645                         }
646                         
647                         set {
648                                 if (value == null) 
649                                         throw new ArgumentNullException
650                                         ("The value specified for the property is a null reference");
651                         
652                                 if (isReadOnly)
653                                         throw new InvalidOperationException
654                                         ("The current instance is read-only and a set operation was attempted");        
655                                 
656                                 numberGroupSeparator = value;
657                         }
658                 }
659
660                 public int[] NumberGroupSizes {
661                         get {
662                                 return (int []) RawNumberGroupSizes.Clone ();
663                         }
664                         
665                         set {
666                                 RawNumberGroupSizes = value;
667                         }
668                 }
669
670                 internal int[] RawNumberGroupSizes {
671                         get {
672                                 return numberGroupSizes;
673                         }
674                         
675                         set {
676                                 if (value == null) 
677                                         throw new ArgumentNullException
678                                         ("The value specified for the property is a null reference");
679                                 
680                                 if (isReadOnly)
681                                         throw new InvalidOperationException
682                                         ("The current instance is read-only and a set operation was attempted");
683                                 
684                                 if (value.Length == 0) {
685                                         numberGroupSizes = new int [0];
686                                         return;
687                                 }
688                                 // All elements except last need to be in range 1 - 9, last can be 0.
689                                 int last = value.Length - 1;
690
691                                 for (int i = 0; i < last; i++)
692                                         if (value[i] < 1 || value[i] > 9)
693                                                 throw new ArgumentOutOfRangeException
694                                                 ("One of the elements in the array specified is not between 1 and 9");
695
696                                 if (value[last] < 0 || value[last] > 9)
697                                         throw new ArgumentOutOfRangeException
698                                         ("Last element in the array specified is not between 0 and 9");
699                                 
700                                 numberGroupSizes = (int[]) value.Clone();
701                         }
702                 }
703
704                 public int NumberNegativePattern {
705                         get {
706                                 // See ECMA NumberFormatInfo page 27
707                                 return numberNegativePattern;
708                         }
709                         
710                         set {
711                                 if (value < 0 || value > 4) 
712                                         throw new ArgumentOutOfRangeException
713                                         ("The value specified for the property is less than 0 or greater than 15");
714                                 
715                                 if (isReadOnly)
716                                         throw new InvalidOperationException
717                                         ("The current instance is read-only and a set operation was attempted");
718
719                                 numberNegativePattern = value;
720                         }
721                 }
722
723                 // =========== Percent Format Properties =========== //
724
725                 public int PercentDecimalDigits {
726                         get {
727                                 return percentDecimalDigits;
728                         }
729                         
730                         set {
731                                 if (value < 0 || value > 99) 
732                                         throw new ArgumentOutOfRangeException
733                                         ("The value specified for the property is less than 0 or greater than 99");
734                                 
735                                 if (isReadOnly)
736                                         throw new InvalidOperationException
737                                         ("The current instance is read-only and a set operation was attempted");
738
739                                 percentDecimalDigits = value;
740                         }
741                 }
742
743                 public string PercentDecimalSeparator {
744                         get {
745                                 return percentDecimalSeparator;
746                         }
747                         
748                         set {
749                                 if (value == null) 
750                                         throw new ArgumentNullException
751                                         ("The value specified for the property is a null reference");
752                                 
753                                 if (isReadOnly)
754                                         throw new InvalidOperationException
755                                         ("The current instance is read-only and a set operation was attempted");
756                                 
757                                 percentDecimalSeparator = value;
758                         }
759                 }
760
761
762                 public string PercentGroupSeparator {
763                         get {
764                                 return percentGroupSeparator;
765                         }
766                         
767                         set {
768                                 if (value == null) 
769                                         throw new ArgumentNullException
770                                         ("The value specified for the property is a null reference");
771                         
772                                 if (isReadOnly)
773                                         throw new InvalidOperationException
774                                         ("The current instance is read-only and a set operation was attempted");        
775                                 
776                                 percentGroupSeparator = value;
777                         }
778                 }
779
780                 public int[] PercentGroupSizes {
781                         get {
782                                 return (int []) RawPercentGroupSizes.Clone ();
783                         }
784                         
785                         set {
786                                 RawPercentGroupSizes = value;
787                         }
788                 }
789
790                 internal int[] RawPercentGroupSizes {
791                         get {
792                                 return percentGroupSizes;
793                         }
794                         
795                         set {
796                                 if (value == null) 
797                                         throw new ArgumentNullException
798                                         ("The value specified for the property is a null reference");
799                                 
800                                 if (isReadOnly)
801                                         throw new InvalidOperationException
802                                         ("The current instance is read-only and a set operation was attempted");
803                                 
804                                 if (this == CultureInfo.CurrentCulture.NumberFormat)
805                                         throw new Exception ("HERE the value was modified");
806                                 
807                                 if (value.Length == 0) {
808                                         percentGroupSizes = new int [0];
809                                         return;
810                                 }
811
812                                 // All elements except last need to be in range 1 - 9, last can be 0.
813                                 int last = value.Length - 1;
814
815                                 for (int i = 0; i < last; i++)
816                                         if (value[i] < 1 || value[i] > 9)
817                                                 throw new ArgumentOutOfRangeException
818                                                 ("One of the elements in the array specified is not between 1 and 9");
819
820                                 if (value[last] < 0 || value[last] > 9)
821                                         throw new ArgumentOutOfRangeException
822                                         ("Last element in the array specified is not between 0 and 9");
823                                 
824                                 percentGroupSizes = (int[]) value.Clone();
825                         }
826                 }
827
828                 public int PercentNegativePattern {
829                         get {
830                                 // See ECMA NumberFormatInfo page 8
831                                 return percentNegativePattern;
832                         }
833                         
834                         set {
835                                 if (value < 0 || value > 2) 
836                                         throw new ArgumentOutOfRangeException
837                                         ("The value specified for the property is less than 0 or greater than 15");
838                                 
839                                 if (isReadOnly)
840                                         throw new InvalidOperationException
841                                         ("The current instance is read-only and a set operation was attempted");
842
843                                 percentNegativePattern = value;
844                         }
845                 }
846
847                 public int PercentPositivePattern {
848                         get {
849                                 // See ECMA NumberFormatInfo page 11 
850                                 return percentPositivePattern;
851                         }
852                         
853                         set {
854                                 if (value < 0 || value > 2) 
855                                         throw new ArgumentOutOfRangeException
856                                         ("The value specified for the property is less than 0 or greater than 3");
857                                 
858                                 if (isReadOnly)
859                                         throw new InvalidOperationException
860                                         ("The current instance is read-only and a set operation was attempted");
861
862                                 percentPositivePattern = value;
863                         }
864                 }
865
866                 public string PercentSymbol {
867                         get {
868                                 return percentSymbol;
869                         }
870                         
871                         set {
872                                 if (value == null) 
873                                         throw new ArgumentNullException
874                                         ("The value specified for the property is a null reference");
875                         
876                                 if (isReadOnly)
877                                         throw new InvalidOperationException
878                                         ("The current instance is read-only and a set operation was attempted");        
879                                 
880                                 percentSymbol = value;
881                         }
882                 }
883
884                 public string PerMilleSymbol {
885                         get {
886                                 return perMilleSymbol;
887                         }
888                         
889                         set {
890                                 if (value == null) 
891                                         throw new ArgumentNullException
892                                         ("The value specified for the property is a null reference");
893                                 
894                                 if (isReadOnly)
895                                         throw new InvalidOperationException
896                                         ("The current instance is read-only and a set operation was attempted");
897                                 
898                                 perMilleSymbol = value;
899                         }
900                 }
901
902                 public string PositiveInfinitySymbol {
903                         get {
904                                 return positiveInfinitySymbol;
905                         }
906                         
907                         set {
908                                 if (value == null) 
909                                         throw new ArgumentNullException
910                                         ("The value specified for the property is a null reference");
911                         
912                                 if (isReadOnly)
913                                         throw new InvalidOperationException
914                                         ("The current instance is read-only and a set operation was attempted");        
915                                 
916                                 positiveInfinitySymbol = value;
917                         }
918                 }
919
920                 public string PositiveSign {
921                         get {
922                                 return positiveSign;
923                         }
924                         
925                         set {
926                                 if (value == null) 
927                                         throw new ArgumentNullException
928                                         ("The value specified for the property is a null reference");
929                         
930                                 if (isReadOnly)
931                                         throw new InvalidOperationException
932                                         ("The current instance is read-only and a set operation was attempted");        
933                                 
934                                 positiveSign = value;
935                         }
936                 }
937
938                 public object GetFormat (Type formatType) 
939                 {
940                         return (formatType == typeof (NumberFormatInfo)) ? this : null;
941                 }
942                 
943                 public object Clone () 
944                 {
945                         NumberFormatInfo clone = (NumberFormatInfo) MemberwiseClone();
946                         // clone is not read only
947                         clone.isReadOnly = false;
948                         return clone;
949                 }
950
951                 public static NumberFormatInfo ReadOnly (NumberFormatInfo nfi)
952                 {
953                         NumberFormatInfo copy = (NumberFormatInfo)nfi.Clone();
954                         copy.isReadOnly = true;
955                         return copy;
956                 }                       
957
958                 public static NumberFormatInfo GetInstance(IFormatProvider formatProvider)
959                 {
960                         if (formatProvider != null) {
961                                 NumberFormatInfo nfi;
962                                 nfi = (NumberFormatInfo)formatProvider.GetFormat(typeof(NumberFormatInfo));
963                                 if (nfi != null)
964                                         return nfi;
965                         }
966                         
967                         return CurrentInfo;
968                 }
969         }
970 }