From 74cea9595637c6351b2f633d3be782a7b7c681ec Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 22 Feb 2016 18:48:11 -0600 Subject: [PATCH 1/6] Can now bind to a non-IList/Array indexer (with any parameter type convertible via TypeUtilities). --- src/Markup/Perspex.Markup/Data/IndexerNode.cs | 30 +++++++++++++++++++ .../Data/Parsers/ArgumentListParser.cs | 8 ++++- .../Data/ExpressionObserverTests_Indexer.cs | 10 +++++++ 3 files changed, 47 insertions(+), 1 deletion(-) diff --git a/src/Markup/Perspex.Markup/Data/IndexerNode.cs b/src/Markup/Perspex.Markup/Data/IndexerNode.cs index b791e15eaa..97f3b688f9 100644 --- a/src/Markup/Perspex.Markup/Data/IndexerNode.cs +++ b/src/Markup/Perspex.Markup/Data/IndexerNode.cs @@ -1,10 +1,12 @@ // Copyright (c) The Perspex Project. All rights reserved. // Licensed under the MIT license. See licence.md file in the project root for full license information. +using Perspex.Utilities; using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.Globalization; using System.Linq; using System.Reflection; @@ -104,6 +106,34 @@ namespace Perspex.Markup.Data return list[_intArgs[0]]; } } + else + { + PropertyInfo indexerProperty = null; + ParameterInfo[] indexerParameters = null; + foreach (var property in typeInfo.DeclaredProperties) + { + var indexParams = property.GetIndexParameters(); + if (indexParams.Length > 0) + { + indexerProperty = property; + indexerParameters = indexParams; + } + } + if (indexerProperty != null && indexerParameters.Length == Arguments.Count) + { + var convertedObjectArray = new object[indexerParameters.Length]; + for (int i = 0; i < Arguments.Count; i++) + { + object temp = null; + if (!TypeUtilities.TryConvert(indexerParameters[i].ParameterType, Arguments[i], CultureInfo.InvariantCulture, out temp)) + { + return PerspexProperty.UnsetValue; + } + convertedObjectArray[i] = temp; + } + return indexerProperty.GetValue(target, convertedObjectArray); + } + } return PerspexProperty.UnsetValue; } diff --git a/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs b/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs index 5b67166739..41ceb2ca1e 100644 --- a/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs +++ b/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Text; namespace Perspex.Markup.Data.Parsers { @@ -26,7 +27,12 @@ namespace Perspex.Markup.Data.Parsers } else { - throw new ExpressionParseException(r.Position, "Expected integer."); + var builder = new StringBuilder(); + while (!r.End && r.Peek != ',' && r.Peek != close) + { + builder.Append(r.Take()); + } + result.Add(builder.ToString()); } r.SkipWhitespace(); diff --git a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs index 1359a853e3..2f54ae3758 100644 --- a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs +++ b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs @@ -32,6 +32,16 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal("qux", result); } + [Fact] + public async void Should_Get_Value_For_Non_Integer_Indexer() + { + var data = new { Foo = new Dictionary { { "foo", "bar" }, { "baz", "qux" } } }; + var target = new ExpressionObserver(data, "Foo[foo]"); + var result = await target.Take(1); + + Assert.Equal("bar", result); + } + [Fact] public async void Array_Out_Of_Bounds_Should_Return_UnsetValue() { From 5ed27d36d3642ea596579ce4e60969472434d518 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 22 Feb 2016 19:58:42 -0600 Subject: [PATCH 2/6] Added code for listening for indexer updates via INotifyPropertyChanged. --- .../Data/CommonPropertyNames.cs | 13 +++++ src/Markup/Perspex.Markup/Data/IndexerNode.cs | 49 ++++++++++++++----- .../Perspex.Markup/Perspex.Markup.csproj | 1 + .../Data/ExpressionObserverTests_Indexer.cs | 35 +++++++++++++ 4 files changed, 87 insertions(+), 11 deletions(-) create mode 100644 src/Markup/Perspex.Markup/Data/CommonPropertyNames.cs diff --git a/src/Markup/Perspex.Markup/Data/CommonPropertyNames.cs b/src/Markup/Perspex.Markup/Data/CommonPropertyNames.cs new file mode 100644 index 0000000000..5866539886 --- /dev/null +++ b/src/Markup/Perspex.Markup/Data/CommonPropertyNames.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Perspex.Markup.Data +{ + public static class CommonPropertyNames + { + public const string IndexerName = "Item"; + } +} diff --git a/src/Markup/Perspex.Markup/Data/IndexerNode.cs b/src/Markup/Perspex.Markup/Data/IndexerNode.cs index 97f3b688f9..46663074e3 100644 --- a/src/Markup/Perspex.Markup/Data/IndexerNode.cs +++ b/src/Markup/Perspex.Markup/Data/IndexerNode.cs @@ -6,6 +6,7 @@ using System; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.ComponentModel; using System.Globalization; using System.Linq; using System.Reflection; @@ -40,6 +41,22 @@ namespace Perspex.Markup.Data { incc.CollectionChanged += CollectionChanged; } + + var inpc = target as INotifyPropertyChanged; + + if(inpc != null) + { + inpc.PropertyChanged += IndexerPropertyChanged; + } + } + + private void IndexerPropertyChanged(object sender, PropertyChangedEventArgs e) + { + var typeInfo = sender.GetType().GetTypeInfo(); + if (typeInfo.GetDeclaredProperty(e.PropertyName).GetIndexParameters().Any()) + { + CurrentValue = GetValue(sender); + } } protected override void Unsubscribe(object target) @@ -108,17 +125,8 @@ namespace Perspex.Markup.Data } else { - PropertyInfo indexerProperty = null; - ParameterInfo[] indexerParameters = null; - foreach (var property in typeInfo.DeclaredProperties) - { - var indexParams = property.GetIndexParameters(); - if (indexParams.Length > 0) - { - indexerProperty = property; - indexerParameters = indexParams; - } - } + var indexerProperty = GetIndexer(typeInfo); + var indexerParameters = indexerProperty?.GetIndexParameters(); if (indexerProperty != null && indexerParameters.Length == Arguments.Count) { var convertedObjectArray = new object[indexerParameters.Length]; @@ -138,6 +146,25 @@ namespace Perspex.Markup.Data return PerspexProperty.UnsetValue; } + private static PropertyInfo GetIndexer(TypeInfo typeInfo) + { + PropertyInfo indexer; + // Check for the default indexer name first to make this faster. + // This will only be false when a class in VB has a custom indexer name. + if ((indexer = typeInfo.GetDeclaredProperty(CommonPropertyNames.IndexerName)) != null) + { + return indexer; + } + foreach (var property in typeInfo.DeclaredProperties) + { + if (property.GetIndexParameters().Any()) + { + return property; + } + } + return null; + } + private bool InBounds(int[] args, Array array) { if (args.Length == array.Rank) diff --git a/src/Markup/Perspex.Markup/Perspex.Markup.csproj b/src/Markup/Perspex.Markup/Perspex.Markup.csproj index 535fec127f..ba6e7d9331 100644 --- a/src/Markup/Perspex.Markup/Perspex.Markup.csproj +++ b/src/Markup/Perspex.Markup/Perspex.Markup.csproj @@ -41,6 +41,7 @@ Properties\SharedAssemblyInfo.cs + diff --git a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs index 2f54ae3758..a233586a58 100644 --- a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs +++ b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs @@ -146,5 +146,40 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal(new[] { "bar", PerspexProperty.UnsetValue }, result); } + + [Fact] + public void Should_Track_NonIntegerIndexer() + { + var data = new { Foo = new NonIntegerIndexer() }; + data.Foo["foo"] = "bar"; + data.Foo["baz"] = "qux"; + + var target = new ExpressionObserver(data, "Foo[foo]"); + var result = new List(); + + var sub = target.Subscribe(x => result.Add(x)); + data.Foo["foo"] = "bar2"; + + var expected = new[] { "bar", "bar2" }; + Assert.Equal(expected, result); + } + + private class NonIntegerIndexer : NotifyingBase + { + private Dictionary storage = new Dictionary(); + + public string this[string key] + { + get + { + return storage[key]; + } + set + { + storage[key] = value; + RaisePropertyChanged(CommonPropertyNames.IndexerName); + } + } + } } } From 8c6c1ffaea3c739be1164074560b163f8761c1c0 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 22 Feb 2016 20:15:03 -0600 Subject: [PATCH 3/6] Added unit tests for parsing floating point indexers and changed LiteralParser to support it. --- .../Data/Parsers/LiteralParser.cs | 18 +++++++++++++++--- .../Data/ExpressionObserverTests_Indexer.cs | 12 +++++++++++- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/src/Markup/Perspex.Markup/Data/Parsers/LiteralParser.cs b/src/Markup/Perspex.Markup/Data/Parsers/LiteralParser.cs index 36e08c3dbe..b50e46f635 100644 --- a/src/Markup/Perspex.Markup/Data/Parsers/LiteralParser.cs +++ b/src/Markup/Perspex.Markup/Data/Parsers/LiteralParser.cs @@ -12,21 +12,33 @@ namespace Perspex.Markup.Data.Parsers { if (char.IsDigit(r.Peek)) { - StringBuilder result = new StringBuilder(); - + var result = new StringBuilder(); + var foundDecimal = false; while (!r.End) { if (char.IsDigit(r.Peek)) { result.Append(r.Take()); } + else if (!foundDecimal && r.Peek == '.') + { + result.Append(r.Take()); + foundDecimal = true; + } else { break; } } - return int.Parse(result.ToString(), CultureInfo.InvariantCulture); + if (!foundDecimal) + { + return int.Parse(result.ToString(), CultureInfo.InvariantCulture); + } + else + { + return result.ToString(); // Leave as a string to support double, float, and decimal indicies + } } return null; diff --git a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs index a233586a58..d336afa884 100644 --- a/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs +++ b/tests/Perspex.Markup.UnitTests/Data/ExpressionObserverTests_Indexer.cs @@ -33,7 +33,7 @@ namespace Perspex.Markup.UnitTests.Data } [Fact] - public async void Should_Get_Value_For_Non_Integer_Indexer() + public async void Should_Get_Value_For_String_Indexer() { var data = new { Foo = new Dictionary { { "foo", "bar" }, { "baz", "qux" } } }; var target = new ExpressionObserver(data, "Foo[foo]"); @@ -42,6 +42,16 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal("bar", result); } + [Fact] + public async void Should_Get_Value_For_Non_String_Indexer() + { + var data = new { Foo = new Dictionary { { 1.0, "bar" }, { 2.0, "qux" } } }; + var target = new ExpressionObserver(data, "Foo[1.0]"); + var result = await target.Take(1); + + Assert.Equal("bar", result); + } + [Fact] public async void Array_Out_Of_Bounds_Should_Return_UnsetValue() { From 6783fd21e06faed5a6e20917286d325e6438c5e2 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Mon, 22 Feb 2016 22:13:21 -0600 Subject: [PATCH 4/6] Fixed failing tests. --- src/Markup/Perspex.Markup/Data/IndexerNode.cs | 4 ++++ src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/src/Markup/Perspex.Markup/Data/IndexerNode.cs b/src/Markup/Perspex.Markup/Data/IndexerNode.cs index 46663074e3..0364689735 100644 --- a/src/Markup/Perspex.Markup/Data/IndexerNode.cs +++ b/src/Markup/Perspex.Markup/Data/IndexerNode.cs @@ -53,6 +53,10 @@ namespace Perspex.Markup.Data private void IndexerPropertyChanged(object sender, PropertyChangedEventArgs e) { var typeInfo = sender.GetType().GetTypeInfo(); + if (typeInfo.GetDeclaredProperty(e.PropertyName) == null) + { + return; + } if (typeInfo.GetDeclaredProperty(e.PropertyName).GetIndexParameters().Any()) { CurrentValue = GetValue(sender); diff --git a/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs b/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs index 41ceb2ca1e..6e6c3d21fd 100644 --- a/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs +++ b/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs @@ -32,6 +32,10 @@ namespace Perspex.Markup.Data.Parsers { builder.Append(r.Take()); } + if (builder.Length == 0) + { + throw new ExpressionParseException(r.Position, "Expected indexer argument."); + } result.Add(builder.ToString()); } From 7baf8ba615ee09c046ec223745612382d396bbfe Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 23 Feb 2016 22:26:18 -0600 Subject: [PATCH 5/6] Changed argument parser to parse everything as strings and refactored the IndexerNode.GetValue to special case places where we can check validity (and multidimensional arrays since they don't work like everything else). Removed LiteralParser since it isn't used by anything any more. --- src/Markup/Perspex.Markup/Data/IndexerNode.cs | 201 ++++++++++++------ .../Data/Parsers/ArgumentListParser.cs | 25 +-- .../Data/Parsers/LiteralParser.cs | 47 ---- .../Perspex.Markup/Perspex.Markup.csproj | 1 - .../Data/ExpressionNodeBuilderTests.cs | 27 ++- 5 files changed, 158 insertions(+), 143 deletions(-) delete mode 100644 src/Markup/Perspex.Markup/Data/Parsers/LiteralParser.cs diff --git a/src/Markup/Perspex.Markup/Data/IndexerNode.cs b/src/Markup/Perspex.Markup/Data/IndexerNode.cs index 0364689735..712adeebcf 100644 --- a/src/Markup/Perspex.Markup/Data/IndexerNode.cs +++ b/src/Markup/Perspex.Markup/Data/IndexerNode.cs @@ -15,21 +15,12 @@ namespace Perspex.Markup.Data { internal class IndexerNode : ExpressionNode { - private readonly int[] _intArgs; - - public IndexerNode(IList arguments) + public IndexerNode(IList arguments) { Arguments = arguments; - - var intArgs = Arguments.OfType().ToArray(); - - if (intArgs.Length == arguments.Count) - { - _intArgs = intArgs; - } } - public IList Arguments { get; } + public IList Arguments { get; } protected override void SubscribeAndUpdate(object target) { @@ -75,29 +66,41 @@ namespace Perspex.Markup.Data private void CollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { - bool update = false; - - switch (e.Action) + var update = false; + if (sender is IList) + { + object indexObject; + if (!TypeUtilities.TryConvert(typeof(int), Arguments[0], CultureInfo.InvariantCulture, out indexObject)) + { + return; + } + var index = (int)indexObject; + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + update = index >= e.NewStartingIndex; + break; + case NotifyCollectionChangedAction.Remove: + update = index >= e.OldStartingIndex; + break; + case NotifyCollectionChangedAction.Replace: + update = index >= e.NewStartingIndex && + index < e.NewStartingIndex + e.NewItems.Count; + break; + case NotifyCollectionChangedAction.Move: + update = (index >= e.NewStartingIndex && + index < e.NewStartingIndex + e.NewItems.Count) || + (index >= e.OldStartingIndex && + index < e.OldStartingIndex + e.OldItems.Count); + break; + case NotifyCollectionChangedAction.Reset: + update = true; + break; + } + } + else { - case NotifyCollectionChangedAction.Add: - update = _intArgs[0] >= e.NewStartingIndex; - break; - case NotifyCollectionChangedAction.Remove: - update = _intArgs[0] >= e.OldStartingIndex; - break; - case NotifyCollectionChangedAction.Replace: - update = _intArgs[0] >= e.NewStartingIndex && - _intArgs[0] < e.NewStartingIndex + e.NewItems.Count; - break; - case NotifyCollectionChangedAction.Move: - update = (_intArgs[0] >= e.NewStartingIndex && - _intArgs[0] < e.NewStartingIndex + e.NewItems.Count) || - (_intArgs[0] >= e.OldStartingIndex && - _intArgs[0] < e.OldStartingIndex + e.OldItems.Count); - break; - case NotifyCollectionChangedAction.Reset: - update = true; - break; + update = true; } if (update) @@ -110,72 +113,130 @@ namespace Perspex.Markup.Data { var typeInfo = target.GetType().GetTypeInfo(); var list = target as IList; - - if (typeInfo.IsArray && _intArgs != null) + var dictionary = target as IDictionary; + //TODO: Implement array as special case. It doesn't have an indexer property. + var indexerProperty = GetIndexer(typeInfo); + var indexerParameters = indexerProperty?.GetIndexParameters(); + if (indexerProperty != null && indexerParameters.Length == Arguments.Count) { - var array = (Array)target; - - if (InBounds(_intArgs, array)) + var convertedObjectArray = new object[indexerParameters.Length]; + for (int i = 0; i < Arguments.Count; i++) { - return array.GetValue(_intArgs); + object temp = null; + if (!TypeUtilities.TryConvert(indexerParameters[i].ParameterType, Arguments[i], CultureInfo.InvariantCulture, out temp)) + { + return PerspexProperty.UnsetValue; + } + convertedObjectArray[i] = temp; } - } - else if (target is IList && _intArgs?.Length == 1) - { - if (_intArgs[0] < list.Count) + var intArgs = convertedObjectArray.OfType().ToArray(); + + // Try special cases where we can validate indicies + if (typeInfo.IsArray) { - return list[_intArgs[0]]; + return GetValueFromArray((Array)target, intArgs); } - } - else - { - var indexerProperty = GetIndexer(typeInfo); - var indexerParameters = indexerProperty?.GetIndexParameters(); - if (indexerProperty != null && indexerParameters.Length == Arguments.Count) + else if (Arguments.Count == 1) { - var convertedObjectArray = new object[indexerParameters.Length]; - for (int i = 0; i < Arguments.Count; i++) + if (list != null) + { + if (intArgs.Length == Arguments.Count && intArgs[0] >= 0 && intArgs[0] < list.Count) + { + return list[intArgs[0]]; + } + return PerspexProperty.UnsetValue; + } + else if (dictionary != null) { - object temp = null; - if (!TypeUtilities.TryConvert(indexerParameters[i].ParameterType, Arguments[i], CultureInfo.InvariantCulture, out temp)) + if (dictionary.Contains(convertedObjectArray[0])) { - return PerspexProperty.UnsetValue; + return dictionary[convertedObjectArray[0]]; } - convertedObjectArray[i] = temp; + return PerspexProperty.UnsetValue; } - return indexerProperty.GetValue(target, convertedObjectArray); + else + { + // Fallback to unchecked access + return indexerProperty.GetValue(target, convertedObjectArray); + } + } + else + { + // Fallback to unchecked access + return indexerProperty.GetValue(target, convertedObjectArray); } } + // Multidimensional arrays end up here because the indexer search picks up the IList indexer instead of the + // multidimensional indexer, which doesn't take the same number of arguments + else if (typeInfo.IsArray) + { + return GetValueFromArray((Array)target); + } return PerspexProperty.UnsetValue; } - private static PropertyInfo GetIndexer(TypeInfo typeInfo) + private object GetValueFromArray(Array array) { - PropertyInfo indexer; - // Check for the default indexer name first to make this faster. - // This will only be false when a class in VB has a custom indexer name. - if ((indexer = typeInfo.GetDeclaredProperty(CommonPropertyNames.IndexerName)) != null) + int[] intArgs; + if (!ConvertArgumentsToInts(out intArgs)) + return PerspexProperty.UnsetValue; + return GetValueFromArray(array, intArgs); + } + + private object GetValueFromArray(Array array, int[] indicies) + { + if (ValidBounds(indicies, array)) { - return indexer; + return array.GetValue(indicies); } - foreach (var property in typeInfo.DeclaredProperties) + return PerspexProperty.UnsetValue; + } + + private bool ConvertArgumentsToInts(out int[] intArgs) + { + intArgs = new int[Arguments.Count]; + for (int i = 0; i < Arguments.Count; ++i) { - if (property.GetIndexParameters().Any()) + object value; + if (!TypeUtilities.TryConvert(typeof(int), Arguments[i], CultureInfo.InvariantCulture, out value)) { - return property; + return false; } + intArgs[i] = (int)value; + } + return true; + } + + private static PropertyInfo GetIndexer(TypeInfo typeInfo) + { + PropertyInfo indexer; + for (;typeInfo != null; typeInfo = typeInfo.BaseType?.GetTypeInfo()) + { + // Check for the default indexer name first to make this faster. + // This will only be false when a class in VB has a custom indexer name. + if ((indexer = typeInfo.GetDeclaredProperty(CommonPropertyNames.IndexerName)) != null) + { + return indexer; + } + foreach (var property in typeInfo.DeclaredProperties) + { + if (property.GetIndexParameters().Any()) + { + return property; + } + } } return null; } - private bool InBounds(int[] args, Array array) + private bool ValidBounds(int[] indicies, Array array) { - if (args.Length == array.Rank) + if (indicies.Length == array.Rank) { - for (var i = 0; i < args.Length; ++i) + for (var i = 0; i < indicies.Length; ++i) { - if (args[i] >= array.GetLength(i)) + if (indicies[i] >= array.GetLength(i)) { return false; } diff --git a/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs b/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs index 6e6c3d21fd..21061d7075 100644 --- a/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs +++ b/src/Markup/Perspex.Markup/Data/Parsers/ArgumentListParser.cs @@ -9,35 +9,26 @@ namespace Perspex.Markup.Data.Parsers { internal static class ArgumentListParser { - public static IList Parse(Reader r, char open, char close) + public static IList Parse(Reader r, char open, char close) { if (r.Peek == open) { - var result = new List(); + var result = new List(); r.Take(); while (!r.End) { - var literal = LiteralParser.Parse(r); - - if (literal != null) + var builder = new StringBuilder(); + while (!r.End && r.Peek != ',' && r.Peek != close && !char.IsWhiteSpace(r.Peek)) { - result.Add(literal); + builder.Append(r.Take()); } - else + if (builder.Length == 0) { - var builder = new StringBuilder(); - while (!r.End && r.Peek != ',' && r.Peek != close) - { - builder.Append(r.Take()); - } - if (builder.Length == 0) - { - throw new ExpressionParseException(r.Position, "Expected indexer argument."); - } - result.Add(builder.ToString()); + throw new ExpressionParseException(r.Position, "Expected indexer argument."); } + result.Add(builder.ToString()); r.SkipWhitespace(); diff --git a/src/Markup/Perspex.Markup/Data/Parsers/LiteralParser.cs b/src/Markup/Perspex.Markup/Data/Parsers/LiteralParser.cs deleted file mode 100644 index b50e46f635..0000000000 --- a/src/Markup/Perspex.Markup/Data/Parsers/LiteralParser.cs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (c) The Perspex Project. All rights reserved. -// Licensed under the MIT license. See licence.md file in the project root for full license information. - -using System.Globalization; -using System.Text; - -namespace Perspex.Markup.Data.Parsers -{ - internal static class LiteralParser - { - public static object Parse(Reader r) - { - if (char.IsDigit(r.Peek)) - { - var result = new StringBuilder(); - var foundDecimal = false; - while (!r.End) - { - if (char.IsDigit(r.Peek)) - { - result.Append(r.Take()); - } - else if (!foundDecimal && r.Peek == '.') - { - result.Append(r.Take()); - foundDecimal = true; - } - else - { - break; - } - } - - if (!foundDecimal) - { - return int.Parse(result.ToString(), CultureInfo.InvariantCulture); - } - else - { - return result.ToString(); // Leave as a string to support double, float, and decimal indicies - } - } - - return null; - } - } -} diff --git a/src/Markup/Perspex.Markup/Perspex.Markup.csproj b/src/Markup/Perspex.Markup/Perspex.Markup.csproj index ba6e7d9331..f5b61e2cea 100644 --- a/src/Markup/Perspex.Markup/Perspex.Markup.csproj +++ b/src/Markup/Perspex.Markup/Perspex.Markup.csproj @@ -53,7 +53,6 @@ - diff --git a/tests/Perspex.Markup.UnitTests/Data/ExpressionNodeBuilderTests.cs b/tests/Perspex.Markup.UnitTests/Data/ExpressionNodeBuilderTests.cs index 33d8a6ff6c..3c2ff39873 100644 --- a/tests/Perspex.Markup.UnitTests/Data/ExpressionNodeBuilderTests.cs +++ b/tests/Perspex.Markup.UnitTests/Data/ExpressionNodeBuilderTests.cs @@ -77,7 +77,18 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal(2, result.Count); AssertIsProperty(result[0], "Foo"); - AssertIsIndexer(result[1], 15); + AssertIsIndexer(result[1], "15"); + Assert.IsType(result[1]); + } + + [Fact] + public void Should_Build_Indexed_Property_StringIndex() + { + var result = ToList(ExpressionNodeBuilder.Build("Foo[Key]")); + + Assert.Equal(2, result.Count); + AssertIsProperty(result[0], "Foo"); + AssertIsIndexer(result[1], "Key"); Assert.IsType(result[1]); } @@ -88,7 +99,7 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal(2, result.Count); AssertIsProperty(result[0], "Foo"); - AssertIsIndexer(result[1], 15, 6); + AssertIsIndexer(result[1], "15", "6"); } [Fact] @@ -98,7 +109,7 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal(2, result.Count); AssertIsProperty(result[0], "Foo"); - AssertIsIndexer(result[1], 5, 16); + AssertIsIndexer(result[1], "5", "16"); } [Fact] @@ -108,8 +119,8 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal(3, result.Count); AssertIsProperty(result[0], "Foo"); - AssertIsIndexer(result[1], 15); - AssertIsIndexer(result[2], 16); + AssertIsIndexer(result[1], "15"); + AssertIsIndexer(result[2], "16"); } [Fact] @@ -120,7 +131,7 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal(4, result.Count); AssertIsProperty(result[0], "Foo"); AssertIsProperty(result[1], "Bar"); - AssertIsIndexer(result[2], 5, 6); + AssertIsIndexer(result[2], "5", "6"); AssertIsProperty(result[3], "Baz"); } @@ -132,12 +143,12 @@ namespace Perspex.Markup.UnitTests.Data Assert.Equal(name, p.PropertyName); } - private void AssertIsIndexer(ExpressionNode node, params object[] args) + private void AssertIsIndexer(ExpressionNode node, params string[] args) { Assert.IsType(node); var e = (IndexerNode)node; - Assert.Equal(e.Arguments.ToArray(), args.ToArray()); + Assert.Equal(e.Arguments.ToArray(), args); } private List ToList(ExpressionNode node) From 069e6d9aca63d9a8547b7a7c9f28d78a1e83ad97 Mon Sep 17 00:00:00 2001 From: Jeremy Koritzinsky Date: Tue, 23 Feb 2016 22:31:49 -0600 Subject: [PATCH 6/6] Removed TODO comment. --- src/Markup/Perspex.Markup/Data/IndexerNode.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Markup/Perspex.Markup/Data/IndexerNode.cs b/src/Markup/Perspex.Markup/Data/IndexerNode.cs index 712adeebcf..7a6dd39301 100644 --- a/src/Markup/Perspex.Markup/Data/IndexerNode.cs +++ b/src/Markup/Perspex.Markup/Data/IndexerNode.cs @@ -114,7 +114,6 @@ namespace Perspex.Markup.Data var typeInfo = target.GetType().GetTypeInfo(); var list = target as IList; var dictionary = target as IDictionary; - //TODO: Implement array as special case. It doesn't have an indexer property. var indexerProperty = GetIndexer(typeInfo); var indexerParameters = indexerProperty?.GetIndexParameters(); if (indexerProperty != null && indexerParameters.Length == Arguments.Count)