From 16bc44cc17a9667b661461a2a614b35bc1dd83ef Mon Sep 17 00:00:00 2001 From: Stuart Date: Fri, 18 Jan 2013 15:20:05 +0000 Subject: [PATCH] Binding changes - to allow [] properties --- .../Auto/ExpressionExtensions.cs | 5 +- .../Binders/Json/MvxJsonBindingParser.cs | 1 + .../Json/MvxJsonBindingSpecification.cs | 1 + .../Binders/MvxFullBinding.cs | 3 + .../{ => Chained}/MvxChainedSourceBinding.cs | 24 +- .../Chained/MvxIndexerChainedSourceBinding.cs | 31 +++ .../Chained/MvxSimpleChainedSourceBinding.cs | 32 +++ .../Construction/MvxSourceBindingFactory.cs | 94 +++++-- .../PropertyTokens/IMvxPropertyTokeniser.cs | 16 ++ .../PropertyTokens/MvxBasePropertyToken.cs | 13 + .../PropertyTokens/MvxEmptyPropertyToken.cs | 13 + .../PropertyTokens/MvxIndexerPropertyToken.cs | 32 +++ .../MvxIntegerIndexerPropertyToken.cs | 17 ++ .../MvxPropertyNamePropertyToken.cs | 19 ++ .../PropertyTokens/MvxPropertyTokeniser.cs | 256 ++++++++++++++++++ .../MvxStringIndexerPropertyToken.cs | 17 ++ .../{ => Leaf}/MvxDirectToSourceBinding.cs | 2 +- ...MvxIndexerLeafPropertyInfoSourceBinding.cs | 27 ++ .../MvxLeafPropertyInfoSourceBinding.cs} | 15 +- .../MvxSimpleLeafPropertyInfoSourceBinding.cs | 27 ++ .../MvxBasePropertyInfoSourceBinding.cs | 19 ++ .../Cirrious.MvvmCross.Binding.csproj | 19 +- .../Construction/IMvxSourceBindingFactory.cs | 3 +- .../MvxSerializableBindingDescription.cs | 4 +- .../MvxBaseBindingBuilder.cs | 8 + 25 files changed, 653 insertions(+), 45 deletions(-) rename Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/{ => Chained}/MvxChainedSourceBinding.cs (83%) create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxIndexerChainedSourceBinding.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxSimpleChainedSourceBinding.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/IMvxPropertyTokeniser.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxBasePropertyToken.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxEmptyPropertyToken.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxIndexerPropertyToken.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxIntegerIndexerPropertyToken.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxPropertyNamePropertyToken.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxPropertyTokeniser.cs create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxStringIndexerPropertyToken.cs rename Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/{ => Leaf}/MvxDirectToSourceBinding.cs (94%) create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxIndexerLeafPropertyInfoSourceBinding.cs rename Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/{MvxPropertyInfoSourceBinding.cs => Leaf/MvxLeafPropertyInfoSourceBinding.cs} (81%) create mode 100644 Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxSimpleLeafPropertyInfoSourceBinding.cs diff --git a/Cirrious/Cirrious.MvvmCross.AutoView/Auto/ExpressionExtensions.cs b/Cirrious/Cirrious.MvvmCross.AutoView/Auto/ExpressionExtensions.cs index 018fc1258f..a3f167721e 100644 --- a/Cirrious/Cirrious.MvvmCross.AutoView/Auto/ExpressionExtensions.cs +++ b/Cirrious/Cirrious.MvvmCross.AutoView/Auto/ExpressionExtensions.cs @@ -8,6 +8,7 @@ using System; using System.Linq.Expressions; using System.Reflection; +using Cirrious.MvvmCross.Binding.Interfaces; using Cirrious.MvvmCross.Exceptions; using Cirrious.MvvmCross.ExtensionMethods; using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics; @@ -21,7 +22,7 @@ public static class ExpressionExtensions public static string CreateBindingText(this Expression> bindingExpression, string converter, string converterParameter) { - var binding = new Cirrious.MvvmCross.Binding.Binders.Json.MvxSerializableBindingDescription + var binding = new MvxSerializableBindingDescription { Path = bindingExpression.GetPropertyText(), Converter = converter, @@ -40,7 +41,7 @@ public static class ExpressionExtensions public static string CreateBindingText(this string path, string converter, string converterParameter) { - var binding = new Cirrious.MvvmCross.Binding.Binders.Json.MvxSerializableBindingDescription + var binding = new MvxSerializableBindingDescription { Path = path, Converter = converter, diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Binders/Json/MvxJsonBindingParser.cs b/Cirrious/Cirrious.MvvmCross.Binding/Binders/Json/MvxJsonBindingParser.cs index c878cd9d34..659501c9bb 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Binders/Json/MvxJsonBindingParser.cs +++ b/Cirrious/Cirrious.MvvmCross.Binding/Binders/Json/MvxJsonBindingParser.cs @@ -6,6 +6,7 @@ // Project Lead - Stuart Lodge, @slodge, me@slodge.com using System; +using Cirrious.MvvmCross.Binding.Interfaces; using Cirrious.MvvmCross.ExtensionMethods; using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics; using Cirrious.MvvmCross.Interfaces.ServiceProvider; diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Binders/Json/MvxJsonBindingSpecification.cs b/Cirrious/Cirrious.MvvmCross.Binding/Binders/Json/MvxJsonBindingSpecification.cs index 2035a66be5..d913b0189a 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Binders/Json/MvxJsonBindingSpecification.cs +++ b/Cirrious/Cirrious.MvvmCross.Binding/Binders/Json/MvxJsonBindingSpecification.cs @@ -6,6 +6,7 @@ // Project Lead - Stuart Lodge, @slodge, me@slodge.com using System.Collections.Generic; +using Cirrious.MvvmCross.Binding.Interfaces; namespace Cirrious.MvvmCross.Binding.Binders.Json { diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Binders/MvxFullBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Binders/MvxFullBinding.cs index 6b1e86b8d6..016f221426 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Binders/MvxFullBinding.cs +++ b/Cirrious/Cirrious.MvvmCross.Binding/Binders/MvxFullBinding.cs @@ -160,6 +160,8 @@ private void CreateTargetBinding(object target) } } +#warning where did this method come from - R# says not used so commented it out + /* private static void UpdateSourceFromTarget(MvxBindingRequest bindingRequest, IMvxSourceBinding sourceBinding, object value) { @@ -182,6 +184,7 @@ private void CreateTargetBinding(object target) exception.ToLongString()); } } + */ protected bool NeedToObserveSourceChanges { diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxChainedSourceBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxChainedSourceBinding.cs similarity index 83% rename from Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxChainedSourceBinding.cs rename to Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxChainedSourceBinding.cs index 4d8fdd50f8..1f54e6f0ae 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxChainedSourceBinding.cs +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxChainedSourceBinding.cs @@ -7,31 +7,29 @@ using System; using System.Collections.Generic; -using System.Linq; +using Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens; using Cirrious.MvvmCross.Binding.Interfaces.Bindings.Source; using Cirrious.MvvmCross.Binding.Interfaces.Bindings.Source.Construction; using Cirrious.MvvmCross.ExtensionMethods; using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics; using Cirrious.MvvmCross.Interfaces.ServiceProvider; -namespace Cirrious.MvvmCross.Binding.Bindings.Source +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Chained { - public class MvxChainedSourceBinding + public abstract class MvxChainedSourceBinding : MvxBasePropertyInfoSourceBinding , IMvxServiceConsumer { - private readonly List _childPropertyNames; + private readonly IList _childTokens; private IMvxSourceBinding _currentChildBinding; - public MvxChainedSourceBinding( + protected MvxChainedSourceBinding( object source, string propertyName, - IEnumerable childPropertyNames) + IList childTokens) : base(source, propertyName) { - _childPropertyNames = childPropertyNames.ToList(); - - UpdateChildBinding(); + _childTokens = childTokens; } protected override void Dispose(bool isDisposing) @@ -58,7 +56,7 @@ public override Type SourceType get { throw new NotImplementedException(); } } - private void UpdateChildBinding() + protected void UpdateChildBinding() { if (_currentChildBinding != null) { @@ -72,7 +70,7 @@ private void UpdateChildBinding() return; } - var currentValue = PropertyInfo.GetValue(Source, null); + var currentValue = PropertyInfo.GetValue(Source, PropertyIndexParameters()); if (currentValue == null) { // value will be missing... so end consumer will need to use fallback values @@ -80,11 +78,13 @@ private void UpdateChildBinding() } else { - _currentChildBinding = SourceBindingFactory.CreateBinding(currentValue, _childPropertyNames); + _currentChildBinding = SourceBindingFactory.CreateBinding(currentValue, _childTokens); _currentChildBinding.Changed += ChildSourceBindingChanged; } } + protected abstract object[] PropertyIndexParameters(); + private void ChildSourceBindingChanged(object sender, MvxSourcePropertyBindingEventArgs e) { FireChanged(e); diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxIndexerChainedSourceBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxIndexerChainedSourceBinding.cs new file mode 100644 index 0000000000..e0cc7f720e --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxIndexerChainedSourceBinding.cs @@ -0,0 +1,31 @@ +// MvxIndexerChainedSourceBinding.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +using System.Collections.Generic; +using Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens; + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Chained +{ + public class MvxIndexerChainedSourceBinding + : MvxChainedSourceBinding + { + private readonly MvxIndexerPropertyToken _indexerPropertyToken; + + public MvxIndexerChainedSourceBinding(object source, MvxIndexerPropertyToken indexerPropertyToken, + IList childTokens) + : base(source, "Item", childTokens) + { + _indexerPropertyToken = indexerPropertyToken; + UpdateChildBinding(); + } + + protected override object[] PropertyIndexParameters() + { + return new[] {_indexerPropertyToken.Key}; + } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxSimpleChainedSourceBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxSimpleChainedSourceBinding.cs new file mode 100644 index 0000000000..8c9af1c9d9 --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Chained/MvxSimpleChainedSourceBinding.cs @@ -0,0 +1,32 @@ +// MvxSimpleChainedSourceBinding.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +using System.Collections.Generic; +using Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens; + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Chained +{ + public class MvxSimpleChainedSourceBinding + : MvxChainedSourceBinding + { + private readonly MvxPropertyNamePropertyToken _propertyNamePropertyToken; + + public MvxSimpleChainedSourceBinding( + object source, + MvxPropertyNamePropertyToken propertyNamePropertyToken, + IList childTokens) + : base(source, propertyNamePropertyToken.PropertyName, childTokens) + { + _propertyNamePropertyToken = propertyNamePropertyToken; + } + + protected override object[] PropertyIndexParameters() + { + return null; + } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/MvxSourceBindingFactory.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/MvxSourceBindingFactory.cs index d7996ec8c5..3cf41a0044 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/MvxSourceBindingFactory.cs +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/MvxSourceBindingFactory.cs @@ -5,17 +5,38 @@ // // Project Lead - Stuart Lodge, @slodge, me@slodge.com -using System; using System.Collections.Generic; using System.Linq; +using Cirrious.MvvmCross.Binding.Bindings.Source.Chained; +using Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens; +using Cirrious.MvvmCross.Binding.Bindings.Source.Leaf; using Cirrious.MvvmCross.Binding.Interfaces.Bindings.Source; using Cirrious.MvvmCross.Binding.Interfaces.Bindings.Source.Construction; +using Cirrious.MvvmCross.Exceptions; +using Cirrious.MvvmCross.ExtensionMethods; +using Cirrious.MvvmCross.Interfaces.ServiceProvider; namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction { - public class MvxSourceBindingFactory : IMvxSourceBindingFactory + public class MvxSourceBindingFactory + : IMvxSourceBindingFactory + , IMvxServiceConsumer { - private static readonly char[] FieldSeparator = new[] {'.'}; + private static readonly char[] FieldSeparator = new[] {'.', '['}; + + private IMvxPropertyTokeniser _propertyTokeniser; + + private IMvxPropertyTokeniser PropertyTokeniser + { + get + { + if (_propertyTokeniser == null) + { + _propertyTokeniser = this.GetService(); + } + return _propertyTokeniser; + } + } #region IMvxSourceBindingFactory Members @@ -24,26 +45,65 @@ public IMvxSourceBinding CreateBinding(object source, string combinedPropertyNam if (combinedPropertyName == null) combinedPropertyName = ""; - return CreateBinding(source, - combinedPropertyName.Split(FieldSeparator, StringSplitOptions.RemoveEmptyEntries)); + var tokens = PropertyTokeniser.Tokenise(combinedPropertyName); + var queue = new List(tokens); + return CreateBinding(source, queue); + } + +#warning CreateBinding should probably take an IList to make the code run quicker! + public IMvxSourceBinding CreateBinding(object source, IList tokens) + { + if (tokens == null || tokens.Count == 0) + { + throw new MvxException("empty token list passed to CreateBinding"); + } + + var currentToken = tokens[0]; + if (tokens.Count == 1) + { + return CreateLeafBinding(source, currentToken); + } + else + { + var remainingTokens = tokens.Skip(1).ToList(); + return CreateChainedBinding(source, currentToken, remainingTokens); + } } - public IMvxSourceBinding CreateBinding(object source, IEnumerable childPropertyNames) + private static MvxChainedSourceBinding CreateChainedBinding(object source, MvxBasePropertyToken propertyToken, + List remainingTokens) { - var childPropertyNameList = childPropertyNames.ToList(); + if (propertyToken is MvxIndexerPropertyToken) + { + return new MvxIndexerChainedSourceBinding(source, (MvxIndexerPropertyToken) propertyToken, + remainingTokens); + } + else if (propertyToken is MvxPropertyNamePropertyToken) + { + return new MvxSimpleChainedSourceBinding(source, (MvxPropertyNamePropertyToken) propertyToken, + remainingTokens); + } + + throw new MvxException("Unexpected property chaining - seen token type {0}", + propertyToken.GetType().FullName); + } - switch (childPropertyNameList.Count) + private static IMvxSourceBinding CreateLeafBinding(object source, MvxBasePropertyToken propertyToken) + { + if (propertyToken is MvxIndexerPropertyToken) + { + return new MvxIndexerLeafPropertyInfoSourceBinding(source, (MvxIndexerPropertyToken) propertyToken); + } + else if (propertyToken is MvxPropertyNamePropertyToken) { - case 0: - return new MvxDirectToSourceBinding(source); - case 1: - return new MvxPropertyInfoSourceBinding(source, - childPropertyNameList.DefaultIfEmpty(string.Empty) - .FirstOrDefault()); - default: - return new MvxChainedSourceBinding(source, childPropertyNameList.First(), - childPropertyNameList.Skip(1)); + return new MvxSimpleLeafPropertyInfoSourceBinding(source, (MvxPropertyNamePropertyToken) propertyToken); } + else if (propertyToken is MvxEmptyPropertyToken) + { + return new MvxDirectToSourceBinding(source); + } + + throw new MvxException("Unexpected property source - seen token type {0}", propertyToken.GetType().FullName); } #endregion diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/IMvxPropertyTokeniser.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/IMvxPropertyTokeniser.cs new file mode 100644 index 0000000000..445e703e69 --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/IMvxPropertyTokeniser.cs @@ -0,0 +1,16 @@ +// IMvxPropertyTokeniser.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +using System.Collections.Generic; + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens +{ + public interface IMvxPropertyTokeniser + { + IList Tokenise(string toParse); + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxBasePropertyToken.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxBasePropertyToken.cs new file mode 100644 index 0000000000..9daecaa4e1 --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxBasePropertyToken.cs @@ -0,0 +1,13 @@ +// MvxBasePropertyToken.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens +{ + public class MvxBasePropertyToken + { + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxEmptyPropertyToken.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxEmptyPropertyToken.cs new file mode 100644 index 0000000000..4e4e96b946 --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxEmptyPropertyToken.cs @@ -0,0 +1,13 @@ +// MvxEmptyPropertyToken.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens +{ + public class MvxEmptyPropertyToken : MvxBasePropertyToken + { + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxIndexerPropertyToken.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxIndexerPropertyToken.cs new file mode 100644 index 0000000000..b55c7e4203 --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxIndexerPropertyToken.cs @@ -0,0 +1,32 @@ +// MvxIndexerPropertyToken.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens +{ + public class MvxIndexerPropertyToken : MvxBasePropertyToken + { + protected MvxIndexerPropertyToken(object key) + { + Key = key; + } + + public object Key { get; private set; } + } + + public class MvxIndexerPropertyToken : MvxIndexerPropertyToken + { + protected MvxIndexerPropertyToken(T key) + : base(key) + { + } + + public new T Key + { + get { return (T) base.Key; } + } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxIntegerIndexerPropertyToken.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxIntegerIndexerPropertyToken.cs new file mode 100644 index 0000000000..8d5cc23ae0 --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxIntegerIndexerPropertyToken.cs @@ -0,0 +1,17 @@ +// MvxIntegerIndexerPropertyToken.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens +{ + public class MvxIntegerIndexerPropertyToken : MvxIndexerPropertyToken + { + public MvxIntegerIndexerPropertyToken(int key) + : base(key) + { + } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxPropertyNamePropertyToken.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxPropertyNamePropertyToken.cs new file mode 100644 index 0000000000..f532a4ac9b --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxPropertyNamePropertyToken.cs @@ -0,0 +1,19 @@ +// MvxPropertyNamePropertyToken.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens +{ + public class MvxPropertyNamePropertyToken : MvxBasePropertyToken + { + public MvxPropertyNamePropertyToken(string propertyText) + { + PropertyName = propertyText; + } + + public string PropertyName { get; private set; } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxPropertyTokeniser.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxPropertyTokeniser.cs new file mode 100644 index 0000000000..6446400a16 --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxPropertyTokeniser.cs @@ -0,0 +1,256 @@ +// MvxPropertyTokeniser.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +using System.Collections.Generic; +using System.Text; +using Cirrious.MvvmCross.Exceptions; + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens +{ + public class MvxPropertyTokeniser : IMvxPropertyTokeniser + { + private string _fullText; + private int _currentIndex; + private List _currentResult; + + public IList Tokenise(string toParse) + { + _fullText = toParse; + _currentIndex = 0; + _currentResult = new List(); + + while (!IsComplete) + { + ParseNextToken(); + } + + if (_currentResult.Count == 0) + { + _currentResult.Add(new MvxEmptyPropertyToken()); + } + + return _currentResult; + } + + private void ParseNextToken() + { + SkipWhitespaceAndPeriods(); + + if (IsComplete) + { + return; + } + + if (CurrentChar == '[') + { + ParseIndexer(); + } + else if (char.IsLetter(CurrentChar)) + { + ParsePropertyName(); + } + else + { + throw new MvxException("Unexpected character {0} at position {1} in property text {2}", CurrentChar, + _currentIndex, _fullText); + } + } + + private void ParsePropertyName() + { + var propertyText = new StringBuilder(); + while (!IsComplete && char.IsLetterOrDigit(CurrentChar)) + { + propertyText.Append(CurrentChar); + MoveNext(); + } + _currentResult.Add(new MvxPropertyNamePropertyToken(propertyText.ToString())); + } + + private void ParseIndexer() + { + if (CurrentChar != '[') + { + throw new MvxException( + "Internal error - ParseIndexer should only be called with a string starting with ["); + } + + MoveNext(); + if (IsComplete) + { + throw new MvxException("Invalid indexer property text {0}", _fullText); + } + + SkipWhitespaceAndPeriods(); + + if (IsComplete) + { + throw new MvxException("Invalid indexer property text {0}", _fullText); + } + + if (CurrentChar == '\'' || CurrentChar == '\"') + { + ParseStringIndexer(); + } + else if (char.IsDigit(CurrentChar)) + { + ParseIntegerIndexer(); + } + else + { + throw new MvxException( + "Unexpected character {0} at position {1} in property text {2} - expected terminator start of string or number", + CurrentChar, _currentIndex, _fullText); + } + + SkipWhitespaceAndPeriods(); + if (IsComplete) + { + throw new MvxException("Invalid termination of indexer property text in {0}", _fullText); + } + + if (CurrentChar != ']') + { + throw new MvxException( + "Unexpected character {0} at position {1} in property text {2} - expected terminator", CurrentChar, + _currentIndex, _fullText); + } + + MoveNext(); + } + + private void ParseIntegerIndexer() + { + var integerStringBuilder = new StringBuilder(); + while (!IsComplete && char.IsDigit(CurrentChar)) + { + integerStringBuilder.Append(CurrentChar); + MoveNext(); + } + int index; + var integerText = integerStringBuilder.ToString(); + if (!int.TryParse(integerText, out index)) + { + throw new MvxException("Unable to parse integer text from {0} in {1}", integerText, _fullText); + } + + _currentResult.Add(new MvxIntegerIndexerPropertyToken(index)); + } + + private void ParseStringIndexer() + { + bool nextCharacterEscaped = false; + char quoteCharacter = CurrentChar; + + if (quoteCharacter != '\'' && quoteCharacter != '\"') + { + throw new MvxException("Error parsing string indexer - unexpected quote character {0} in text {1}", + quoteCharacter, _fullText); + } + + MoveNext(); + if (IsComplete) + { + throw new MvxException("Error parsing string indexer - unterminated in text {0}", _fullText); + } + + var textBuilder = new StringBuilder(); + while (true) + { + if (IsComplete) + { + throw new MvxException("Error parsing string indexer - unterminated in text {0}", _fullText); + } + + var currentChar = CurrentChar; + + if (nextCharacterEscaped) + { + char charToInsert; + switch (currentChar) + { +#warning Check this list! Possibly also need unicode codes and other stuff here? + case 't': + charToInsert = '\t'; + break; + case 'r': + charToInsert = '\r'; + break; + case 'n': + charToInsert = '\n'; + break; + case '\'': + charToInsert = '\''; + break; + case '\"': + charToInsert = '\"'; + break; + case '\\': + charToInsert = '\\'; + break; + default: + throw new MvxException("Sorry we don't currently support escaped characters like \\{0}", + currentChar); + } + textBuilder.Append(charToInsert); + nextCharacterEscaped = false; + MoveNext(); + continue; + } + + if (currentChar == '\\') + { + nextCharacterEscaped = true; + MoveNext(); + continue; + } + + if (currentChar == quoteCharacter) + { + MoveNext(); + break; + } + + textBuilder.Append(currentChar); + MoveNext(); + } + + var text = textBuilder.ToString(); + _currentResult.Add(new MvxStringIndexerPropertyToken(text)); + } + + private void SkipWhitespaceAndPeriods() + { + while (!IsComplete + && IsWhiteSpaceOrPeriod(CurrentChar)) + { + MoveNext(); + } + } + + private static bool IsWhiteSpaceOrPeriod(char toTest) + { + return char.IsWhiteSpace(toTest) || toTest == '.'; + } + + private bool MoveNext() + { + _currentIndex++; + return IsComplete; + } + + private bool IsComplete + { + get { return _currentIndex >= _fullText.Length; } + } + + private char CurrentChar + { + get { return _fullText[_currentIndex]; } + } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxStringIndexerPropertyToken.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxStringIndexerPropertyToken.cs new file mode 100644 index 0000000000..164b8ce442 --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Construction/PropertyTokens/MvxStringIndexerPropertyToken.cs @@ -0,0 +1,17 @@ +// MvxStringIndexerPropertyToken.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens +{ + public class MvxStringIndexerPropertyToken : MvxIndexerPropertyToken + { + public MvxStringIndexerPropertyToken(string key) + : base(key) + { + } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxDirectToSourceBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxDirectToSourceBinding.cs similarity index 94% rename from Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxDirectToSourceBinding.cs rename to Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxDirectToSourceBinding.cs index f31b5b11eb..9951405f6b 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxDirectToSourceBinding.cs +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxDirectToSourceBinding.cs @@ -8,7 +8,7 @@ using System; using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics; -namespace Cirrious.MvvmCross.Binding.Bindings.Source +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Leaf { public class MvxDirectToSourceBinding : MvxBaseSourceBinding { diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxIndexerLeafPropertyInfoSourceBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxIndexerLeafPropertyInfoSourceBinding.cs new file mode 100644 index 0000000000..817af7682b --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxIndexerLeafPropertyInfoSourceBinding.cs @@ -0,0 +1,27 @@ +// MvxIndexerLeafPropertyInfoSourceBinding.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +using Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens; + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Leaf +{ + public class MvxIndexerLeafPropertyInfoSourceBinding : MvxLeafPropertyInfoSourceBinding + { + private readonly object _key; + + public MvxIndexerLeafPropertyInfoSourceBinding(object source, MvxIndexerPropertyToken indexToken) + : base(source, "Item") + { + _key = indexToken.Key; + } + + protected override object[] PropertyIndexParameters() + { + return new[] {_key}; + } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxPropertyInfoSourceBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxLeafPropertyInfoSourceBinding.cs similarity index 81% rename from Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxPropertyInfoSourceBinding.cs rename to Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxLeafPropertyInfoSourceBinding.cs index 9177f90192..9b078ba9c2 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxPropertyInfoSourceBinding.cs +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxLeafPropertyInfoSourceBinding.cs @@ -1,4 +1,4 @@ -// MvxPropertyInfoSourceBinding.cs +// MvxLeafPropertyInfoSourceBinding.cs // (c) Copyright Cirrious Ltd. http://www.cirrious.com // MvvmCross is licensed using Microsoft Public License (Ms-PL) // Contributions and inspirations noted in readme.md and license.txt @@ -11,11 +11,11 @@ using Cirrious.MvvmCross.ExtensionMethods; using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics; -namespace Cirrious.MvvmCross.Binding.Bindings.Source +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Leaf { - public class MvxPropertyInfoSourceBinding : MvxBasePropertyInfoSourceBinding + public abstract class MvxLeafPropertyInfoSourceBinding : MvxBasePropertyInfoSourceBinding { - public MvxPropertyInfoSourceBinding(object source, string propertyName) + protected MvxLeafPropertyInfoSourceBinding(object source, string propertyName) : base(source, propertyName) { } @@ -45,10 +45,13 @@ public override bool TryGetValue(out object value) return false; } - value = PropertyInfo.GetValue(Source, null); + value = PropertyInfo.GetValue(Source, PropertyIndexParameters()); + return true; } + protected abstract object[] PropertyIndexParameters(); + public override void SetValue(object value) { if (PropertyInfo == null) @@ -68,7 +71,7 @@ public override void SetValue(object value) { var propertyType = PropertyInfo.PropertyType; var safeValue = propertyType.MakeSafeValue(value); - PropertyInfo.SetValue(Source, safeValue, null); + PropertyInfo.SetValue(Source, safeValue, PropertyIndexParameters()); } catch (Exception exception) { diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxSimpleLeafPropertyInfoSourceBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxSimpleLeafPropertyInfoSourceBinding.cs new file mode 100644 index 0000000000..d875d603fd --- /dev/null +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/Leaf/MvxSimpleLeafPropertyInfoSourceBinding.cs @@ -0,0 +1,27 @@ +// MvxSimpleLeafPropertyInfoSourceBinding.cs +// (c) Copyright Cirrious Ltd. http://www.cirrious.com +// MvvmCross is licensed using Microsoft Public License (Ms-PL) +// Contributions and inspirations noted in readme.md and license.txt +// +// Project Lead - Stuart Lodge, @slodge, me@slodge.com + +using Cirrious.MvvmCross.Binding.Bindings.Source.Construction.PropertyTokens; + +namespace Cirrious.MvvmCross.Binding.Bindings.Source.Leaf +{ + public class MvxSimpleLeafPropertyInfoSourceBinding : MvxLeafPropertyInfoSourceBinding + { + private readonly MvxPropertyNamePropertyToken _token; + + public MvxSimpleLeafPropertyInfoSourceBinding(object source, MvxPropertyNamePropertyToken token) + : base(source, token.PropertyName) + { + _token = token; + } + + protected override object[] PropertyIndexParameters() + { + return null; + } + } +} \ No newline at end of file diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxBasePropertyInfoSourceBinding.cs b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxBasePropertyInfoSourceBinding.cs index 7c6f317c86..3dd5a90bf6 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxBasePropertyInfoSourceBinding.cs +++ b/Cirrious/Cirrious.MvvmCross.Binding/Bindings/Source/MvxBasePropertyInfoSourceBinding.cs @@ -5,7 +5,9 @@ // // Project Lead - Stuart Lodge, @slodge, me@slodge.com +using System.Collections.Generic; using System.ComponentModel; +using System.Linq; using System.Reflection; using Cirrious.MvvmCross.Interfaces.Platform.Diagnostics; @@ -19,6 +21,7 @@ public abstract class MvxBasePropertyInfoSourceBinding : MvxBaseSourceBinding protected MvxBasePropertyInfoSourceBinding(object source, string propertyName) : base(source) { + var dict = new Dictionary(); _propertyName = propertyName; if (Source == null) @@ -51,11 +54,27 @@ protected string PropertyName get { return _propertyName; } } + protected string PropertyNameForChangedEvent + { + get + { + if (IsIndexedProperty) + return _propertyName + "[]"; + else + return _propertyName; + } + } + protected PropertyInfo PropertyInfo { get { return _propertyInfo; } } + protected bool IsIndexedProperty + { + get { return _propertyInfo.GetIndexParameters().Any(); } + } + protected override void Dispose(bool isDisposing) { if (isDisposing) diff --git a/Cirrious/Cirrious.MvvmCross.Binding/Cirrious.MvvmCross.Binding.csproj b/Cirrious/Cirrious.MvvmCross.Binding/Cirrious.MvvmCross.Binding.csproj index 811348aa7c..217a9e22fb 100644 --- a/Cirrious/Cirrious.MvvmCross.Binding/Cirrious.MvvmCross.Binding.csproj +++ b/Cirrious/Cirrious.MvvmCross.Binding/Cirrious.MvvmCross.Binding.csproj @@ -40,6 +40,18 @@ + + + + + + + + + + + + @@ -53,9 +65,9 @@ - - - + + + @@ -99,6 +111,7 @@ Cirrious.MvvmCross.Plugins.Json +