Skip to content
This repository has been archived by the owner on Jan 24, 2021. It is now read-only.

Commit

Permalink
Merge pull request #2454 from thecodejunkie/dynamicdictionaryvalue-da…
Browse files Browse the repository at this point in the history
…te-parsing

Use GlobalizationConfiguration.DateTimeStyles in DynamicDictionaryValue DateTime parsing
  • Loading branch information
jchannon committed May 25, 2016
2 parents 8920db0 + d8d954a commit 96cde2b
Show file tree
Hide file tree
Showing 7 changed files with 134 additions and 59 deletions.
44 changes: 30 additions & 14 deletions src/Nancy/DynamicDictionary.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,30 +14,46 @@
[DebuggerDisplay("{DebuggerDisplay, nq}")]
public class DynamicDictionary : DynamicObject, IEquatable<DynamicDictionary>, IHideObjectMembers, IEnumerable<string>, IDictionary<string, object>
{
private readonly GlobalizationConfiguration globalizationConfiguration;

private readonly IDictionary<string, dynamic> dictionary =
new Dictionary<string, dynamic>(StaticConfiguration.CaseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase);

/// <summary>
/// Initializes a new istance of the <see cref="DynamicDictionary"/> class.
/// </summary>
public DynamicDictionary()
: this(GlobalizationConfiguration.Default)
{
}

/// <summary>
/// Initializes a new istance of the <see cref="DynamicDictionary"/> class.
/// </summary>
/// <param name="globalizationConfiguration">A <see cref="GlobalizationConfiguration"/> instance.</param>
public DynamicDictionary(GlobalizationConfiguration globalizationConfiguration)
{
this.globalizationConfiguration = globalizationConfiguration;
}

/// <summary>
/// Returns an empty dynamic dictionary.
/// </summary>
/// <value>A <see cref="DynamicDictionary"/> instance.</value>
public static DynamicDictionary Empty
{
get
{
return new DynamicDictionary();
}
get { return new DynamicDictionary(); }
}

/// <summary>
/// Creates a dynamic dictionary from an <see cref="IDictionary{TKey,TValue}"/> instance.
/// </summary>
/// <param name="values">An <see cref="IDictionary{TKey,TValue}"/> instance, that the dynamic dictionary should be created from.</param>
/// <param name="globalizationConfiguration"></param>
/// <returns>An <see cref="DynamicDictionary"/> instance.</returns>
public static DynamicDictionary Create(IDictionary<string, object> values)
public static DynamicDictionary Create(IDictionary<string, object> values, GlobalizationConfiguration globalizationConfiguration)
{
var instance = new DynamicDictionary();
var instance = new DynamicDictionary(globalizationConfiguration);

foreach (var key in values.Keys)
{
Expand Down Expand Up @@ -67,7 +83,7 @@ public override bool TryGetMember(GetMemberBinder binder, out object result)
{
if (!dictionary.TryGetValue(binder.Name, out result))
{
result = new DynamicDictionaryValue(null);
result = new DynamicDictionaryValue(null, this.globalizationConfiguration);
}

return true;
Expand Down Expand Up @@ -113,7 +129,7 @@ public dynamic this[string name]
dynamic member;
if (!dictionary.TryGetValue(name, out member))
{
member = new DynamicDictionaryValue(null);
member = new DynamicDictionaryValue(null, this.globalizationConfiguration);
}

return member;
Expand All @@ -122,7 +138,7 @@ public dynamic this[string name]
{
name = GetNeutralKey(name);

dictionary[name] = value is DynamicDictionaryValue ? value : new DynamicDictionaryValue(value);
dictionary[name] = value is DynamicDictionaryValue ? value : new DynamicDictionaryValue(value, this.globalizationConfiguration);
}
}

Expand Down Expand Up @@ -257,7 +273,7 @@ public int Count
public bool Contains(KeyValuePair<string, dynamic> item)
{
var dynamicValueKeyValuePair =
GetDynamicKeyValuePair(item);
this.GetDynamicKeyValuePair(item);

return this.dictionary.Contains(dynamicValueKeyValuePair);
}
Expand Down Expand Up @@ -300,7 +316,7 @@ public bool Remove(string key)
public bool Remove(KeyValuePair<string, dynamic> item)
{
var dynamicValueKeyValuePair =
GetDynamicKeyValuePair(item);
this.GetDynamicKeyValuePair(item);

return this.dictionary.Remove(dynamicValueKeyValuePair);
}
Expand All @@ -317,10 +333,10 @@ public ICollection<dynamic> Values
}
}

private static KeyValuePair<string, dynamic> GetDynamicKeyValuePair(KeyValuePair<string, dynamic> item)
private KeyValuePair<string, dynamic> GetDynamicKeyValuePair(KeyValuePair<string, dynamic> item)
{
var dynamicValueKeyValuePair =
new KeyValuePair<string, dynamic>(item.Key, new DynamicDictionaryValue(item.Value));
new KeyValuePair<string, dynamic>(item.Key, new DynamicDictionaryValue(item.Value, this.globalizationConfiguration));
return dynamicValueKeyValuePair;
}

Expand Down Expand Up @@ -360,7 +376,7 @@ private string DebuggerDisplay
for (var i = 0; i < maxItems; i++)
{
var item = this.dictionary.ElementAt(i);

builder.AppendFormat(" {0} = {1}{2}", item.Key, item.Value, i < maxItems - 1 ? "," : string.Empty);
}

Expand Down
64 changes: 38 additions & 26 deletions src/Nancy/DynamicDictionaryValue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,34 @@
using System.Dynamic;
using System.Globalization;
using System.Linq.Expressions;

using Microsoft.CSharp.RuntimeBinder;
using Nancy.Routing.Trie.Nodes;

/// <summary>
/// A value that is stored inside a <see cref="DynamicDictionary"/> instance.
/// </summary>
public class DynamicDictionaryValue : DynamicObject, IEquatable<DynamicDictionaryValue>, IHideObjectMembers, IConvertible
{
private readonly object value;
private readonly GlobalizationConfiguration globalizationConfiguration;

/// <summary>
/// Initializes a new instance of the <see cref="DynamicDictionaryValue"/> class.
/// </summary>
/// <param name="value">The value to store in the instance</param>
public DynamicDictionaryValue(object value)
: this(value, GlobalizationConfiguration.Default)
{
}

/// <summary>
/// Initializes a new instance of the <see cref="DynamicDictionaryValue"/> class.
/// </summary>
/// <param name="value">The value to store in the instance</param>
/// <param name="globalizationConfiguration">A <see cref="GlobalizationConfiguration"/> instance.</param>
public DynamicDictionaryValue(object value, GlobalizationConfiguration globalizationConfiguration)
{
this.value = value;
this.globalizationConfiguration = globalizationConfiguration;
}

/// <summary>
Expand Down Expand Up @@ -52,13 +65,12 @@ public object Value
{
try
{
return (T)value;
return (T)this.value;
}
catch
{
var typeName = value.GetType().Name;
var message = string.Format("Cannot convert value of type '{0}' to type '{1}'",
typeName, typeof(T).Name);
var typeName = this.value.GetType().Name;
var message = string.Format("Cannot convert value of type '{0}' to type '{1}'", typeName, typeof(T).Name);

throw new InvalidCastException(message);
}
Expand All @@ -79,21 +91,21 @@ public object Value
{
try
{
var valueType = value.GetType();
var valueType = this.value.GetType();
var parseType = typeof(T);

// check for direct cast
if (valueType.IsAssignableFrom(parseType))
{
return (T)value;
return (T)this.value;
}

var stringValue = value as string;
var stringValue = this.value as string;
if (parseType == typeof(DateTime))
{
DateTime result;

if (DateTime.TryParse(stringValue, CultureInfo.InvariantCulture, DateTimeStyles.None, out result))
if (DateTime.TryParse(stringValue, CultureInfo.InvariantCulture, this.globalizationConfiguration.DateTimeStyles, out result))
{
return (T)((object)result);
}
Expand All @@ -115,7 +127,7 @@ public object Value

var underlyingType = Nullable.GetUnderlyingType(parseType) ?? parseType;

return (T)Convert.ChangeType(value, underlyingType, CultureInfo.InvariantCulture);
return (T)Convert.ChangeType(this.value, underlyingType, CultureInfo.InvariantCulture);
}
catch
{
Expand Down Expand Up @@ -209,7 +221,7 @@ public override bool TryBinaryOperation(BinaryOperationBinder binder, object arg
var convert =
Binder.Convert(CSharpBinderFlags.None, arg.GetType(), typeof(DynamicDictionaryValue));

if (!TryConvert((ConvertBinder)convert, out resultOfCast))
if (!this.TryConvert((ConvertBinder)convert, out resultOfCast))
{
return false;
}
Expand All @@ -230,22 +242,22 @@ public override bool TryConvert(ConvertBinder binder, out object result)
{
result = null;

if (value == null)
if (this.value == null)
{
return true;
}

var binderType = binder.Type;
if (binderType == typeof(String))
if (binderType == typeof(string))
{
result = Convert.ToString(value);
result = Convert.ToString(this.value);
return true;
}

if (binderType == typeof(Guid) || binderType == typeof(Guid?))
{
Guid guid;
if (Guid.TryParse(Convert.ToString(value), out guid))
if (Guid.TryParse(Convert.ToString(this.value), out guid))
{
result = guid;
return true;
Expand All @@ -254,7 +266,7 @@ public override bool TryConvert(ConvertBinder binder, out object result)
else if (binderType == typeof(TimeSpan) || binderType == typeof(TimeSpan?))
{
TimeSpan timespan;
if (TimeSpan.TryParse(Convert.ToString(value), out timespan))
if (TimeSpan.TryParse(Convert.ToString(this.value), out timespan))
{
result = timespan;
return true;
Expand All @@ -263,21 +275,21 @@ public override bool TryConvert(ConvertBinder binder, out object result)
else if (binderType.IsEnum)
{
// handles enum to enum assignments
if (value.GetType().IsEnum)
if (this.value.GetType().IsEnum)
{
if (binderType == value.GetType())
if (binderType == this.value.GetType())
{
result = value;
result = this.value;
return true;
}

return false;
}

// handles number to enum assignments
if (Enum.GetUnderlyingType(binderType) == value.GetType())
if (Enum.GetUnderlyingType(binderType) == this.value.GetType())
{
result = Enum.ToObject(binderType, value);
result = Enum.ToObject(binderType, this.value);
return true;
}

Expand All @@ -294,9 +306,9 @@ public override bool TryConvert(ConvertBinder binder, out object result)

if (typeCode == TypeCode.Object)
{
if (binderType.IsAssignableFrom(value.GetType()))
if (binderType.IsAssignableFrom(this.value.GetType()))
{
result = value;
result = this.value;
return true;
}
else
Expand All @@ -305,7 +317,7 @@ public override bool TryConvert(ConvertBinder binder, out object result)
}
}

result = Convert.ChangeType(value, typeCode);
result = Convert.ChangeType(this.value, typeCode);

return true;
}
Expand Down Expand Up @@ -427,7 +439,7 @@ public static implicit operator DateTime(DynamicDictionaryValue dynamicValue)
return (DateTime)dynamicValue.value;
}

return DateTime.Parse(dynamicValue.ToString());
return DateTime.Parse(dynamicValue.ToString(), CultureInfo.InvariantCulture, dynamicValue.globalizationConfiguration.DateTimeStyles);
}

public static implicit operator TimeSpan?(DynamicDictionaryValue dynamicValue)
Expand Down
2 changes: 1 addition & 1 deletion src/Nancy/Extensions/StringExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public static bool IsParameterized(this string segment)
public static DynamicDictionary AsQueryDictionary(this string queryString)
{
var coll = HttpUtility.ParseQueryString(queryString);
var ret = new DynamicDictionary();
var ret = new DynamicDictionary(GlobalizationConfiguration.Default);

var found = 0;
foreach (var key in coll.AllKeys.Where(key => key != null))
Expand Down
28 changes: 22 additions & 6 deletions src/Nancy/GlobalizationConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,31 @@ public class GlobalizationConfiguration
/// <summary>
/// A default instance of the <see cref="GlobalizationConfiguration"/> class
/// </summary>
public static readonly GlobalizationConfiguration Default = new GlobalizationConfiguration(supportedCultureNames: new[] { CultureInfo.CurrentCulture.Name }, defaultCulture: CultureInfo.CurrentCulture.Name);
public static readonly GlobalizationConfiguration Default = new GlobalizationConfiguration
{
SupportedCultureNames = new[] { CultureInfo.CurrentCulture.Name },
DefaultCulture = CultureInfo.CurrentCulture.Name,
DateTimeStyles = DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal
};

private GlobalizationConfiguration()
{
}

/// <summary>
/// Initializes a new instance of the <see cref="GlobalizationConfiguration"/> class
/// </summary>
/// <param name="supportedCultureNames">An array of supported cultures</param>
/// <param name="defaultCulture">The default culture of the application</param>
public GlobalizationConfiguration(IEnumerable<string> supportedCultureNames, string defaultCulture = null)
/// <param name="dateTimeStyles">The <see cref="DateTimeStyles"/> that should be used for date parsing.</param>
public GlobalizationConfiguration(IEnumerable<string> supportedCultureNames, string defaultCulture = null, DateTimeStyles? dateTimeStyles = null)
{
if (supportedCultureNames == null)
{
throw new ConfigurationException("Invalid Globalization configuration. You must support at least one culture");
}

supportedCultureNames = supportedCultureNames.Where(cultureName => !string.IsNullOrEmpty(cultureName));
supportedCultureNames = supportedCultureNames.Where(cultureName => !string.IsNullOrEmpty(cultureName)).ToArray();

if (!supportedCultureNames.Any())
{
Expand All @@ -44,18 +54,24 @@ public GlobalizationConfiguration(IEnumerable<string> supportedCultureNames, str
throw new ConfigurationException("Invalid Globalization configuration. " + defaultCulture + " does not exist in the supported culture names");
}

this.SupportedCultureNames = supportedCultureNames;
this.DateTimeStyles = dateTimeStyles ?? Default.DateTimeStyles;
this.DefaultCulture = defaultCulture;
this.SupportedCultureNames = supportedCultureNames;
}

/// <summary>
/// A set of supported cultures
/// The <see cref="DateTimeStyles"/> that should be used for date parsing.
/// </summary>
public IEnumerable<string> SupportedCultureNames { get; private set; }
public DateTimeStyles DateTimeStyles { get; private set; }

/// <summary>
/// The default culture for the application
/// </summary>
public string DefaultCulture { get; private set; }

/// <summary>
/// A set of supported cultures
/// </summary>
public IEnumerable<string> SupportedCultureNames { get; private set; }
}
}
Loading

0 comments on commit 96cde2b

Please sign in to comment.