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