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