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