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