mirror of
				https://github.com/art-ist/mustache-sharp.git
				synced 2024-06-16 21:05:32 +00:00 
			
		
		
		
	Handle dictionaries with non-object values.
In cases where a Dictionary property had a non-object value type, the dictionary couldn't be treated like an object. This code will wrap a dictionary so its non-object values are upcast to objects.
This commit is contained in:
		
							parent
							
								
									7bda253bab
								
							
						
					
					
						commit
						10304d811c
					
				
							
								
								
									
										206
									
								
								mustache-sharp.test/UpcastDictionaryTester.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										206
									
								
								mustache-sharp.test/UpcastDictionaryTester.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,206 @@ | ||||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| 
 | ||||
| namespace Mustache.Test | ||||
| { | ||||
|     [TestClass] | ||||
|     public class UpcastDictionaryTester | ||||
|     { | ||||
|         [TestMethod] | ||||
|         public void ShouldReturnNullForNull() | ||||
|         { | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(null); | ||||
|             Assert.IsNull(result, "Null should be returned for null."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldReturnArgumentIfIDictionary_string_object() | ||||
|         { | ||||
|             object source = new Dictionary<string, object>(); | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             Assert.AreSame(source, result, "The up-cast wrapper should not be applied if already a IDictionary<string, object>."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldReturnNullIfNotGenericType() | ||||
|         { | ||||
|             object source = String.Empty; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             Assert.IsNull(result, "Null should be returned for non-generic types."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldReturnNullIfWrongNumberOfGenericArguments() | ||||
|         { | ||||
|             object source = new List<string>(); | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             Assert.IsNull(result, "Null should be returned for generic types with the wrong number of type arguments."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldReturnNullIfFirstGenericTypeArgumentIsNotAString() | ||||
|         { | ||||
|             object source = new Dictionary<object, object>(); | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             Assert.IsNull(result, "Null should be returned if the first generic type argument is not a string."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldReturnNullIfNotDictionaryType() | ||||
|         { | ||||
|             object source = (Converter<string, object>)(s => (object)s); | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             Assert.IsNull(result, "Null should be returned for non-dictionary types."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldReturnUpcastWrapperForDictionary_string_TValue() | ||||
|         { | ||||
|             object source = new Dictionary<string, string>(); | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             Assert.IsInstanceOfType(result, typeof(UpcastDictionary<string>), "The source was not wrapped."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldFindKeyIfInWrappedDictionary() | ||||
|         { | ||||
|             object source = new Dictionary<string, string>() { { "Name", "Bob" } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             bool containsKey = result.ContainsKey("Name"); | ||||
|             Assert.IsTrue(containsKey, "The key Name should have been found."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldNotFindKeyIfNotInWrappedDictionary() | ||||
|         { | ||||
|             object source = new Dictionary<string, string>() { { "Name", "Bob" } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             bool containsKey = result.ContainsKey("Age"); | ||||
|             Assert.IsFalse(containsKey, "The key Age should not have been found."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldFindKeysInWrappedDictionary() | ||||
|         { | ||||
|             var source = new Dictionary<string, string>() { { "Name", "Bob" }, { "Age", "100" } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             ICollection sourceKeys = source.Keys; | ||||
|             ICollection wrappedKeys = result.Keys.ToArray(); | ||||
|             CollectionAssert.AreEquivalent(sourceKeys, wrappedKeys, "The same keys should have been found in both collections."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldFindKeyIfInWrappedDictionary_TryGetValue() | ||||
|         { | ||||
|             var source = new Dictionary<string, string>() { { "Name", "Bob" } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             object value; | ||||
|             bool found = result.TryGetValue("Name", out value); | ||||
|             Assert.IsTrue(found, "The key should have been found."); | ||||
|             Assert.AreSame(source["Name"], value, "The value in the underlying dictionary should have been returned."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldNotFindKeyIfNotInWrappedDictionary_TryGetValue() | ||||
|         { | ||||
|             var source = new Dictionary<string, int>() { { "Age", 100 } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             object value; | ||||
|             bool found = result.TryGetValue("Name", out value); | ||||
|             Assert.IsFalse(found, "The key should not have been found."); | ||||
|             Assert.IsNull(value, "The value should be null even if the actual type is a struct."); | ||||
|          | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldReturnValuesAsObjects() | ||||
|         { | ||||
|             var source = new Dictionary<string, int>() { { "Age", 100 }, { "Weight", 500 } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             ICollection sourceValues = source.Values; | ||||
|             ICollection wrappedValues = result.Values.ToArray(); | ||||
|             CollectionAssert.AreEquivalent(sourceValues, wrappedValues, "The underlying values were not returned."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldFindKeyIfInWrappedDictionary_Indexer() | ||||
|         { | ||||
|             var source = new Dictionary<string, string>() { { "Name", "Bob" } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             object value = result["Name"]; | ||||
|             Assert.AreSame(source["Name"], value, "The value in the underlying dictionary should have been returned."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         [ExpectedException(typeof(KeyNotFoundException))] | ||||
|         public void ShouldNotFindKeyIfNotInWrappedDictionary_Indexer() | ||||
|         { | ||||
|             var source = new Dictionary<string, int>() { { "Age", 100 } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             object value = result["Name"]; | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldNotFindPairIfValueWrongType() | ||||
|         { | ||||
|             var source = new Dictionary<string, int>() { { "Age", 100 } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             bool contains = result.Contains(new KeyValuePair<string, object>("Age", "Blah")); | ||||
|             Assert.IsFalse(contains, "The pair should not have been found."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldFindPairInWrappedDictionary() | ||||
|         { | ||||
|             var source = new Dictionary<string, int>() { { "Age", 100 } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             bool contains = result.Contains(new KeyValuePair<string, object>("Age", 100)); | ||||
|             Assert.IsTrue(contains, "The pair should have been found."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldCopyPairsToArray() | ||||
|         { | ||||
|             var source = new Dictionary<string, int>() { { "Age", 100 }, { "Weight", 45 } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             var array = new KeyValuePair<string, object>[2]; | ||||
|             result.CopyTo(array, 0); | ||||
|             var expected = new KeyValuePair<string, object>[] | ||||
|             { | ||||
|                 new KeyValuePair<string, object>("Age", 100), | ||||
|                 new KeyValuePair<string, object>("Weight", 45) | ||||
|             }; | ||||
|             CollectionAssert.AreEqual(expected, array, "The pairs were not copied."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldGetCount() | ||||
|         { | ||||
|             var source = new Dictionary<string, int>() { { "Age", 100 }, { "Weight", 45 } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             Assert.AreEqual(source.Count, result.Count, "The source and Upcast dictionary should have the same count."); | ||||
|         } | ||||
| 
 | ||||
|         [TestMethod] | ||||
|         public void ShouldGetEnumerator() | ||||
|         { | ||||
|             var source = new Dictionary<string, int>() { { "Age", 100 }, { "Weight", 45 } }; | ||||
|             IDictionary<string, object> result = UpcastDictionary.Create(source); | ||||
|             IEnumerator<KeyValuePair<string, object>> enumerator = result.GetEnumerator(); | ||||
|             var values = new List<KeyValuePair<string, object>>(); | ||||
|             while (enumerator.MoveNext()) | ||||
|             { | ||||
|                 values.Add(enumerator.Current); | ||||
|             } | ||||
|             var expected = new KeyValuePair<string, object>[] | ||||
|             { | ||||
|                 new KeyValuePair<string, object>("Age", 100), | ||||
|                 new KeyValuePair<string, object>("Weight", 45) | ||||
|             }; | ||||
|             CollectionAssert.AreEqual(expected, values, "The enumerator did not return the correct pairs."); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -45,6 +45,7 @@ | ||||
|   <ItemGroup> | ||||
|     <Compile Include="FormatCompilerTester.cs" /> | ||||
|     <Compile Include="Properties\AssemblyInfo.cs" /> | ||||
|     <Compile Include="UpcastDictionaryTester.cs" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\mustache-sharp\mustache-sharp.csproj"> | ||||
|  | ||||
| @ -125,7 +125,7 @@ namespace Mustache | ||||
| 
 | ||||
|         private static IDictionary<string, object> toLookup(object value) | ||||
|         { | ||||
|             IDictionary<string, object> lookup = value as IDictionary<string, object>; | ||||
|             IDictionary<string, object> lookup = UpcastDictionary.Create(value); | ||||
|             if (lookup == null) | ||||
|             { | ||||
|                 lookup = new PropertyDictionary(value); | ||||
|  | ||||
							
								
								
									
										167
									
								
								mustache-sharp/UpcastDictionary.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										167
									
								
								mustache-sharp/UpcastDictionary.cs
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,167 @@ | ||||
| using System; | ||||
| using System.Collections; | ||||
| using System.Collections.Generic; | ||||
| using System.ComponentModel; | ||||
| using System.Linq; | ||||
| 
 | ||||
| namespace Mustache | ||||
| { | ||||
|     internal static class UpcastDictionary | ||||
|     { | ||||
|         public static IDictionary<string, object> Create(object source) | ||||
|         { | ||||
|             if (source == null) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|             IDictionary<string, object> sourceDictionary = source as IDictionary<string, object>; | ||||
|             if (sourceDictionary != null) | ||||
|             { | ||||
|                 return sourceDictionary; | ||||
|             } | ||||
|             Type sourceType = source.GetType(); | ||||
|             if (!sourceType.IsGenericType) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|             Type[] argumentTypes = sourceType.GetGenericArguments(); | ||||
|             if (argumentTypes.Length != 2) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|             Type keyType = argumentTypes[0]; | ||||
|             if (keyType != typeof(string)) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|             Type valueType = argumentTypes[1]; | ||||
|             Type genericType = typeof(IDictionary<,>).MakeGenericType(typeof(string), valueType); | ||||
|             if (!genericType.IsAssignableFrom(sourceType)) | ||||
|             { | ||||
|                 return null; | ||||
|             } | ||||
|             Type upcastType = typeof(UpcastDictionary<>).MakeGenericType(valueType); | ||||
|             return (IDictionary<string, object>)Activator.CreateInstance(upcastType, source); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     internal class UpcastDictionary<TValue> : IDictionary<string, object> | ||||
|     { | ||||
|         private readonly IDictionary<string, TValue> dictionary; | ||||
| 
 | ||||
|         public UpcastDictionary(IDictionary<string, TValue> dictionary) | ||||
|         { | ||||
|             this.dictionary = dictionary; | ||||
|         } | ||||
| 
 | ||||
|         [EditorBrowsable(EditorBrowsableState.Never)] | ||||
|         void IDictionary<string, object>.Add(string key, object value) | ||||
|         { | ||||
|             throw new NotSupportedException(); | ||||
|         } | ||||
| 
 | ||||
|         public bool ContainsKey(string key) | ||||
|         { | ||||
|             return dictionary.ContainsKey(key); | ||||
|         } | ||||
| 
 | ||||
|         public ICollection<string> Keys | ||||
|         { | ||||
|             get { return dictionary.Keys; } | ||||
|         } | ||||
| 
 | ||||
|         [EditorBrowsable(EditorBrowsableState.Never)] | ||||
|         bool IDictionary<string, object>.Remove(string key) | ||||
|         { | ||||
|             throw new NotSupportedException(); | ||||
|         } | ||||
| 
 | ||||
|         public bool TryGetValue(string key, out object value) | ||||
|         { | ||||
|             TValue result; | ||||
|             if (dictionary.TryGetValue(key, out result)) | ||||
|             { | ||||
|                 value = result; | ||||
|                 return true; | ||||
|             } | ||||
|             else | ||||
|             { | ||||
|                 value = null; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         public ICollection<object> Values | ||||
|         { | ||||
|             get { return dictionary.Values.Cast<object>().ToArray(); } | ||||
|         } | ||||
| 
 | ||||
|         public object this[string key] | ||||
|         { | ||||
|             get | ||||
|             { | ||||
|                 return dictionary[key]; | ||||
|             } | ||||
|             [EditorBrowsable(EditorBrowsableState.Never)] | ||||
|             set | ||||
|             { | ||||
|                 throw new NotSupportedException(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         [EditorBrowsable(EditorBrowsableState.Never)] | ||||
|         void ICollection<KeyValuePair<string, object>>.Add(KeyValuePair<string, object> item) | ||||
|         { | ||||
|             throw new NotSupportedException(); | ||||
|         } | ||||
| 
 | ||||
|         [EditorBrowsable(EditorBrowsableState.Never)] | ||||
|         void ICollection<KeyValuePair<string, object>>.Clear() | ||||
|         { | ||||
|             throw new NotSupportedException(); | ||||
|         } | ||||
| 
 | ||||
|         bool ICollection<KeyValuePair<string, object>>.Contains(KeyValuePair<string, object> item) | ||||
|         { | ||||
|             if (!(item.Value is TValue)) | ||||
|             { | ||||
|                 return false; | ||||
|             } | ||||
|             KeyValuePair<string, TValue> pair = new KeyValuePair<string,TValue>(item.Key, (TValue)item.Value); | ||||
|             ICollection<KeyValuePair<string, TValue>> collection = dictionary; | ||||
|             return dictionary.Contains(pair); | ||||
|         } | ||||
| 
 | ||||
|         void ICollection<KeyValuePair<string, object>>.CopyTo(KeyValuePair<string, object>[] array, int arrayIndex) | ||||
|         { | ||||
|             var pairs = dictionary.Select(p => new KeyValuePair<string, object>(p.Key, p.Value)).ToArray(); | ||||
|             pairs.CopyTo(array, arrayIndex); | ||||
|         } | ||||
| 
 | ||||
|         public int Count | ||||
|         { | ||||
|             get { return dictionary.Count; } | ||||
|         } | ||||
| 
 | ||||
|         bool ICollection<KeyValuePair<string, object>>.IsReadOnly | ||||
|         { | ||||
|             get { return true; } | ||||
|         } | ||||
| 
 | ||||
|         [EditorBrowsable(EditorBrowsableState.Never)] | ||||
|         bool ICollection<KeyValuePair<string, object>>.Remove(KeyValuePair<string, object> item) | ||||
|         { | ||||
|             throw new NotSupportedException(); | ||||
|         } | ||||
| 
 | ||||
|         public IEnumerator<KeyValuePair<string, object>> GetEnumerator() | ||||
|         { | ||||
|             return dictionary.Select(p => new KeyValuePair<string, object>(p.Key, p.Value)).GetEnumerator(); | ||||
|         } | ||||
| 
 | ||||
|         IEnumerator IEnumerable.GetEnumerator() | ||||
|         { | ||||
|             return GetEnumerator(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -40,6 +40,7 @@ | ||||
|     <Compile Include="ContentTagDefinition.cs" /> | ||||
|     <Compile Include="Context.cs" /> | ||||
|     <Compile Include="ContextParameter.cs" /> | ||||
|     <Compile Include="UpcastDictionary.cs" /> | ||||
|     <Compile Include="VariableFoundEventArgs.cs" /> | ||||
|     <Compile Include="SetTagDefinition.cs" /> | ||||
|     <Compile Include="NewlineTagDefinition.cs" /> | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Travis Parks
						Travis Parks