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