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;
36 namespace Microsoft.VisualBasic {
37 [Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute]
38 sealed public class Conversion {
41 /// Collection : The BASIC Collection Object
48 //Nothing to do, nobody should see this constructor
52 private static readonly char[] _HexDigits = {
53 '0', '1', '2', '3', '4', '5', '6', '7',
54 '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
56 private static readonly char[] _OctDigits = {
57 '0', '1', '2', '3', '4', '5', '6', '7'
59 private static readonly long[] _Maxes = {
60 32767, 2147483647, 9223372036854775807
62 private enum SizeIndexes {
72 public static string ErrorToString () {
73 return Information.Err().Description;
76 public static string ErrorToString (System.Int32 ErrorNumber) {
77 if(ErrorNumber >= 65535)
78 throw new ArgumentException(VBUtils.GetResourceString("MaxErrNumber"));
80 return Information.Err().Description;
82 String errStr = VBUtils.GetResourceString(ErrorNumber);
85 errStr = VBUtils.GetResourceString(95);
90 // Return whether d is +/- Could do this with a macro,
91 // but this is cleaner
92 private static int Sign(double d) { return d > 0 ? 1 : -1;}
94 // try to cast an Object to a string...used in several places
95 private static string CastToString (System.Object Expression) {
97 return Expression.ToString();
100 throw new InvalidCastException();
104 // Fix on Integer types doesn't do anything
105 public static short Fix (short Number) { return Number; }
106 public static int Fix (int Number) { return Number; }
107 public static long Fix (long Number) { return Number; }
109 // Fix on other numberic types = Sign(Number) * Int(Abs(Number))
110 public static double Fix (double Number) {
111 return Sign(Number) * Int (Math.Abs (Number));
113 public static float Fix (float Number) {
114 return Sign(Number) * Int (Math.Abs (Number));
116 public static decimal Fix (decimal Number) {
117 return Sign((double)Number) * Int (Math.Abs (Number));
120 // Fix on an Object type is trickier
121 // first we have to cast it to the right type
122 public static System.Object Fix (System.Object Number)
124 // always start out by throwing an exception
126 if (Number == null) {
127 throw new ArgumentNullException ("Number",
128 "Value cannot be null");
131 TypeCode TC = Type.GetTypeCode (Number.GetType ());
133 // switch on TypeCode and call appropriate Fix method
135 case TypeCode.Decimal:
136 return Fix (Convert.ToDecimal (Number));
137 case TypeCode.Double:
138 return Fix (Convert.ToDouble (Number));
139 case TypeCode.Single:
140 return Fix (Convert.ToSingle (Number));
141 case TypeCode.String:
142 return Fix (Double.Parse (
143 CastToString (Number)));
145 // for integer types, don't need to do anything
151 case TypeCode.UInt16:
152 case TypeCode.UInt32:
153 case TypeCode.UInt64:
156 // spec defines Empty as returning 0
160 // we can't convert these types
161 case TypeCode.Boolean:
163 case TypeCode.DateTime:
164 case TypeCode.DBNull:
165 case TypeCode.Object:
167 throw new ArgumentException (
168 "Type of argument 'Number' is '"
169 + Number.GetType().FullName +
170 "', which is not numeric.");
174 // Int on Integer types doesn't do anything
175 public static short Int (short Number) { return Number; }
176 public static int Int (int Number) { return Number; }
177 public static long Int (long Number) { return Number; }
179 // Int on other numberic types is same thing as "Floor"
180 public static double Int (double Number) {
181 return (double) Math.Floor(Number);
183 public static float Int (float Number) {
184 return (float) Math.Floor(Number);
186 public static decimal Int (decimal Number) {
187 return Decimal.Floor(Number);
190 // doing an Int on an Object is trickier
191 // first we have to cast to the correct type
192 public static System.Object Int (System.Object Number) {
193 // always start out by throwing an exception
195 if (Number == null) {
196 throw new ArgumentNullException("Number",
197 "Value cannot be null");
200 TypeCode TC = Type.GetTypeCode (Number.GetType ());
202 // switch on TypeCode and call appropriate Int method
204 case TypeCode.Decimal:
205 return Int (Convert.ToDecimal (Number));
206 case TypeCode.Double:
207 return Int (Convert.ToDouble (Number));
208 case TypeCode.Single:
209 return Int (Convert.ToSingle (Number));
210 case TypeCode.String:
211 return Int (Double.Parse (
212 CastToString(Number)));
214 // Int on integer types does nothing
220 case TypeCode.UInt16:
221 case TypeCode.UInt32:
222 case TypeCode.UInt64:
225 // Spec defines Empty as returning 0
229 // otherwise, it's we can't cast to a numeric
230 case TypeCode.Boolean:
232 case TypeCode.DateTime:
233 case TypeCode.DBNull:
234 case TypeCode.Object:
236 throw new ArgumentException (
237 "Type of argument 'Number' is '" +
238 Number.GetType().FullName +
239 "', which is not numeric.");
243 // we use this internally to get a string
244 // representation of a number in a specific base
245 private static string ToBase (ulong Number, int Length,
246 char[] BaseDigits, uint Base) {
249 // we use a char array here for performance
250 char [] c = new Char[Length];
254 for (i = Length - 1; i >= 0; i--) {
256 Number = Number / Base;
257 c[i] = BaseDigits[r];
259 s = new string (c, i, Length - i);
264 return new string (c);
272 // convert a number to Hex
273 // a little bit of magic goes on here with negative #'s
274 private static string ToHex(long Number, int Digits,
279 // we add maxint of the Number's type
280 // twice and then 2 more...this has the
281 // effect of turning it into a ulong
282 // that has the same hex representation
283 UNumber = (ulong)((Number + 2) +
285 (ulong)_Maxes[(int)Size];
288 UNumber = (ulong)Number;
290 return ToBase(UNumber, Digits, _HexDigits, 16);
293 // call our private function,
294 // passing it the size of the item to convert
295 public static string Hex (short Number) {
296 return ToHex(Number, 4, SizeIndexes.Int16);
298 public static string Hex (byte Number) {
299 return ToHex(Number, 2, SizeIndexes.Int16);
301 public static string Hex (int Number) {
302 return ToHex(Number, 8, SizeIndexes.Int32);
304 public static string Hex (long Number) {
305 return ToHex(Number, 16, SizeIndexes.Int64);
308 // Objects are trickier
309 // first we have to cast to appropriate type
310 public static System.String Hex (System.Object Number) {
311 // always start out by throwing an exception
314 if (Number == null) {
315 throw new ArgumentNullException ("Number",
316 "Value cannot be null");
319 TypeCode TC = Type.GetTypeCode (Number.GetType ());
322 // try to parse the string as an Int32,
323 // then an Int64, if that fails
324 case TypeCode.String:
328 CastToString (Number)));
333 CastToString (Number)));
336 // for the int types,
337 // just call the normal "Hex" for that type
339 return Hex ((byte)Number);
341 return Hex ((short)Number);
343 return Hex ((int)Number);
345 return Hex ((long)Number);
347 // empty is defined as returning 0
350 case TypeCode.Single:
351 float fval = (float)Number;
352 lval = (long) Math.Round(fval);
353 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
354 return Hex ((int)lval);
357 case TypeCode.Double:
358 double dval = (double)Number;
359 if (dval > Int64.MaxValue || dval < Int64.MinValue)
360 throw new OverflowException(
361 VBUtils.GetResourceString("Overflow_Int64"));
362 lval = (long) Math.Round(dval);
363 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
364 return Hex((int)lval);
368 case TypeCode.Decimal:
369 Decimal big = new Decimal(Int64.MaxValue);
370 Decimal small = new Decimal(Int64.MinValue);
371 Decimal current = (Decimal)Number;
373 if (current.CompareTo(big) > 0 ||
374 current.CompareTo(small) < 0)
375 throw new OverflowException(
376 VBUtils.GetResourceString("Overflow_Int64"));
378 lval = Decimal.ToInt64(current);
379 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
380 return Hex((int)lval);
383 // we can't do any of these types
384 case TypeCode.Boolean:
386 case TypeCode.DBNull:
387 case TypeCode.DateTime:
388 case TypeCode.Object:
390 case TypeCode.UInt16:
391 case TypeCode.UInt32:
392 case TypeCode.UInt64:
394 throw new ArgumentException (
395 "Type of argument 'Number' is '" +
396 Number.GetType().FullName +
397 "', which is not numeric.");
401 // ToOct works just like ToHex, only in Octal.
402 private static string ToOct(long Number, int Digits,
407 // for neg numbers add the maxint of
408 // the appropriate size twice, and then two more
409 // this has the effect of turning it
410 // into a ulong with the same oct representation
411 UNumber = (ulong)((Number + 2) +
413 (ulong)(_Maxes[(int)Size]);
416 UNumber = (ulong)Number;
418 return ToBase (UNumber, Digits, _OctDigits, 8);
421 // call ToOct with appropriate information
422 public static string Oct (short Number) {
423 return ToOct(Number, 6, SizeIndexes.Int16);
425 public static string Oct (byte Number) {
426 return ToOct(Number, 3, SizeIndexes.Int16);
428 public static string Oct (int Number) {
429 return ToOct(Number, 11, SizeIndexes.Int32);
431 public static string Oct (long Number) {
432 return ToOct(Number, 22, SizeIndexes.Int64);
435 // Objects are always trickier
436 // first need to cast to appropriate type
437 public static string Oct (System.Object Number) {
438 // first, always throw an exception if Number is null
439 if (Number == null) {
440 throw new ArgumentNullException("Number",
441 "Value cannot be null");
444 TypeCode TC = Type.GetTypeCode (Number.GetType ());
447 // try to parse a string as an Int32
449 case TypeCode.String:
453 CastToString (Number)));
458 CastToString (Number)));
461 // integer types just call the appropriate "Oct"
463 return Oct ((byte)Number);
465 return Oct ((short)Number);
467 return Oct ((int)Number);
469 return Oct ((long)Number);
470 case TypeCode.Single:
471 float fval = (float)Number;
472 lval = (long) Math.Round(fval);
473 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
474 return Oct((int)lval);
477 case TypeCode.Double:
478 double dval = (double)Number;
479 if (dval > Int64.MaxValue || dval < Int64.MinValue)
480 throw new OverflowException(
481 VBUtils.GetResourceString("Overflow_Int64"));
483 lval = (long) Math.Round(dval);
484 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
485 return Oct((int)lval);
488 case TypeCode.Decimal:
489 Decimal big = new Decimal(Int64.MaxValue);
490 Decimal small = new Decimal(Int64.MinValue);
491 Decimal current = (Decimal) Number;
493 if ((current.CompareTo(big) > 0) ||
494 (current.CompareTo(small) < 0))
495 throw new OverflowException(
496 VBUtils.GetResourceString("Overflow_Int64"));
498 lval = Decimal.ToInt64(current);
499 if ((lval > Int32.MinValue) && (lval < Int32.MaxValue))
500 return Oct((int)lval);
503 // Empty is defined as returning 0
507 // We can't convert these to Octal
508 case TypeCode.Boolean:
510 case TypeCode.DBNull:
511 case TypeCode.DateTime:
512 case TypeCode.Object:
514 case TypeCode.UInt16:
515 case TypeCode.UInt32:
516 case TypeCode.UInt64:
518 throw new ArgumentException (
519 "Type of argument 'Number' is '" +
520 Number.GetType().FullName +
521 "', which is not numeric.");
525 // Str is pretty easy now that we have a language
526 // with a ToString method()
527 public static string Str (System.Object Number) {
529 // check for null as always and throw an exception
530 if (Number == null) {
531 throw new ArgumentNullException("Number");
534 switch (Type.GetTypeCode (Number.GetType ())) {
535 // for unsigned types, just call ToString
537 case TypeCode.UInt16:
538 case TypeCode.UInt32:
539 case TypeCode.UInt64:
540 return Number.ToString();
542 // for signed types, we have to leave a
543 // space for the missing + sign
544 case TypeCode.Decimal:
545 return ((decimal)Number > 0 ? " " : "")
547 case TypeCode.Double:
548 return ((double)Number > 0 ? " " : "")
551 return ((short)Number > 0 ? " " : "")
554 return ((int)Number > 0 ? " " : "")
557 return ((long)Number > 0 ? " " : "")
560 return ((sbyte)Number > 0 ? " " : "")
562 case TypeCode.Single:
563 return ((float)Number > 0 ? " " : "")
566 // can't cast anything else to a Number
568 throw new InvalidCastException(
569 "Argument 'Number' cannot be converted to a numeric value.");
573 // The Val function is pretty bizarre
574 // Val ("&HFF") = 255
575 // Val ("&O377") = 255
576 // Val ("1234 Any Street") = 1234
577 // Val (" 12 45 . 90 7 E + 0 0 2 ") = 1245.907e+002 = 124590.7
578 public static double Val (string InputStr) {
583 int Length = InputStr.Length;
584 char[] Number = new char[Length];
585 bool FoundRadixPrefix = false;
593 for (i = 0; i < Length; i++) {
596 // look for Radix prefix "&"
597 if (i == 0 && c == '&') {
598 FoundRadixPrefix = true;
601 // look for an H or O following the prefix
602 else if (FoundRadixPrefix && i == 1 &&
603 (char.ToLower(c) == 'h' ||
604 char.ToLower(c) == 'o')) {
613 // if we didn't find a radix prefix,
615 else if (char.IsWhiteSpace(c) && (Base == 10)) {
619 // mash what's left together
621 Number[NumChars++] = c;
625 // now we have a string to parse
627 // FIXME : for Octal and Hex,
628 // Regex is probably overkill
629 // Even for base 10, it might be faster
630 // to write our own parser
632 NumberReg = new Regex ("^[0-7]*");
633 NumberMatch = NumberReg.Match (
634 new string(Number, 0, NumChars));
637 NumberReg = new Regex ("^[0-9a-f]*",
638 RegexOptions.IgnoreCase);
639 NumberMatch = NumberReg.Match (
640 new string(Number, 0, NumChars));
644 NumberReg = new Regex (
645 "^[+-]?\\d*\\.?\\d*(e[+-]?\\d*)?",
646 RegexOptions.IgnoreCase);
647 NumberMatch = NumberReg.Match (
648 new string(Number, 0, NumChars));
654 // we found a match, try to convert it
655 if (NumberMatch.Success) {
657 if(NumberMatch.Length == 0)
676 throw new OverflowException();
684 // Val on a char type is pretty simple '9' = 9, 'a' = exception
685 public static int Val (char Expression) {
686 if (char.IsDigit(Expression)) {
687 return Expression - '0';
690 throw new ArgumentException();
694 // if it's an object, and we can't convert
695 // it to a string, it's an exception
696 public static double Val (System.Object Expression) {
697 // always check for null first
698 if (Expression == null) {
699 throw new ArgumentNullException ("Expression",
700 "Value cannot be null");
704 return Val (CastToString (Expression));
708 throw new ArgumentException(
709 "Type of argument 'Expression' is '" +
710 Expression.GetType().FullName +
711 "', which can nt be converted to numeric.");