2002-05-14 Gonzalo Paniagua Javier <gonzalo@ximian.com>
[mono.git] / mcs / class / corlib / System / Int32.cs
1 //
2 // System.Int32.cs
3 //
4 // Author:
5 //   Miguel de Icaza (miguel@ximian.com)
6 //
7 // (C) Ximian, Inc.  http://www.ximian.com
8 //
9
10 using System.Globalization;
11 using System.Threading;
12
13 namespace System {
14         
15         [Serializable]
16         public struct Int32 : IComparable, IFormattable, IConvertible {
17
18                 public const int MaxValue = 0x7fffffff;
19                 public const int MinValue = -2147483648;
20                 
21                 public int value;
22
23                 public int CompareTo (object v)
24                 {
25                         if (v == null)
26                                 return 1;
27                         
28                         if (!(v is System.Int32))
29                                 throw new ArgumentException (Locale.GetText ("Value is not a System.Int32"));
30
31                         int xv = (int) v;
32                         if (value == xv)
33                                 return 0;
34                         if (value > xv)
35                                 return 1;
36                         else
37                                 return -1;
38                 }
39
40                 public override bool Equals (object o)
41                 {
42                         if (!(o is System.Int32))
43                                 return false;
44
45                         return ((int) o) == value;
46                 }
47
48                 public override int GetHashCode ()
49                 {
50                         return value;
51                 }
52
53                 public static int Parse (string s)
54                 {
55                         int val = 0;
56                         int len;
57                         int i, sign = 1;
58                         bool digits_seen = false;
59                         
60                         if (s == null)
61                                 throw new ArgumentNullException (Locale.GetText ("s is null"));
62
63                         len = s.Length;
64
65                         char c;
66                         for (i = 0; i < len; i++){
67                                 c = s [i];
68                                 if (!Char.IsWhiteSpace (c))
69                                         break;
70                         }
71                         
72                         if (i == len)
73                                 throw new FormatException ();
74
75                         c = s [i];
76                         if (c == '+')
77                                 i++;
78                         else if (c == '-'){
79                                 sign = -1;
80                                 i++;
81                         }
82                         
83                         for (; i < len; i++){
84                                 c = s [i];
85
86                                 if (c >= '0' && c <= '9'){
87                                         val = checked (val * 10 + (c - '0') * sign);
88                                         digits_seen = true;
89                                 } else {
90                                         if (Char.IsWhiteSpace (c)){
91                                                 for (i++; i < len; i++){
92                                                         if (!Char.IsWhiteSpace (s [i]))
93                                                                 throw new FormatException ();
94                                                 }
95                                                 break;
96                                         } else
97                                                 throw new FormatException ();
98                                 }
99                         }
100                         if (!digits_seen)
101                                 throw new FormatException ();
102                         
103                         return val;
104                 }
105
106                 public static int Parse (string s, IFormatProvider fp)
107                 {
108                         return Parse (s, NumberStyles.Integer, fp);
109                 }
110
111                 public static int Parse (string s, NumberStyles style)
112                 {
113                         return Parse (s, style, null);
114                 }
115
116                 internal static void CheckStyle (NumberStyles style)
117                 {
118                         if ((style & NumberStyles.AllowHexSpecifier) != 0) {
119                                 NumberStyles ne = style ^ NumberStyles.AllowHexSpecifier;
120                                 if ((ne & NumberStyles.AllowLeadingWhite) != 0)
121                                         ne ^= NumberStyles.AllowLeadingWhite;
122                                 if ((ne & NumberStyles.AllowTrailingWhite) != 0)
123                                         ne ^= NumberStyles.AllowTrailingWhite;
124                                 if (ne != 0)
125                                         throw new ArgumentException (
126                                                 "With AllowHexSpecifier only " + 
127                                                 "AllowLeadingWhite and AllowTrailingWhite " + 
128                                                 "are permitted.");
129                         }
130                 }
131                 
132                 internal static int JumpOverWhite (int pos, string s, bool excp)
133                 {
134                         while (pos < s.Length && Char.IsWhiteSpace (s [pos]))
135                                 pos++;
136
137                         if (excp && pos >= s.Length)
138                                 throw new FormatException ("Input string was not in the correct format.");
139
140                         return pos;
141                 }
142
143                 internal static void FindSign (ref int pos, string s, NumberFormatInfo nfi, 
144                                       ref bool foundSign, ref bool negative)
145                 {
146                         if ((pos + nfi.NegativeSign.Length) <= s.Length &&
147                              s.Substring (pos, nfi.NegativeSign.Length) == nfi.NegativeSign) {
148                                 negative = true;
149                                 foundSign = true;
150                                 pos += nfi.NegativeSign.Length;
151                         } 
152                         else if ((pos + nfi.PositiveSign.Length) < s.Length &&
153                              s.Substring (pos, nfi.PositiveSign.Length) == nfi.PositiveSign) {
154                                 negative = false;
155                                 pos += nfi.PositiveSign.Length;
156                                 foundSign = true;
157                         } 
158                 }
159
160                 internal static void FindCurrency (ref int pos,
161                                                  string s, 
162                                                  NumberFormatInfo nfi,
163                                                  ref bool foundCurrency)
164                 {
165                         if ((pos + nfi.CurrencySymbol.Length) <= s.Length &&
166                              s.Substring (pos, nfi.CurrencySymbol.Length) == nfi.CurrencySymbol) {
167                                 foundCurrency = true;
168                                 pos += nfi.CurrencySymbol.Length;
169                         } 
170                 }
171
172                 internal static bool FindOther (ref int pos,
173                                               string s, 
174                                               string other)
175                 {
176                         if ((pos + other.Length) <= s.Length &&
177                              s.Substring (pos, other.Length) == other) {
178                                 pos += other.Length;
179                                 return true;
180                         } 
181
182                         return false;
183                 }
184
185                 internal static bool ValidDigit (char e, bool allowHex)
186                 {
187                         if (allowHex)
188                                 return Char.IsDigit (e) || (e >= 'A' && e <= 'F') || (e >= 'a' && e <= 'f');
189
190                         return Char.IsDigit (e);
191                 }
192                 
193                 public static int Parse (string s, NumberStyles style, IFormatProvider fp)
194                 {
195                         if (s == null)
196                                 throw new ArgumentNullException ();
197
198                         if (s.Length == 0)
199                                 throw new FormatException ("Input string was not " + 
200                                                            "in the correct format.");
201
202                         NumberFormatInfo nfi;
203                         if (fp != null) {
204                                 Type typeNFI = typeof (System.Globalization.NumberFormatInfo);
205                                 nfi = (NumberFormatInfo) fp.GetFormat (typeNFI);
206                         }
207                         else
208                                 nfi = Thread.CurrentThread.CurrentCulture.NumberFormat;
209
210                         CheckStyle (style);
211
212                         bool AllowCurrencySymbol = (style & NumberStyles.AllowCurrencySymbol) != 0;
213                         bool AllowExponent = (style & NumberStyles.AllowExponent) != 0;
214                         bool AllowHexSpecifier = (style & NumberStyles.AllowHexSpecifier) != 0;
215                         bool AllowThousands = (style & NumberStyles.AllowThousands) != 0;
216                         bool AllowDecimalPoint = (style & NumberStyles.AllowDecimalPoint) != 0;
217                         bool AllowParentheses = (style & NumberStyles.AllowParentheses) != 0;
218                         bool AllowTrailingSign = (style & NumberStyles.AllowTrailingSign) != 0;
219                         bool AllowLeadingSign = (style & NumberStyles.AllowLeadingSign) != 0;
220                         bool AllowTrailingWhite = (style & NumberStyles.AllowTrailingWhite) != 0;
221                         bool AllowLeadingWhite = (style & NumberStyles.AllowLeadingWhite) != 0;
222
223                         int pos = 0;
224
225                         if (AllowLeadingWhite)
226                                 pos = JumpOverWhite (pos, s, true);
227
228                         bool foundOpenParentheses = false;
229                         bool negative = false;
230                         bool foundSign = false;
231                         bool foundCurrency = false;
232
233                         // Pre-number stuff
234                         if (AllowParentheses && s [pos] == '(') {
235                                 foundOpenParentheses = true;
236                                 foundSign = true;
237                                 negative = true; // MS always make the number negative when there parentheses
238                                                  // even when NumberFormatInfo.NumberNegativePattern != 0!!!
239                                 pos++;
240                                 if (AllowLeadingWhite)
241                                         pos = JumpOverWhite (pos, s, true);
242
243                                 if (s.Substring (pos, nfi.NegativeSign.Length) == nfi.NegativeSign)
244                                         throw new FormatException ("Input string was not in the correct " +
245                                                                    "format.");
246                                 if (s.Substring (pos, nfi.PositiveSign.Length) == nfi.PositiveSign)
247                                         throw new FormatException ("Input string was not in the correct " +
248                                                                    "format.");
249                         }
250
251                         if (AllowLeadingSign && !foundSign) {
252                                 // Sign + Currency
253                                 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
254                                 if (foundSign) {
255                                         if (AllowLeadingWhite)
256                                                 pos = JumpOverWhite (pos, s, true);
257                                         if (AllowCurrencySymbol) {
258                                                 FindCurrency (ref pos, s, nfi,
259                                                               ref foundCurrency);
260                                                 if (foundCurrency && AllowLeadingWhite)
261                                                         pos = JumpOverWhite (pos, s, true);
262                                         }
263                                 }
264                         }
265                         
266                         if (AllowCurrencySymbol && !foundCurrency) {
267                                 // Currency + sign
268                                 FindCurrency (ref pos, s, nfi, ref foundCurrency);
269                                 if (foundCurrency) {
270                                         if (AllowLeadingWhite)
271                                                 pos = JumpOverWhite (pos, s, true);
272                                         if (foundCurrency) {
273                                                 if (!foundSign && AllowLeadingSign) {
274                                                         FindSign (ref pos, s, nfi, ref foundSign,
275                                                                   ref negative);
276                                                         if (foundSign && AllowLeadingWhite)
277                                                                 pos = JumpOverWhite (pos, s, true);
278                                                 }
279                                         }
280                                 }
281                         }
282                         
283                         int number = 0;
284                         int nDigits = 0;
285                         bool decimalPointFound = false;
286                         int digitValue;
287                         char hexDigit;
288                                 
289                         // Number stuff
290                         do {
291
292                                 if (!ValidDigit (s [pos], AllowHexSpecifier)) {
293                                         if (AllowThousands &&
294                                             FindOther (ref pos, s, nfi.NumberGroupSeparator))
295                                             continue;
296                                         else
297                                         if (!decimalPointFound && AllowDecimalPoint &&
298                                             FindOther (ref pos, s, nfi.NumberDecimalSeparator)) {
299                                             decimalPointFound = true;
300                                             continue;
301                                         }
302
303                                         break;
304                                 }
305                                 else if (AllowHexSpecifier) {
306                                         nDigits++;
307                                         hexDigit = s [pos++];
308                                         if (Char.IsDigit (hexDigit))
309                                                 digitValue = (int) (hexDigit - '0');
310                                         else if (Char.IsLower (hexDigit))
311                                                 digitValue = (int) (hexDigit - 'a' + 10);
312                                         else
313                                                 digitValue = (int) (hexDigit - 'A' + 10);
314
315                                         number = checked (number * 16 - digitValue);
316                                 }
317                                 else if (decimalPointFound) {
318                                         nDigits++;
319                                         // Allows decimal point as long as it's only 
320                                         // followed by zeroes.
321                                         if (s [pos++] != '0')
322                                                 throw new OverflowException ("Value too large or too " +
323                                                                              "small.");
324                                 }
325                                 else {
326                                         nDigits++;
327
328                                         try {
329                                                 // Calculations done as negative
330                                                 // (abs (MinValue) > abs (MaxValue))
331                                                 number = checked (
332                                                         number * 10 - 
333                                                         (int) (s [pos++] - '0')
334                                                         );
335                                         } catch (OverflowException) {
336                                                 throw new OverflowException ("Value too large or too " +
337                                                                              "small.");
338                                         }
339                                 }
340                         } while (pos < s.Length);
341
342                         // Post number stuff
343                         if (nDigits == 0)
344                                 throw new FormatException ("Input string was not in the correct format.");
345
346                         if (AllowTrailingSign && !foundSign) {
347                                 // Sign + Currency
348                                 FindSign (ref pos, s, nfi, ref foundSign, ref negative);
349                                 if (foundSign) {
350                                         if (AllowTrailingWhite)
351                                                 pos = JumpOverWhite (pos, s, true);
352                                         if (AllowCurrencySymbol)
353                                                 FindCurrency (ref pos, s, nfi,
354                                                               ref foundCurrency);
355                                 }
356                         }
357                         
358                         if (AllowCurrencySymbol && !foundCurrency) {
359                                 // Currency + sign
360                                 FindCurrency (ref pos, s, nfi, ref foundCurrency);
361                                 if (foundCurrency) {
362                                         if (AllowTrailingWhite)
363                                                 pos = JumpOverWhite (pos, s, true);
364                                         if (!foundSign && AllowTrailingSign)
365                                                 FindSign (ref pos, s, nfi, ref foundSign,
366                                                           ref negative);
367                                 }
368                         }
369                         
370                         if (AllowTrailingWhite && pos < s.Length)
371                                 pos = JumpOverWhite (pos, s, false);
372
373                         if (foundOpenParentheses) {
374                                 if (pos >= s.Length || s [pos++] != ')')
375                                         throw new FormatException ("Input string was not in the correct " +
376                                                                    "format.");
377                                 if (AllowTrailingWhite && pos < s.Length)
378                                         pos = JumpOverWhite (pos, s, false);
379                         }
380
381                         if (pos < s.Length)
382                                 throw new FormatException ("Input string was not in the correct format.");
383
384                         
385                         if (!negative)
386                                 number = -number;
387
388                         return number;
389                 }
390
391                 public override string ToString ()
392                 {
393                         return IntegerFormatter.FormatGeneral(value, 0, null, true);
394                 }
395
396                 public string ToString (IFormatProvider fp)
397                 {
398                         return ToString (null, fp);
399                 }
400
401                 public string ToString (string format)
402                 {
403                         return ToString (format, null);
404                 }
405
406                 public string ToString (string format, IFormatProvider fp )
407                 {
408                         NumberFormatInfo nfi = NumberFormatInfo.GetInstance( fp );
409                         
410                         if ( format == null )
411                                 format = "G";
412
413                         return IntegerFormatter.NumberToString (format, nfi, value);
414                 }
415
416                 // =========== IConvertible Methods =========== //
417
418                 public TypeCode GetTypeCode ()
419                 {
420                         return TypeCode.Int32;
421                 }
422                 
423                 bool IConvertible.ToBoolean  (IFormatProvider provider)
424                 {
425                         return System.Convert.ToBoolean (value);
426                 }
427                 byte IConvertible.ToByte     (IFormatProvider provider)
428                 {
429                         return System.Convert.ToByte (value);
430                 }
431                 char IConvertible.ToChar     (IFormatProvider provider)
432                 {
433                         return System.Convert.ToChar (value);
434                 }
435                 DateTime IConvertible.ToDateTime (IFormatProvider provider)
436                 {
437                         return System.Convert.ToDateTime (value);
438                 }
439                 decimal IConvertible.ToDecimal  (IFormatProvider provider)
440                 {
441                         return System.Convert.ToDecimal (value);
442                 }
443                 double IConvertible.ToDouble   (IFormatProvider provider)
444                 {
445                         return System.Convert.ToDouble (value);
446                 }
447                 short IConvertible.ToInt16    (IFormatProvider provider)
448                 {
449                         return System.Convert.ToInt16 (value);
450                 }
451                 int IConvertible.ToInt32    (IFormatProvider provider)
452                 {
453                         return value;
454                 }
455                 long IConvertible.ToInt64    (IFormatProvider provider)
456                 {
457                         return System.Convert.ToInt64 (value);
458                 }
459                 [CLSCompliant(false)]
460                 sbyte IConvertible.ToSByte    (IFormatProvider provider)
461                 {
462                         return System.Convert.ToSByte (value);
463                 }
464                 float IConvertible.ToSingle   (IFormatProvider provider)
465                 {
466                         return System.Convert.ToSingle (value);
467                 }
468
469                 object IConvertible.ToType     (Type conversionType, IFormatProvider provider)
470                 {
471                         return System.Convert.ToType (value, conversionType, provider);
472                 }
473                 
474                 [CLSCompliant(false)]
475                 ushort IConvertible.ToUInt16   (IFormatProvider provider)
476                 {
477                         return System.Convert.ToUInt16 (value);
478                 }
479                 [CLSCompliant(false)]
480                 uint IConvertible.ToUInt32   (IFormatProvider provider)
481                 {
482                         return System.Convert.ToUInt32 (value);
483                 }
484                 [CLSCompliant(false)]
485                 ulong IConvertible.ToUInt64   (IFormatProvider provider)
486                 {
487                         return System.Convert.ToUInt64 (value);
488                 }
489         }
490 }