ConvertUtils
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data.SqlTypes;
using System.Globalization;
using System.Numerics;
using System.Reflection;
namespace Newtonsoft.Json.Utilities
{
internal static class ConvertUtils
{
internal struct TypeConvertKey : IEquatable<TypeConvertKey>
{
private readonly Type _initialType;
private readonly Type _targetType;
public Type InitialType => _initialType;
public Type TargetType => _targetType;
public TypeConvertKey(Type initialType, Type targetType)
{
_initialType = initialType;
_targetType = targetType;
}
public override int GetHashCode()
{
return _initialType.GetHashCode() ^ _targetType.GetHashCode();
}
public override bool Equals(object obj)
{
if (!(obj is TypeConvertKey))
return false;
return Equals((TypeConvertKey)obj);
}
public bool Equals(TypeConvertKey other)
{
if (_initialType == other._initialType)
return _targetType == other._targetType;
return false;
}
}
private static readonly Dictionary<Type, PrimitiveTypeCode> TypeCodeMap = new Dictionary<Type, PrimitiveTypeCode> {
{
typeof(char),
PrimitiveTypeCode.Char
},
{
typeof(char?),
PrimitiveTypeCode.CharNullable
},
{
typeof(bool),
PrimitiveTypeCode.Boolean
},
{
typeof(bool?),
PrimitiveTypeCode.BooleanNullable
},
{
typeof(sbyte),
PrimitiveTypeCode.SByte
},
{
typeof(sbyte?),
PrimitiveTypeCode.SByteNullable
},
{
typeof(short),
PrimitiveTypeCode.Int16
},
{
typeof(short?),
PrimitiveTypeCode.Int16Nullable
},
{
typeof(ushort),
PrimitiveTypeCode.UInt16
},
{
typeof(ushort?),
PrimitiveTypeCode.UInt16Nullable
},
{
typeof(int),
PrimitiveTypeCode.Int32
},
{
typeof(int?),
PrimitiveTypeCode.Int32Nullable
},
{
typeof(byte),
PrimitiveTypeCode.Byte
},
{
typeof(byte?),
PrimitiveTypeCode.ByteNullable
},
{
typeof(uint),
PrimitiveTypeCode.UInt32
},
{
typeof(uint?),
PrimitiveTypeCode.UInt32Nullable
},
{
typeof(long),
PrimitiveTypeCode.Int64
},
{
typeof(long?),
PrimitiveTypeCode.Int64Nullable
},
{
typeof(ulong),
PrimitiveTypeCode.UInt64
},
{
typeof(ulong?),
PrimitiveTypeCode.UInt64Nullable
},
{
typeof(float),
PrimitiveTypeCode.Single
},
{
typeof(float?),
PrimitiveTypeCode.SingleNullable
},
{
typeof(double),
PrimitiveTypeCode.Double
},
{
typeof(double?),
PrimitiveTypeCode.DoubleNullable
},
{
typeof(DateTime),
PrimitiveTypeCode.DateTime
},
{
typeof(DateTime?),
PrimitiveTypeCode.DateTimeNullable
},
{
typeof(DateTimeOffset),
PrimitiveTypeCode.DateTimeOffset
},
{
typeof(DateTimeOffset?),
PrimitiveTypeCode.DateTimeOffsetNullable
},
{
typeof(decimal),
PrimitiveTypeCode.Decimal
},
{
typeof(decimal?),
PrimitiveTypeCode.DecimalNullable
},
{
typeof(Guid),
PrimitiveTypeCode.Guid
},
{
typeof(Guid?),
PrimitiveTypeCode.GuidNullable
},
{
typeof(TimeSpan),
PrimitiveTypeCode.TimeSpan
},
{
typeof(TimeSpan?),
PrimitiveTypeCode.TimeSpanNullable
},
{
typeof(BigInteger),
PrimitiveTypeCode.BigInteger
},
{
typeof(BigInteger?),
PrimitiveTypeCode.BigIntegerNullable
},
{
typeof(Uri),
PrimitiveTypeCode.Uri
},
{
typeof(string),
PrimitiveTypeCode.String
},
{
typeof(byte[]),
PrimitiveTypeCode.Bytes
},
{
typeof(DBNull),
PrimitiveTypeCode.DBNull
}
};
private static readonly List<TypeInformation> PrimitiveTypeCodes = new List<TypeInformation> {
new TypeInformation {
Type = typeof(object),
TypeCode = PrimitiveTypeCode.Empty
},
new TypeInformation {
Type = typeof(object),
TypeCode = PrimitiveTypeCode.Object
},
new TypeInformation {
Type = typeof(object),
TypeCode = PrimitiveTypeCode.DBNull
},
new TypeInformation {
Type = typeof(bool),
TypeCode = PrimitiveTypeCode.Boolean
},
new TypeInformation {
Type = typeof(char),
TypeCode = PrimitiveTypeCode.Char
},
new TypeInformation {
Type = typeof(sbyte),
TypeCode = PrimitiveTypeCode.SByte
},
new TypeInformation {
Type = typeof(byte),
TypeCode = PrimitiveTypeCode.Byte
},
new TypeInformation {
Type = typeof(short),
TypeCode = PrimitiveTypeCode.Int16
},
new TypeInformation {
Type = typeof(ushort),
TypeCode = PrimitiveTypeCode.UInt16
},
new TypeInformation {
Type = typeof(int),
TypeCode = PrimitiveTypeCode.Int32
},
new TypeInformation {
Type = typeof(uint),
TypeCode = PrimitiveTypeCode.UInt32
},
new TypeInformation {
Type = typeof(long),
TypeCode = PrimitiveTypeCode.Int64
},
new TypeInformation {
Type = typeof(ulong),
TypeCode = PrimitiveTypeCode.UInt64
},
new TypeInformation {
Type = typeof(float),
TypeCode = PrimitiveTypeCode.Single
},
new TypeInformation {
Type = typeof(double),
TypeCode = PrimitiveTypeCode.Double
},
new TypeInformation {
Type = typeof(decimal),
TypeCode = PrimitiveTypeCode.Decimal
},
new TypeInformation {
Type = typeof(DateTime),
TypeCode = PrimitiveTypeCode.DateTime
},
new TypeInformation {
Type = typeof(object),
TypeCode = PrimitiveTypeCode.Empty
},
new TypeInformation {
Type = typeof(string),
TypeCode = PrimitiveTypeCode.String
}
};
private static readonly ThreadSafeStore<TypeConvertKey, Func<object, object>> CastConverters = new ThreadSafeStore<TypeConvertKey, Func<object, object>>(CreateCastConverter);
public static PrimitiveTypeCode GetTypeCode(Type t)
{
if (TypeCodeMap.TryGetValue(t, out PrimitiveTypeCode value))
return value;
if (t.IsEnum())
return GetTypeCode(Enum.GetUnderlyingType(t));
if (ReflectionUtils.IsNullableType(t)) {
Type underlyingType = Nullable.GetUnderlyingType(t);
if (underlyingType.IsEnum()) {
Type t2 = typeof(Nullable<>).MakeGenericType(Enum.GetUnderlyingType(underlyingType));
return GetTypeCode(t2);
}
}
return PrimitiveTypeCode.Object;
}
public static PrimitiveTypeCode GetTypeCode(object o)
{
return GetTypeCode(o.GetType());
}
public static TypeInformation GetTypeInformation(IConvertible convertable)
{
return PrimitiveTypeCodes[(int)convertable.GetTypeCode()];
}
public static bool IsConvertible(Type t)
{
return typeof(IConvertible).IsAssignableFrom(t);
}
public static TimeSpan ParseTimeSpan(string input)
{
return TimeSpan.Parse(input, CultureInfo.InvariantCulture);
}
private static Func<object, object> CreateCastConverter(TypeConvertKey t)
{
MethodInfo method = t.TargetType.GetMethod("op_Implicit", new Type[1] {
t.InitialType
});
if (method == (MethodInfo)null)
method = t.TargetType.GetMethod("op_Explicit", new Type[1] {
t.InitialType
});
if (method == (MethodInfo)null)
return null;
MethodCall<object, object> call = JsonTypeReflector.ReflectionDelegateFactory.CreateMethodCall<object>(method);
return (object o) => call(null, o);
}
internal static BigInteger ToBigInteger(object value)
{
if (value is BigInteger)
return (BigInteger)value;
if (value is string)
return BigInteger.Parse((string)value, CultureInfo.InvariantCulture);
if (value is float)
return new BigInteger((float)value);
if (value is double)
return new BigInteger((double)value);
if (value is decimal)
return new BigInteger((decimal)value);
if (value is int)
return new BigInteger((int)value);
if (value is long)
return new BigInteger((long)value);
if (value is uint)
return new BigInteger((uint)value);
if (value is ulong)
return new BigInteger((ulong)value);
if (value is byte[])
return new BigInteger((byte[])value);
throw new InvalidCastException("Cannot convert {0} to BigInteger.".FormatWith(CultureInfo.InvariantCulture, value.GetType()));
}
public static object FromBigInteger(BigInteger i, Type targetType)
{
if (targetType == typeof(decimal))
return (decimal)i;
if (targetType == typeof(double))
return (double)i;
if (targetType == typeof(float))
return (float)i;
if (!(targetType == typeof(ulong)))
try {
return System.Convert.ChangeType((long)i, targetType, CultureInfo.InvariantCulture);
} catch (Exception innerException) {
throw new InvalidOperationException("Can not convert from BigInteger to {0}.".FormatWith(CultureInfo.InvariantCulture, targetType), innerException);
}
return (ulong)i;
}
public static object Convert(object initialValue, CultureInfo culture, Type targetType)
{
if (initialValue == null)
throw new ArgumentNullException("initialValue");
if (ReflectionUtils.IsNullableType(targetType))
targetType = Nullable.GetUnderlyingType(targetType);
Type type = initialValue.GetType();
if (targetType == type)
return initialValue;
if (IsConvertible(initialValue.GetType()) && IsConvertible(targetType)) {
if (targetType.IsEnum()) {
if (initialValue is string)
return Enum.Parse(targetType, initialValue.ToString(), true);
if (IsInteger(initialValue))
return Enum.ToObject(targetType, initialValue);
}
return System.Convert.ChangeType(initialValue, targetType, culture);
}
if (initialValue is DateTime && targetType == typeof(DateTimeOffset))
return new DateTimeOffset((DateTime)initialValue);
if (initialValue is byte[] && targetType == typeof(Guid))
return new Guid((byte[])initialValue);
if (initialValue is string) {
if (targetType == typeof(Guid))
return new Guid((string)initialValue);
if (targetType == typeof(Uri))
return new Uri((string)initialValue, UriKind.RelativeOrAbsolute);
if (targetType == typeof(TimeSpan))
return ParseTimeSpan((string)initialValue);
if (targetType == typeof(byte[]))
return System.Convert.FromBase64String((string)initialValue);
if (typeof(Type).IsAssignableFrom(targetType))
return Type.GetType((string)initialValue, true);
}
if (targetType == typeof(BigInteger))
return ToBigInteger(initialValue);
if (initialValue is BigInteger)
return FromBigInteger((BigInteger)initialValue, targetType);
TypeConverter converter = GetConverter(type);
if (converter != null && converter.CanConvertTo(targetType))
return converter.ConvertTo(null, culture, initialValue, targetType);
TypeConverter converter2 = GetConverter(targetType);
if (converter2 != null && converter2.CanConvertFrom(type))
return converter2.ConvertFrom(null, culture, initialValue);
if (initialValue == DBNull.Value) {
if (ReflectionUtils.IsNullable(targetType))
return EnsureTypeAssignable(null, type, targetType);
throw new Exception("Can not convert null {0} into non-nullable {1}.".FormatWith(CultureInfo.InvariantCulture, type, targetType));
}
if (initialValue is INullable)
return EnsureTypeAssignable(ToValue((INullable)initialValue), type, targetType);
if (targetType.IsInterface() || targetType.IsGenericTypeDefinition() || targetType.IsAbstract())
throw new ArgumentException("Target type {0} is not a value type or a non-abstract class.".FormatWith(CultureInfo.InvariantCulture, targetType), "targetType");
throw new InvalidOperationException("Can not convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, type, targetType));
}
public static bool TryConvert(object initialValue, CultureInfo culture, Type targetType, out object convertedValue)
{
try {
convertedValue = Convert(initialValue, culture, targetType);
return true;
} catch {
convertedValue = null;
return false;
}
}
public static object ConvertOrCast(object initialValue, CultureInfo culture, Type targetType)
{
if (targetType == typeof(object))
return initialValue;
if (initialValue == null && ReflectionUtils.IsNullable(targetType))
return null;
if (TryConvert(initialValue, culture, targetType, out object convertedValue))
return convertedValue;
return EnsureTypeAssignable(initialValue, ReflectionUtils.GetObjectType(initialValue), targetType);
}
private static object EnsureTypeAssignable(object value, Type initialType, Type targetType)
{
Type type = value?.GetType();
if (value != null) {
if (targetType.IsAssignableFrom(type))
return value;
Func<object, object> func = CastConverters.Get(new TypeConvertKey(type, targetType));
if (func != null)
return func(value);
} else if (ReflectionUtils.IsNullable(targetType)) {
return null;
}
throw new ArgumentException("Could not cast or convert from {0} to {1}.".FormatWith(CultureInfo.InvariantCulture, (initialType != (Type)null) ? initialType.ToString() : "{null}", targetType));
}
public static object ToValue(INullable nullableValue)
{
if (nullableValue == null)
return null;
if (nullableValue is SqlInt32)
return ToValue((SqlInt32)nullableValue);
if (nullableValue is SqlInt64)
return ToValue((SqlInt64)nullableValue);
if (nullableValue is SqlBoolean)
return ToValue((SqlBoolean)nullableValue);
if (nullableValue is SqlString)
return ToValue((SqlString)nullableValue);
if (nullableValue is SqlDateTime)
return ToValue((SqlDateTime)nullableValue);
throw new ArgumentException("Unsupported INullable type: {0}".FormatWith(CultureInfo.InvariantCulture, nullableValue.GetType()));
}
internal static TypeConverter GetConverter(Type t)
{
return JsonTypeReflector.GetTypeConverter(t);
}
public static bool IsInteger(object value)
{
switch (GetTypeCode(value)) {
case PrimitiveTypeCode.SByte:
case PrimitiveTypeCode.Int16:
case PrimitiveTypeCode.UInt16:
case PrimitiveTypeCode.Int32:
case PrimitiveTypeCode.Byte:
case PrimitiveTypeCode.UInt32:
case PrimitiveTypeCode.Int64:
case PrimitiveTypeCode.UInt64:
return true;
default:
return false;
}
}
public static ParseResult Int32TryParse(char[] chars, int start, int length, out int value)
{
value = 0;
if (length == 0)
return ParseResult.Invalid;
bool flag = chars[start] == '-';
if (flag) {
if (length == 1)
return ParseResult.Invalid;
start++;
length--;
}
int num = start + length;
for (int i = start; i < num; i++) {
int num2 = chars[i] - 48;
if (num2 < 0 || num2 > 9)
return ParseResult.Invalid;
int num3 = 10 * value - num2;
if (num3 > value) {
for (i++; i < num; i++) {
num2 = chars[i] - 48;
if (num2 < 0 || num2 > 9)
return ParseResult.Invalid;
}
return ParseResult.Overflow;
}
value = num3;
}
if (!flag) {
if (value == -2147483648)
return ParseResult.Overflow;
value = -value;
}
return ParseResult.Success;
}
public static ParseResult Int64TryParse(char[] chars, int start, int length, out long value)
{
value = 0;
if (length == 0)
return ParseResult.Invalid;
bool flag = chars[start] == '-';
if (flag) {
if (length == 1)
return ParseResult.Invalid;
start++;
length--;
}
int num = start + length;
for (int i = start; i < num; i++) {
int num2 = chars[i] - 48;
if (num2 < 0 || num2 > 9)
return ParseResult.Invalid;
long num3 = 10 * value - num2;
if (num3 > value) {
for (i++; i < num; i++) {
num2 = chars[i] - 48;
if (num2 < 0 || num2 > 9)
return ParseResult.Invalid;
}
return ParseResult.Overflow;
}
value = num3;
}
if (!flag) {
if (value == -9223372036854775808)
return ParseResult.Overflow;
value = -value;
}
return ParseResult.Success;
}
}
}