|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.Collections;
using System.ComponentModel.Design.Serialization;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
namespace System.ComponentModel
{
/// <summary>
/// TypeConverter to convert Nullable types to and from strings or the underlying simple type.
/// </summary>
public class NullableConverter : TypeConverter
{
private static readonly ConstructorInfo s_nullableConstructor = typeof(Nullable<>).GetConstructor(typeof(Nullable<>).GetGenericArguments())!;
/// <summary>
/// Nullable converter is initialized with the underlying simple type.
/// </summary>
[RequiresUnreferencedCode("The UnderlyingType cannot be statically discovered.")]
public NullableConverter(Type type)
{
NullableType = type;
UnderlyingType = Nullable.GetUnderlyingType(type)!;
if (UnderlyingType == null)
{
throw new ArgumentException(SR.NullableConverterBadCtorArg, nameof(type));
}
UnderlyingTypeConverter = TypeDescriptor.GetConverter(UnderlyingType);
}
/// <summary>
/// Gets a value indicating whether this converter can convert an object in the
/// given source type to the underlying simple type or a null.
/// </summary>
public override bool CanConvertFrom(ITypeDescriptorContext? context, Type sourceType)
{
if (sourceType == UnderlyingType)
{
return true;
}
else if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.CanConvertFrom(context, sourceType);
}
return base.CanConvertFrom(context, sourceType);
}
/// <summary>
/// Converts the given value to the converter's underlying simple type or a null.
/// </summary>
public override object? ConvertFrom(ITypeDescriptorContext? context, CultureInfo? culture, object? value)
{
if (value == null || value.GetType() == UnderlyingType)
{
return value;
}
else if (value is string && string.IsNullOrEmpty(value as string))
{
return null;
}
else if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.ConvertFrom(context, culture, value);
}
return base.ConvertFrom(context, culture, value);
}
/// <summary>
/// Gets a value indicating whether this converter can convert a value object to the destination type.
/// </summary>
public override bool CanConvertTo(ITypeDescriptorContext? context, [NotNullWhen(true)] Type? destinationType)
{
if (destinationType == UnderlyingType)
{
return true;
}
else if (destinationType == typeof(InstanceDescriptor))
{
return true;
}
else if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.CanConvertTo(context, destinationType);
}
return base.CanConvertTo(context, destinationType);
}
/// <summary>
/// Converts the given value object to the destination type.
/// </summary>
public override object? ConvertTo(ITypeDescriptorContext? context, CultureInfo? culture, object? value, Type destinationType)
{
ArgumentNullException.ThrowIfNull(destinationType);
if (destinationType == UnderlyingType && value != null && NullableType.IsInstanceOfType(value))
{
return value;
}
else if (destinationType == typeof(InstanceDescriptor))
{
ConstructorInfo ci = (ConstructorInfo)NullableType.GetMemberWithSameMetadataDefinitionAs(s_nullableConstructor);
Debug.Assert(ci != null, "Couldn't find constructor");
return new InstanceDescriptor(ci, new object?[] { value }, true);
}
else if (value == null)
{
// Handle our own nulls here
if (destinationType == typeof(string))
{
return string.Empty;
}
}
else if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.ConvertTo(context, culture, value, destinationType);
}
return base.ConvertTo(context, culture, value, destinationType);
}
/// <summary>
/// </summary>
public override object? CreateInstance(ITypeDescriptorContext? context, IDictionary propertyValues)
{
if (UnderlyingTypeConverter != null)
{
object? instance = UnderlyingTypeConverter.CreateInstance(context, propertyValues);
return instance;
}
return base.CreateInstance(context, propertyValues);
}
/// <summary>
/// Gets a value indicating whether changing a value on this object requires a call to
/// <see cref='System.ComponentModel.TypeConverter.CreateInstance(IDictionary)'/> to create a new value,
/// using the specified context.
/// </summary>
public override bool GetCreateInstanceSupported(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.GetCreateInstanceSupported(context);
}
return base.GetCreateInstanceSupported(context);
}
/// <summary>
/// Gets a collection of properties for the type of array specified by the value
/// parameter using the specified context and attributes.
/// </summary>
[RequiresUnreferencedCode("The Type of value cannot be statically discovered. " + AttributeCollection.FilterRequiresUnreferencedCodeMessage)]
public override PropertyDescriptorCollection? GetProperties(ITypeDescriptorContext? context, object value, Attribute[]? attributes)
{
if (UnderlyingTypeConverter != null)
{
object unwrappedValue = value;
return UnderlyingTypeConverter.GetProperties(context, unwrappedValue, attributes);
}
return base.GetProperties(context, value, attributes);
}
/// <summary>
/// Gets a value indicating whether this object supports properties using the specified context.
/// </summary>
public override bool GetPropertiesSupported(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.GetPropertiesSupported(context);
}
return base.GetPropertiesSupported(context);
}
/// <summary>
/// Gets a collection of standard values for the data type this type converter is designed for.
/// </summary>
public override StandardValuesCollection? GetStandardValues(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
StandardValuesCollection? values = UnderlyingTypeConverter.GetStandardValues(context);
if (GetStandardValuesSupported(context) && values != null)
{
// Create a set of standard values around nullable instances.
object?[] wrappedValues = new object[values.Count + 1];
int idx = 0;
wrappedValues[idx++] = null;
foreach (object value in values)
{
wrappedValues[idx++] = value;
}
return new StandardValuesCollection(wrappedValues);
}
}
return base.GetStandardValues(context);
}
/// <summary>
/// Gets a value indicating whether the collection of standard values returned from
/// <see cref='System.ComponentModel.TypeConverter.GetStandardValues()'/> is an exclusive
/// list of possible values, using the specified context.
/// </summary>
public override bool GetStandardValuesExclusive(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.GetStandardValuesExclusive(context);
}
return base.GetStandardValuesExclusive(context);
}
/// <summary>
/// Gets a value indicating whether this object supports a standard set of values that can
/// be picked from a list using the specified context.
/// </summary>
public override bool GetStandardValuesSupported(ITypeDescriptorContext? context)
{
if (UnderlyingTypeConverter != null)
{
return UnderlyingTypeConverter.GetStandardValuesSupported(context);
}
return base.GetStandardValuesSupported(context);
}
/// <summary>
/// Gets a value indicating whether the given value object is valid for this type.
/// </summary>
public override bool IsValid(ITypeDescriptorContext? context, object value)
{
if (UnderlyingTypeConverter != null)
{
object? unwrappedValue = value;
if (unwrappedValue == null)
{
return true; // null is valid for nullable.
}
else
{
return UnderlyingTypeConverter.IsValid(context, unwrappedValue);
}
}
return base.IsValid(context, value);
}
/// <summary>
/// The type this converter was initialized with.
/// </summary>
public Type NullableType { get; }
/// <summary>
/// The simple type that is represented as a nullable.
/// </summary>
public Type UnderlyingType { get; }
/// <summary>
/// Converter associated with the underlying simple type.
/// </summary>
public TypeConverter UnderlyingTypeConverter { get; }
}
}
|