5 // Chris J Breisch (cjbreisch@altavista.net)
7 // (C) 2002 Chris J Breisch
11 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
13 // Permission is hereby granted, free of charge, to any person obtaining
14 // a copy of this software and associated documentation files (the
15 // "Software"), to deal in the Software without restriction, including
16 // without limitation the rights to use, copy, modify, merge, publish,
17 // distribute, sublicense, and/or sell copies of the Software, and to
18 // permit persons to whom the Software is furnished to do so, subject to
19 // the following conditions:
21 // The above copyright notice and this permission notice shall be
22 // included in all copies or substantial portions of the Software.
24 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
25 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
26 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
27 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
28 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
29 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
30 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Text.RegularExpressions;
35 using Microsoft.VisualBasic.CompilerServices;
37 namespace Microsoft.VisualBasic {
39 sealed public class Conversion {
42 /// Collection : The BASIC Collection Object
49 //Nothing to do, nobody should see this constructor
53 private static readonly char[] _HexDigits = {
54 '0', '1', '2', '3', '4', '5', '6', '7',
55 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
57 private static readonly char[] _OctDigits = {
58 '0', '1', '2', '3', '4', '5', '6', '7'
60 private static readonly long[] _Maxes = {
61 32767, 2147483647, 9223372036854775807
63 private enum SizeIndexes {
73 public static string ErrorToString () {
74 return Information.Err().Description;
77 public static string ErrorToString (System.Int32 ErrorNumber) {
78 if(ErrorNumber >= 65535)
79 throw new ArgumentException(VBUtils.GetResourceString("MaxErrNumber"));
81 return Information.Err().Description;
83 String errStr = VBUtils.GetResourceString(ErrorNumber);
86 errStr = VBUtils.GetResourceString(95);
91 // Return whether d is +/- Could do this with a macro,
92 // but this is cleaner
93 private static int Sign(double d) { return d > 0 ? 1 : -1;}
95 // try to cast an Object to a string...used in several places
96 private static string CastToString (System.Object Expression) {
98 return Expression.ToString();
101 throw new InvalidCastException();
105 // Fix on Integer types doesn't do anything
106 public static short Fix (short Number) { return Number; }
107 public static int Fix (int Number) { return Number; }
108 public static long Fix (long Number) { return Number; }
110 // Fix on other numberic types = Sign(Number) * Int(Abs(Number))
111 public static double Fix (double Number) {
112 return Sign(Number) * Int (Math.Abs (Number));
114 public static float Fix (float Number) {
115 return Sign(Number) * Int (Math.Abs (Number));
117 public static decimal Fix (decimal Number) {
118 return Sign((double)Number) * Int (Math.Abs (Number));
121 // Fix on an Object type is trickier
122 // first we have to cast it to the right type
123 public static System.Object Fix (System.Object Number)
125 // always start out by throwing an exception
127 if (Number == null) {
128 throw new ArgumentNullException ("Number",
129 "Value cannot be null");
132 TypeCode TC = Type.GetTypeCode (Number.GetType ());
134 // switch on TypeCode and call appropriate Fix method
136 case TypeCode.Decimal:
137 return Fix (Convert.ToDecimal (Number));
138 case TypeCode.Double:
139 return Fix (Convert.ToDouble (Number));
140 case TypeCode.Single:
141 return Fix (Convert.ToSingle (Number));
142 case TypeCode.String:
143 return Fix (Double.Parse (
144 CastToString (Number)));
146 // for integer types, don't need to do anything
152 case TypeCode.UInt16:
153 case TypeCode.UInt32:
154 case TypeCode.UInt64:
157 // spec defines Empty as returning 0
161 // we can't convert these types
162 case TypeCode.Boolean:
164 case TypeCode.DateTime:
165 case TypeCode.DBNull:
166 case TypeCode.Object:
168 throw new ArgumentException (
169 "Type of argument 'Number' is '"
170 + Number.GetType().FullName +
171 "', which is not numeric.");
175 // Int on Integer types doesn't do anything
176 public static short Int (short Number) { return Number; }
177 public static int Int (int Number) { return Number; }
178 public static long Int (long Number) { return Number; }
180 // Int on other numberic types is same thing as "Floor"
181 public static double Int (double Number) {
182 return (double) Math.Floor(Number);
184 public static float Int (float Number) {
185 return (float) Math.Floor(Number);
187 public static decimal Int (decimal Number) {
188 return Decimal.Floor(Number);
191 // doing an Int on an Object is trickier
192 // first we have to cast to the correct type
193 public static System.Object Int (System.Object Number) {
194 // always start out by throwing an exception
196 if (Number == null) {
197 throw new ArgumentNullException("Number",
198 "Value cannot be null");
201 TypeCode TC = Type.GetTypeCode (Number.GetType ());
203 // switch on TypeCode and call appropriate Int method
205 case TypeCode.Decimal:
206 return Int (Convert.ToDecimal (Number));
207 case TypeCode.Double:
208 return Int (Convert.ToDouble (Number));
209 case TypeCode.Single:
210 return Int (Convert.ToSingle (Number));
211 case TypeCode.String:
212 return Int (Double.Parse (
213 CastToString(Number)));
215 // Int on integer types does nothing
221 case TypeCode.UInt16:
222 case TypeCode.UInt32:
223 case TypeCode.UInt64:
226 // Spec defines Empty as returning 0
230 // otherwise, it's we can't cast to a numeric
231 case TypeCode.Boolean:
233 case TypeCode.DateTime:
234 case TypeCode.DBNull:
235 case TypeCode.Object:
237 throw new ArgumentException (
238 "Type of argument 'Number' is '" +
239 Number.GetType().FullName +
240 "', which is not numeric.");
244 // we use this internally to get a string
245 // representation of a number in a specific base
246 private static string ToBase (ulong Number, int Length,
247 char[] BaseDigits, uint Base) {
250 // we use a char array here for performance
251 char [] c = new Char[Length];
255 for (i = Length - 1; i >= 0; i--) {
257 Number = Number / Base;
258 c[i] = BaseDigits[r];
260 s = new string (c, i, Length - i);
265 return new string (c);
273 // convert a number to Hex
274 // a little bit of magic goes on here with negative #'s
275 private static string ToHex(long Number, int Digits,
280 // we add maxint of the Number's type
281 // twice and then 2 more...this has the
282 // effect of turning it into a ulong
283 // that has the same hex representation
284 UNumber = (ulong)((Number + 2) +
286 (ulong)_Maxes[(int)Size];
289 UNumber = (ulong)Number;
291 return ToBase(UNumber, Digits, _HexDigits, 16);
294 // call our private function,
295 // passing it the size of the item to convert
296 public static string Hex (short Number) {
297 return ToHex(Number, 4, SizeIndexes.Int16);
299 public static string Hex (byte Number) {
300 return ToHex(Number, 2, SizeIndexes.Int16);
302 public static string Hex (int Number) {
303 return ToHex(Number, 8, SizeIndexes.Int32);
305 public static string Hex (long Number) {
306 return ToHex(Number, 16, SizeIndexes.Int64);
309 // Objects are trickier
310 // first we have to cast to appropriate type
311 public static System.String Hex (System.Object Number) {
312 // always start out by throwing an exception
315 if (Number == null) {
316 throw new ArgumentNullException ("Number",
317 "Value cannot be null");
320 TypeCode TC = Type.GetTypeCode (Number.GetType ());
323 // try to parse the string as an Int32,
324 // then an Int64, if that fails
325 case TypeCode.String:
329 CastToString (Number)));
334 CastToString (Number)));
337 // for the int types,
338 // just call the normal "Hex" for that type
340 return Hex ((byte)Number);
342 return Hex ((short)Number);
344 return Hex ((int)Number);
346 return Hex ((long)Number);
348 // empty is defined as returning 0
351 case TypeCode.Single:
352 float fval = (float)Number;
353 lval = (long) Math.Round(fval);
354 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
355 return Hex ((int)lval);
358 case TypeCode.Double:
359 double dval = (double)Number;
360 if (dval > Int64.MaxValue || dval < Int64.MinValue)
361 throw new OverflowException(
362 VBUtils.GetResourceString("Overflow_Int64"));
363 lval = (long) Math.Round(dval);
364 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
365 return Hex((int)lval);
369 case TypeCode.Decimal:
370 Decimal big = new Decimal(Int64.MaxValue);
371 Decimal small = new Decimal(Int64.MinValue);
372 Decimal current = (Decimal)Number;
374 if (current.CompareTo(big) > 0 ||
375 current.CompareTo(small) < 0)
376 throw new OverflowException(
377 VBUtils.GetResourceString("Overflow_Int64"));
379 lval = Decimal.ToInt64(current);
380 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
381 return Hex((int)lval);
384 // we can't do any of these types
385 case TypeCode.Boolean:
387 case TypeCode.DBNull:
388 case TypeCode.DateTime:
389 case TypeCode.Object:
391 case TypeCode.UInt16:
392 case TypeCode.UInt32:
393 case TypeCode.UInt64:
395 throw new ArgumentException (
396 "Type of argument 'Number' is '" +
397 Number.GetType().FullName +
398 "', which is not numeric.");
402 // ToOct works just like ToHex, only in Octal.
403 private static string ToOct(long Number, int Digits,
408 // for neg numbers add the maxint of
409 // the appropriate size twice, and then two more
410 // this has the effect of turning it
411 // into a ulong with the same oct representation
412 UNumber = (ulong)((Number + 2) +
414 (ulong)(_Maxes[(int)Size]);
417 UNumber = (ulong)Number;
419 return ToBase (UNumber, Digits, _OctDigits, 8);
422 // call ToOct with appropriate information
423 public static string Oct (short Number) {
424 return ToOct(Number, 6, SizeIndexes.Int16);
426 public static string Oct (byte Number) {
427 return ToOct(Number, 3, SizeIndexes.Int16);
429 public static string Oct (int Number) {
430 return ToOct(Number, 11, SizeIndexes.Int32);
432 public static string Oct (long Number) {
433 return ToOct(Number, 22, SizeIndexes.Int64);
436 // Objects are always trickier
437 // first need to cast to appropriate type
438 public static string Oct (System.Object Number) {
439 // first, always throw an exception if Number is null
440 if (Number == null) {
441 throw new ArgumentNullException("Number",
442 "Value cannot be null");
445 TypeCode TC = Type.GetTypeCode (Number.GetType ());
448 // try to parse a string as an Int32
450 case TypeCode.String:
454 CastToString (Number)));
459 CastToString (Number)));
462 // integer types just call the appropriate "Oct"
464 return Oct ((byte)Number);
466 return Oct ((short)Number);
468 return Oct ((int)Number);
470 return Oct ((long)Number);
471 case TypeCode.Single:
472 float fval = (float)Number;
473 lval = (long) Math.Round(fval);
474 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
475 return Oct((int)lval);
478 case TypeCode.Double:
479 double dval = (double)Number;
480 if (dval > Int64.MaxValue || dval < Int64.MinValue)
481 throw new OverflowException(
482 VBUtils.GetResourceString("Overflow_Int64"));
484 lval = (long) Math.Round(dval);
485 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
486 return Oct((int)lval);
489 case TypeCode.Decimal:
490 Decimal big = new Decimal(Int64.MaxValue);
491 Decimal small = new Decimal(Int64.MinValue);
492 Decimal current = (Decimal) Number;
494 if ((current.CompareTo(big) > 0) ||
495 (current.CompareTo(small) < 0))
496 throw new OverflowException(
497 VBUtils.GetResourceString("Overflow_Int64"));
499 lval = Decimal.ToInt64(current);
500 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
501 return Oct((int)lval);
504 // Empty is defined as returning 0
508 // We can't convert these to Octal
509 case TypeCode.Boolean:
511 case TypeCode.DBNull:
512 case TypeCode.DateTime:
513 case TypeCode.Object:
515 case TypeCode.UInt16:
516 case TypeCode.UInt32:
517 case TypeCode.UInt64:
519 throw new ArgumentException (
520 "Type of argument 'Number' is '" +
521 Number.GetType().FullName +
522 "', which is not numeric.");
526 // Str is pretty easy now that we have a language
527 // with a ToString method()
528 public static string Str (System.Object Number) {
530 // check for null as always and throw an exception
531 if (Number == null) {
532 throw new ArgumentNullException("Number");
535 switch (Type.GetTypeCode (Number.GetType ())) {
536 // for unsigned types, just call ToString
538 case TypeCode.UInt16:
539 case TypeCode.UInt32:
540 case TypeCode.UInt64:
541 return Number.ToString();
543 // for signed types, we have to leave a
544 // space for the missing + sign
545 case TypeCode.Decimal:
546 return ((decimal)Number > 0 ? " " : "")
548 case TypeCode.Double:
549 return ((double)Number > 0 ? " " : "")
552 return ((short)Number > 0 ? " " : "")
555 return ((int)Number > 0 ? " " : "")
558 return ((long)Number > 0 ? " " : "")
561 return ((sbyte)Number > 0 ? " " : "")
563 case TypeCode.Single:
564 return ((float)Number > 0 ? " " : "")
567 // can't cast anything else to a Number
569 throw new InvalidCastException(
570 "Argument 'Number' cannot be converted to a numeric value.");
574 // The Val function is pretty bizarre
575 // Val ("&HFF") = 255
576 // Val ("&O377") = 255
577 // Val ("1234 Any Street") = 1234
578 // Val (" 12 45 . 90 7 E + 0 0 2 ") = 1245.907e+002 = 124590.7
579 public static double Val (string InputStr) {
584 int Length = InputStr.Length;
585 char[] Number = new char[Length];
586 bool FoundRadixPrefix = false;
594 for (i = 0; i < Length; i++) {
597 // look for Radix prefix "&"
598 if (i == 0 && c == '&') {
599 FoundRadixPrefix = true;
602 // look for an H or O following the prefix
603 else if (FoundRadixPrefix && i == 1 &&
604 (char.ToLower(c) == 'h' ||
605 char.ToLower(c) == 'o')) {
614 // if we didn't find a radix prefix,
616 else if (char.IsWhiteSpace(c) && (Base == 10)) {
620 // mash what's left together
622 Number[NumChars++] = c;
626 // now we have a string to parse
628 // FIXME : for Octal and Hex,
629 // Regex is probably overkill
630 // Even for base 10, it might be faster
631 // to write our own parser
633 NumberReg = new Regex ("^[0-7]*");
634 NumberMatch = NumberReg.Match (
635 new string(Number, 0, NumChars));
638 NumberReg = new Regex ("^[0-9a-f]*",
639 RegexOptions.IgnoreCase);
640 NumberMatch = NumberReg.Match (
641 new string(Number, 0, NumChars));
645 NumberReg = new Regex (
646 "^[+-]?\\d*\\.?\\d*(e[+-]?\\d*)?",
647 RegexOptions.IgnoreCase);
648 NumberMatch = NumberReg.Match (
649 new string(Number, 0, NumChars));
655 // we found a match, try to convert it
656 if (NumberMatch.Success) {
658 if(NumberMatch.Length == 0)
677 throw new OverflowException();
685 // Val on a char type is pretty simple '9' = 9, 'a' = exception
686 public static int Val (char Expression) {
687 if (char.IsDigit(Expression)) {
688 return Expression - '0';
691 throw new ArgumentException();
695 // if it's an object, and we can't convert
696 // it to a string, it's an exception
697 public static double Val (System.Object Expression) {
698 // always check for null first
699 if (Expression == null) {
700 throw new ArgumentNullException ("Expression",
701 "Value cannot be null");
705 return Val (CastToString (Expression));
709 throw new ArgumentException(
710 "Type of argument 'Expression' is '" +
711 Expression.GetType().FullName +
712 "', which can nt be converted to numeric.");