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