Index: dam engine/branches/Initial Source/Deltares.DamEngine.Data/Standard/ObjectExtensions.cs =================================================================== diff -u -r303 -r330 --- dam engine/branches/Initial Source/Deltares.DamEngine.Data/Standard/ObjectExtensions.cs (.../ObjectExtensions.cs) (revision 303) +++ dam engine/branches/Initial Source/Deltares.DamEngine.Data/Standard/ObjectExtensions.cs (.../ObjectExtensions.cs) (revision 330) @@ -22,6 +22,8 @@ using System; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; using System.Linq; using System.Reflection; @@ -32,6 +34,7 @@ /// public static class ObjectExtensions { + public static readonly CultureInfo DefaultCulture = new CultureInfo("nl-NL"); private const double threshold = 0.0000001; /// @@ -67,6 +70,167 @@ return (Math.Abs(double1 - double2) <= precision); } + /// + /// Converts an object to another type. Also works with nullable types. The method can't. + /// + /// + /// Also works with nullable types and enums. The method can't do this. + /// + /// The type to converto to + /// The object to convert + /// The converted object + /// When is null and name="type"/> is value type and not nullable. + /// When an error occurs during conversion. + public static T ToType(this object value) + { + if (value == null) + { + ThrowIfValueType(); + + return default(T); + } + + return (T)value.ToType(typeof(T), DefaultCulture); + } + + /// + /// Converts an object to another type. + /// + /// + /// Also works with nullable types and enums. The method can't do this. + /// This method can also be used to downcast numeric values for example from string (double) value "12,0" to short 12 + /// When trying to convert a string to double and the dutch culture is set, then this method tries to + /// parse the string with custom logic. Because it occures that some string are not properly + /// changed according to the current culture. For instance in dbf files where numeric values are stored as + /// strings (for example "10,23") + /// Use this method with care when high performance is required it is not yet tested with large data sets + /// + /// The value to convert + /// The target type to convert to + /// + /// The converted type + /// When is null and is value type and not nullable. + /// When an error occurs during conversion. + public static object ToType(this object value, Type type, CultureInfo culture) + { + if (value == null) + { + ThrowIfValueType(type); + + return null; + } + + if (type.IsEnum) + { + return value.ToEnumType(type); + } + + object result = null; + + if (IsNullable(type)) + { + try + { + var nullableConverter = new NullableConverter(type); + type = nullableConverter.UnderlyingType; + result = Convert.ChangeType(value, type, CultureInfo.InvariantCulture); + } + catch + { + result = null; + } + } + else + { + try + { + if (type == typeof(double) && value is string) + { + if (Equals(culture, new CultureInfo("nl-NL"))) + { + // TODO: Needs to be tested !! + // TODO: replace parsing with regular expression + // TODO: to make this more flexible make parser injectable through lambda's + + var tmp = value as string; + if (tmp.Contains(",") && !tmp.Contains(".")) // 1,5 -> 1.5 + { + value = tmp.Replace(",", "."); + } + else if (tmp.Contains(",") && tmp.Contains(".") && tmp.Length > 4 && !tmp.Contains(" ")) + { + // 100,000.00 -> 100000.00 + value = tmp.Replace(",", ""); + } + + //TODO: how to handle 100,000 english notation? Seems not to occur in most/mabye all situations + } + result = value.Equals("NaN") ? Double.NaN : Convert.ChangeType(value, type, CultureInfo.InvariantCulture); + } + else + { + // value needs to be downcasted + + switch (type.Name) + { + case "SByte": + case "Byte": + case "Int16": + case "UInt16": + case "Int32": + case "UInt32": + case "Int64": + case "UInt64": + var tmp = Convert.ChangeType(value, typeof(double), CultureInfo.InvariantCulture); + result = Convert.ChangeType(tmp, type); + break; + default: + result = Convert.ChangeType(value, type, CultureInfo.InvariantCulture); + break; + } + } + } + catch (Exception e) + { + ThrowConversionException(value, type, e); + } + } + + return result; + } + + /// + /// Convert the value to the enum type + /// + /// The value to convert. + /// The type to convert to. + /// Ignores the case if set to true + /// The enum + public static object ToEnumType(this object value, Type type, bool ignoreCase = false) + { + if (value == null) + { + throw new ArgumentNullException("value"); + } + + if (!ignoreCase && !Enum.IsDefined(type, value)) + { + ThrowConversionException(value, type, null); + } + + string stringVal = value.ToString(); + object result = null; + try + { + result = Enum.Parse(type, stringVal, ignoreCase); + } + catch (Exception e) + { + ThrowConversionException(value, type, e); + } + return result; + } + /// /// Clone properties from an original object to a destination object. /// @@ -135,5 +299,56 @@ return typeof(IList).IsAssignableFrom(destinationProperty.PropertyType); } + /// + /// Throws an ArgumentNullException if target type to convert to is a ValueType. + /// + /// + private static void ThrowIfValueType() + { + ThrowIfValueType(typeof(T)); + } + + /// + /// Throws an ArgumentNullException if target type to convert to is a ValueType. + /// + /// The type. + private static void ThrowIfValueType(Type type) + { + if (type.IsValueType && !IsNullable(type)) + { + throw new ArgumentNullException( + string.Format("The value can't be null because parameter type {0} is a value type", type)); + } + } + + /// + /// Determines whether the specified type is nullable. + /// + /// The type. + /// + /// true if the specified type is nullable; otherwise, false. + /// + private static bool IsNullable(Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>); + } + + /// + /// Throws conversion exception. + /// + /// The value. + /// The type. + /// The exception. + private static void ThrowConversionException(object value, Type type, Exception e) + { + string v = ""; + try + { + v = value.ToString().Replace("\0", " "); + } + catch { } + + throw new ConversionException(type, v, e); + } } } \ No newline at end of file