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