SPRNET-970 - TypeConversionUtils should not throw TypeMismatchException on null value for System.Nullable
This commit is contained in:
@@ -1,180 +1,186 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright 2002-2004 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region Imports
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
|
||||
using Spring.Util;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Spring.Core.TypeConversion
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility methods that are used to convert objects from one type into another.
|
||||
/// </summary>
|
||||
/// <author>Aleksandar Seovic</author>
|
||||
public class TypeConversionUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert the value to the required <see cref="System.Type"/> (if necessary from a string).
|
||||
/// </summary>
|
||||
/// <param name="newValue">The proposed change value.</param>
|
||||
/// <param name="requiredType">
|
||||
/// The <see cref="System.Type"/> we must convert to.
|
||||
/// </param>
|
||||
/// <param name="propertyName">Property name, used for error reporting purposes...</param>
|
||||
/// <exception cref="Spring.Objects.ObjectsException">
|
||||
/// If there is an internal error.
|
||||
/// </exception>
|
||||
/// <returns>The new value, possibly the result of type conversion.</returns>
|
||||
public static object ConvertValueIfNecessary(Type requiredType, object newValue, string propertyName)
|
||||
{
|
||||
if (newValue != null)
|
||||
{
|
||||
// if it is assignable, return the value right away
|
||||
if (IsAssignableFrom(newValue, requiredType))
|
||||
{
|
||||
return newValue;
|
||||
}
|
||||
|
||||
// if required type is an array, convert all the elements
|
||||
if (requiredType != null && requiredType.IsArray)
|
||||
{
|
||||
// convert individual elements to array elements
|
||||
Type componentType = requiredType.GetElementType();
|
||||
if (newValue is ICollection)
|
||||
{
|
||||
ICollection elements = (ICollection) newValue;
|
||||
return ToArrayWithTypeConversion(componentType, elements, propertyName);
|
||||
}
|
||||
else if (newValue is string)
|
||||
{
|
||||
if (requiredType.Equals(typeof(char[])))
|
||||
{
|
||||
return ((string) newValue).ToCharArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] elements = StringUtils.CommaDelimitedListToStringArray((string) newValue);
|
||||
return ToArrayWithTypeConversion(componentType, elements, propertyName);
|
||||
}
|
||||
}
|
||||
else if (!newValue.GetType().IsArray)
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright 2002-2004 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region Imports
|
||||
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.ComponentModel;
|
||||
|
||||
using Spring.Util;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Spring.Core.TypeConversion
|
||||
{
|
||||
/// <summary>
|
||||
/// Utility methods that are used to convert objects from one type into another.
|
||||
/// </summary>
|
||||
/// <author>Aleksandar Seovic</author>
|
||||
public class TypeConversionUtils
|
||||
{
|
||||
/// <summary>
|
||||
/// Convert the value to the required <see cref="System.Type"/> (if necessary from a string).
|
||||
/// </summary>
|
||||
/// <param name="newValue">The proposed change value.</param>
|
||||
/// <param name="requiredType">
|
||||
/// The <see cref="System.Type"/> we must convert to.
|
||||
/// </param>
|
||||
/// <param name="propertyName">Property name, used for error reporting purposes...</param>
|
||||
/// <exception cref="Spring.Objects.ObjectsException">
|
||||
/// If there is an internal error.
|
||||
/// </exception>
|
||||
/// <returns>The new value, possibly the result of type conversion.</returns>
|
||||
public static object ConvertValueIfNecessary(Type requiredType, object newValue, string propertyName)
|
||||
{
|
||||
if (newValue != null)
|
||||
{
|
||||
// if it is assignable, return the value right away
|
||||
if (IsAssignableFrom(newValue, requiredType))
|
||||
{
|
||||
return newValue;
|
||||
}
|
||||
|
||||
// if required type is an array, convert all the elements
|
||||
if (requiredType != null && requiredType.IsArray)
|
||||
{
|
||||
// convert individual elements to array elements
|
||||
Type componentType = requiredType.GetElementType();
|
||||
if (newValue is ICollection)
|
||||
{
|
||||
// A plain value: convert it to an array with a single component.
|
||||
Array result = Array.CreateInstance(componentType, 1);
|
||||
object val = ConvertValueIfNecessary(componentType, newValue, propertyName);
|
||||
result.SetValue(val, 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// try to convert using type converter
|
||||
try
|
||||
{
|
||||
TypeConverter typeConverter = TypeConverterRegistry.GetConverter(requiredType);
|
||||
if (typeConverter != null && typeConverter.CanConvertFrom(newValue.GetType()))
|
||||
{
|
||||
try
|
||||
{
|
||||
newValue = typeConverter.ConvertFrom(newValue);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (newValue is string)
|
||||
{
|
||||
newValue = typeConverter.ConvertFromInvariantString((string)newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
typeConverter = TypeConverterRegistry.GetConverter(newValue.GetType());
|
||||
if (typeConverter != null && typeConverter.CanConvertTo(requiredType))
|
||||
{
|
||||
newValue = typeConverter.ConvertTo(newValue, requiredType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// look if it's an enum
|
||||
if (requiredType != null
|
||||
&& requiredType.IsEnum
|
||||
&& (!(newValue is float)
|
||||
&& (!(newValue is double))))
|
||||
{
|
||||
// convert numeric value into enum's underlying type
|
||||
Type numericType = Enum.GetUnderlyingType(requiredType);
|
||||
newValue = Convert.ChangeType(newValue, numericType);
|
||||
|
||||
if (Enum.IsDefined(requiredType, newValue))
|
||||
{
|
||||
newValue = Enum.ToObject(requiredType, newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TypeMismatchException(
|
||||
CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType);
|
||||
}
|
||||
}
|
||||
else if (newValue is IConvertible)
|
||||
{
|
||||
// last resort - try ChangeType
|
||||
newValue = Convert.ChangeType(newValue, requiredType);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TypeMismatchException(
|
||||
CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new TypeMismatchException(
|
||||
CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType, ex);
|
||||
}
|
||||
if (newValue == null)
|
||||
{
|
||||
throw new TypeMismatchException(
|
||||
CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType);
|
||||
}
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
private static object ToArrayWithTypeConversion(Type componentType, ICollection elements, string propertyName)
|
||||
{
|
||||
ICollection elements = (ICollection) newValue;
|
||||
return ToArrayWithTypeConversion(componentType, elements, propertyName);
|
||||
}
|
||||
else if (newValue is string)
|
||||
{
|
||||
if (requiredType.Equals(typeof(char[])))
|
||||
{
|
||||
return ((string) newValue).ToCharArray();
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] elements = StringUtils.CommaDelimitedListToStringArray((string) newValue);
|
||||
return ToArrayWithTypeConversion(componentType, elements, propertyName);
|
||||
}
|
||||
}
|
||||
else if (!newValue.GetType().IsArray)
|
||||
{
|
||||
// A plain value: convert it to an array with a single component.
|
||||
Array result = Array.CreateInstance(componentType, 1);
|
||||
object val = ConvertValueIfNecessary(componentType, newValue, propertyName);
|
||||
result.SetValue(val, 0);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
// try to convert using type converter
|
||||
try
|
||||
{
|
||||
TypeConverter typeConverter = TypeConverterRegistry.GetConverter(requiredType);
|
||||
if (typeConverter != null && typeConverter.CanConvertFrom(newValue.GetType()))
|
||||
{
|
||||
try
|
||||
{
|
||||
newValue = typeConverter.ConvertFrom(newValue);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (newValue is string)
|
||||
{
|
||||
newValue = typeConverter.ConvertFromInvariantString((string)newValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
typeConverter = TypeConverterRegistry.GetConverter(newValue.GetType());
|
||||
if (typeConverter != null && typeConverter.CanConvertTo(requiredType))
|
||||
{
|
||||
newValue = typeConverter.ConvertTo(newValue, requiredType);
|
||||
}
|
||||
else
|
||||
{
|
||||
// look if it's an enum
|
||||
if (requiredType != null
|
||||
&& requiredType.IsEnum
|
||||
&& (!(newValue is float)
|
||||
&& (!(newValue is double))))
|
||||
{
|
||||
// convert numeric value into enum's underlying type
|
||||
Type numericType = Enum.GetUnderlyingType(requiredType);
|
||||
newValue = Convert.ChangeType(newValue, numericType);
|
||||
|
||||
if (Enum.IsDefined(requiredType, newValue))
|
||||
{
|
||||
newValue = Enum.ToObject(requiredType, newValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TypeMismatchException(
|
||||
CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType);
|
||||
}
|
||||
}
|
||||
else if (newValue is IConvertible)
|
||||
{
|
||||
// last resort - try ChangeType
|
||||
newValue = Convert.ChangeType(newValue, requiredType);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new TypeMismatchException(
|
||||
CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new TypeMismatchException(
|
||||
CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType, ex);
|
||||
}
|
||||
#if NET_2_0
|
||||
if (newValue == null
|
||||
&& (requiredType == null
|
||||
|| !Type.GetType("System.Nullable`1").Equals(requiredType.GetGenericTypeDefinition())))
|
||||
#else
|
||||
if (newValue == null)
|
||||
#endif
|
||||
{
|
||||
throw new TypeMismatchException(
|
||||
CreatePropertyChangeEventArgs(propertyName, null, newValue), requiredType);
|
||||
}
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
|
||||
private static object ToArrayWithTypeConversion(Type componentType, ICollection elements, string propertyName)
|
||||
{
|
||||
Array destination = Array.CreateInstance(componentType, elements.Count);
|
||||
int i = 0;
|
||||
int i = 0;
|
||||
foreach (object element in elements)
|
||||
{
|
||||
object value = ConvertValueIfNecessary(componentType, element, BuildIndexedPropertyName(propertyName, i));
|
||||
destination.SetValue(value, i);
|
||||
i++;
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
return destination;
|
||||
}
|
||||
|
||||
private static string BuildIndexedPropertyName(string propertyName, int index)
|
||||
@@ -182,39 +188,39 @@ namespace Spring.Core.TypeConversion
|
||||
return (propertyName != null ?
|
||||
propertyName + "[" + index + "]":
|
||||
null);
|
||||
}
|
||||
|
||||
private static bool IsAssignableFrom(object newValue, Type requiredType)
|
||||
{
|
||||
if (newValue is MarshalByRefObject)
|
||||
{
|
||||
//TODO see what type of type checking can be done. May need to
|
||||
//preserve information when proxy was created by SaoServiceExporter.
|
||||
return true;
|
||||
}
|
||||
if (requiredType == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return requiredType.IsAssignableFrom(newValue.GetType());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method to create a property change event.
|
||||
/// </summary>
|
||||
/// <param name="fullPropertyName">
|
||||
/// The full name of the property that has changed.
|
||||
/// </param>
|
||||
/// <param name="oldValue">The property old value</param>
|
||||
/// <param name="newValue">The property new value</param>
|
||||
/// <returns>
|
||||
/// A new <see cref="Spring.Core.PropertyChangeEventArgs"/>.
|
||||
/// </returns>
|
||||
private static PropertyChangeEventArgs CreatePropertyChangeEventArgs(string fullPropertyName, object oldValue,
|
||||
object newValue)
|
||||
{
|
||||
return new PropertyChangeEventArgs(fullPropertyName, oldValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsAssignableFrom(object newValue, Type requiredType)
|
||||
{
|
||||
if (newValue is MarshalByRefObject)
|
||||
{
|
||||
//TODO see what type of type checking can be done. May need to
|
||||
//preserve information when proxy was created by SaoServiceExporter.
|
||||
return true;
|
||||
}
|
||||
if (requiredType == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return requiredType.IsAssignableFrom(newValue.GetType());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Utility method to create a property change event.
|
||||
/// </summary>
|
||||
/// <param name="fullPropertyName">
|
||||
/// The full name of the property that has changed.
|
||||
/// </param>
|
||||
/// <param name="oldValue">The property old value</param>
|
||||
/// <param name="newValue">The property new value</param>
|
||||
/// <returns>
|
||||
/// A new <see cref="Spring.Core.PropertyChangeEventArgs"/>.
|
||||
/// </returns>
|
||||
private static PropertyChangeEventArgs CreatePropertyChangeEventArgs(string fullPropertyName, object oldValue,
|
||||
object newValue)
|
||||
{
|
||||
return new PropertyChangeEventArgs(fullPropertyName, oldValue, newValue);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
#region License
|
||||
|
||||
/*
|
||||
* Copyright <20> 2002-2007 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#endregion
|
||||
|
||||
#region Imports
|
||||
|
||||
using System;
|
||||
using NUnit.Framework;
|
||||
|
||||
#endregion
|
||||
|
||||
namespace Spring.Core.TypeConversion
|
||||
{
|
||||
/// <summary>
|
||||
/// This class contains tests for TypeConversionUtils
|
||||
/// </summary>
|
||||
/// <author>Mark Pollack</author>
|
||||
[TestFixture]
|
||||
public class TypeConversionUtilsTests
|
||||
{
|
||||
|
||||
#if NET_2_0
|
||||
[Test]
|
||||
public void NullAbleTest()
|
||||
{
|
||||
object o = TypeConversionUtils.ConvertValueIfNecessary(typeof(DateTime?), "", "bla");
|
||||
Assert.IsNull(o);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
@@ -260,6 +260,7 @@
|
||||
<Compile Include="Core\TypeConversion\StreamConverterTests.cs" />
|
||||
<Compile Include="Core\TypeConversion\StringArrayConverterTests.cs" />
|
||||
<Compile Include="Core\TypeConversion\TimeSpanConverterTests.cs" />
|
||||
<Compile Include="Core\TypeConversion\TypeConversionUtilsTests.cs" />
|
||||
<Compile Include="Core\TypeConversion\TypeConverterRegistryTests.cs" />
|
||||
<Compile Include="Core\TypeConversion\UriConverterTests.cs" />
|
||||
<Compile Include="Core\TypeResolution\CachedTypeResolverTests.cs" />
|
||||
|
||||
Reference in New Issue
Block a user