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.");