public bool ApplyIfRegistered(MethodInfo methodCalledInExpression, ref TraversalOptions options, IEnumerable <Expression> argumentExpressions) { //Skip the first argument, which is the 'this' parameter of a marker extensio method. var arguments = argumentExpressions.Skip(1).Materialize(); return(ApplyIfRegistered(methodCalledInExpression, ref options, arguments)); }
public static TraversalOptions TrackItems(TraversalOptions options, PropertyTrackingInfo propertiesTracked) { options.CollectionTrackOptions = CollectionTrackOptions.TrackItems; options.Properties = propertiesTracked; options.TrackLastChild = true; //this is usually required by the logic of expressions return(options); }
public static TraversalOptions TrackItems(TraversalOptions options, LambdaExpression[] propertiesTracked) { //The line beneath is also a place, where the array of tracked properties is copied (it must be, //so that tracked properties cannot be changed from outside). string[] propertiesTrackedAsStrings = propertiesTracked.Select(ExpressionHelper.GetNameFromUntypedParameterExpression).ToArray(); return(TrackItems(options, propertiesTrackedAsStrings)); }
public void Apply(MethodInfo methodCalledInExpression, ref TraversalOptions options, IEnumerable <object> arguments) { if (!ApplyIfRegistered(methodCalledInExpression, ref options, arguments)) { throw new ArgumentException("Specified marker method is not registered", "methodCalledInExpression"); } }
private static void HandleFieldConstant(Set <UsedPropertyChain> container, MemberExpression memberExpression, ChildInformation child, TraversalOptions options) { Func <object> constantObjectRetriever = CompileMemberExpression(memberExpression); HandleFieldConstant(container, constantObjectRetriever, child, options); }
/// <summary> /// Initializes a new instance of the <see cref="T:UsedSubproperty"/> class. /// </summary> internal UsedSubproperty(string name, Func <INotifyPropertyChanged> parentRetriever, UsedSubproperty child, TraversalOptions traversalOptions) : base(name, child, traversalOptions) { if (parentRetriever == null) { throw new ArgumentNullException("parentRetriever"); } this.parentRetriever = parentRetriever; }
/// <summary> /// Initializes a new instance of the <see cref="T:UsedProperty"/> class. /// </summary> internal UsedPropertyChain(INotifyPropertyChanged parent, string name, UsedSubproperty child, TraversalOptions traversalOptions) : base(name, child, traversalOptions) { if (parent == null) { throw new ArgumentNullException("target"); } this.Parent = parent; this.TopLevelParent = this; }
/// <summary> /// Initializes a new instance of the <see cref="T:UsePropertyBase"/> class. /// </summary> internal UsedPropertyBase(string name, UsedSubproperty child, TraversalOptions traversalOptions) { if (name == null) { throw new ArgumentNullException("name"); } this.Child = child; this.Name = name; this.tracedItems = new List <INotifyPropertyChanged>(); this.TraversalOptions = traversalOptions; this.cyclicAccessGuard = new CyclicAccessGuard <AccessTrace>(1, cyclicAccessRecord); }
public bool ApplyIfRegistered(MethodInfo methodCalledInExpression, ref TraversalOptions options, IEnumerable <object> arguments) { //Delegate effect; //if (register.TryGetValue(methodCalledInExpression, out effect)) // options = InvokeMarkerMethod(effect, options, arguments); foreach (var pair in register) { if (AreMethodInfosPointingTheSameMethod(pair.Key, methodCalledInExpression)) { Delegate effect = pair.Value; options = InvokeMarkerMethod(effect, options, arguments); return(true); } } return(false); }
private static void HandleFieldConstant(Set <UsedPropertyChain> container, Func <object> constantObjectRetriever, ChildInformation child, TraversalOptions options) { INotifyPropertyChanged notifiable; Func <object> targetRetriever; if (child.Property != null) { //This occurs in case of expression like: //React.To(() => recursive.PropertyA).Set(...); //or with marker methods: //React.To(() => recursive.TrackFields().PropertyA).Set(...); notifiable = child.Property.GetCurrentParent(); targetRetriever = constantObjectRetriever; } else { //This case occurs, when a method is called on a tracked field, e.g.: //React.To(() => recursive.TrackFields().GetInner().PropertyA).Set(...); //The call to GetInner() breaks the chain and there is no tracked child //for the 'recursive' object. However, this is still a viable case; //if recursive is INPC, track all properties on it, otherwise track nothing. Func <object> parentRetriever = constantObjectRetriever; notifiable = parentRetriever() as INotifyPropertyChanged; targetRetriever = parentRetriever; } if (notifiable != null && child.IsTrackable) //property and method children are trackable { //Do not allow fields here, because they cannot be tracked anyhow. //string.Empty here as the property name does not mean all properties //of the associated object will be tracked. It only means that associated //object is stored in a field and changes to that field cannot be tracked, //just because it's not a property. Thus, string.Empty indicates lack //of any property name. var chain = new UsedPropertyChain(notifiable, string.Empty, child.Property, options); chain.TargetRetriever = targetRetriever; container.Add(chain); } //Otherwise, track nothing }
private static UsedSubproperty GetContinuationChainLink( MemberExpression memberExpression, ChildInformation child, TraversalOptions options) { Func <INotifyPropertyChanged> parentRetriever = CompileObjectFactoryExpressionINPC(memberExpression.Expression); if (parentRetriever == null) { //This happens when parentMember refers to a non-observable collection return(null); } var usedSub = new UsedSubproperty(memberExpression.Member.Name, parentRetriever, child.Property, options); usedSub.TargetRetriever = CompileMemberExpression(memberExpression); return(usedSub); }
internal static UsedPropertyChain[] GetUsedProperties <T>( Expression <Func <T> > valueExpression, ExpressionPreconfiguration preconfiguration, bool attachHandlersImmediately ) { var set = new Set <UsedPropertyChain>(); TraversalOptions options = preconfiguration != null ? preconfiguration.Options : TraversalOptions.Default(); GetUsedProperties(set, valueExpression, ChildInformation.Null, options); UsedPropertyChain[] array = set.ToArray(); foreach (var up in array) { up.TopLevelParent = up; if (attachHandlersImmediately) { up.EnsureHandlersAreAttached(); } } return(array); }
/// <summary> /// Marks the follow-up expressions /// so that changes applied to any subproperty /// of the last child property should be NOT tracked as well. /// </summary> /// <returns>The same instance of the <see cref="ExpressionPreconfiguration"/> /// type, which holds preconfiguration options</returns> public ExpressionPreconfiguration DoNotTrackLastChild() { this.Options = MarkerMethods.DoNotTrackLastChild(this.Options); return(this); }
private TraversalOptions InvokeMarkerMethod(Delegate effect, TraversalOptions inputOptions, IEnumerable <object> arguments) { var allArguments = Enumerable.Concat(new object[] { inputOptions }, arguments ?? emptyArgs).ToArray(); return((TraversalOptions)effect.DynamicInvoke(allArguments)); }
private static void GetUsedProperties(Set <UsedPropertyChain> container, MemberExpression memberExpression, ChildInformation child, TraversalOptions options) { if (memberExpression == null) { return; } MemberTypes memberType = memberExpression.Member.MemberType; if (memberType != MemberTypes.Property) { if (memberType == MemberTypes.Field) { if (options.TrackFields) { var constant = memberExpression.Expression as ConstantExpression; if (constant != null) { HandleFieldConstant(container, memberExpression, child, options); return; } if (IsFieldChain(memberExpression) && child.IsTrackable) { //Occurs with a.b.c.A kind of expression, where //the field chain is the leading part of the //expression and the constant can be extracted from //it. The constant is taken from evaluation of a.b.c HandleFieldConstant(container, memberExpression, child, options); return; } else { //Dead end. It happens, when a field occurs //after a non-constant element, e.g. after //a property reference as in 'A.B.field.C' //'A.B' is not ConstantExpression and therefore, //even with TrackFields on, the field cannot be //tracked any further. However, the preceding //part of the expression can and should be //tracked. //Break the chain (without continuation) GetUsedProperties(container, memberExpression.Expression, ChildInformation.FieldBreak, options); return; } } else { //Break the chain (without continuation) GetUsedProperties(container, memberExpression.Expression, ChildInformation.FieldBreak, options); return; } } throw new InvalidOperationException("Unhandled case. Revise the code"); } var parentMember = memberExpression.Expression as MemberExpression; if (parentMember != null) { UsedSubproperty usedSubProperty = GetContinuationChainLink(memberExpression, child, options); var usedChild = new ChildInformation(usedSubProperty); //Continue the chain GetUsedProperties(container, parentMember, usedChild, options); } else { if (memberExpression.Expression is ParameterExpression) { //Presumably, we are within a nested lambda parameter block. //Skip, just for now. If there's a nested lambda like "c=>c.property" it //is pretty complicated to know when to update. //TODO: return; } var constant = memberExpression.Expression as ConstantExpression; if (constant != null) { var notifiable = constant.Value as INotifyPropertyChanged; if (notifiable != null) { string propertyName = memberExpression.Member.Name; var chain = new UsedPropertyChain(notifiable, propertyName, child.Property, options); chain.TargetRetriever = CompileMemberExpression(memberExpression); container.Add(chain); } else { //Even now add a chain to the container. Fields are not traced, but their //properties can be. This applies only to cases where a the left-hand part //of an expression is a field or a field/constant chain. HandleFieldConstant(container, memberExpression, child, options); } return; } else { UsedSubproperty usedSubProperty = GetContinuationChainLink(memberExpression, child, options); var usedChild = new ChildInformation(usedSubProperty); //This should break the notification chain or continue, if it's marker method. GetUsedProperties(container, memberExpression.Expression, usedChild, options); } } }
/// <summary> /// /// </summary> /// <param name="container"></param> /// <param name="expression"></param> /// <param name="options"></param> /// <param name="continuedChild">The sub-chain that might be broken by /// a marker method call.</param> private static void GetUsedProperties(Set <UsedPropertyChain> container, Expression expression, ChildInformation continuedChild, TraversalOptions options) { if (expression == null) { return; //e.g. in case of static method calls object is null } var lambda = expression as LambdaExpression; if (lambda != null) { GetUsedProperties(container, lambda.Body, continuedChild, options); return; } var member = expression as MemberExpression; if (member != null) { GetUsedProperties(container, member, continuedChild, options); return; } if (expression is ConstantExpression) { //Not traceable, nothing to do / case handled elsewhere return; } var binary = expression as BinaryExpression; if (binary != null) { GetUsedProperties(container, binary.Left, continuedChild, options); GetUsedProperties(container, binary.Right, continuedChild, options); return; } var typeBinaryExpression = expression as TypeBinaryExpression; if (typeBinaryExpression != null) //is operator { GetUsedProperties(container, typeBinaryExpression.Expression, continuedChild, options); return; } var unary = expression as UnaryExpression; if (unary != null) { GetUsedProperties(container, unary.Operand, continuedChild, options); return; } if (expression is ParameterExpression) { //Presumably, we are within a nested lambda parameter block. //Skip, just for now. If there's a nested lambda like "c=>c.property" it //is pretty complicated to know when to update. //TODO: return; } var methodCall = expression as MethodCallExpression; if (methodCall != null) { IEnumerable <Expression> arguments; bool isMarkerMethod = CheckForMarkerMethodCalls(methodCall, ref options); if (!isMarkerMethod) //required by expression logic { //ApplyMarkerCall(ObservableExpression.TrackLastChild, ref options); //disallow continuation if it is not a marker method continuedChild = ChildInformation.MethodBreak; //take all arguments arguments = methodCall.Arguments; } else { //Take only the first argument, which is the 'this' parameter //of the (marker) extension method. Do *not* further process other //arguments of marker methods, because they can be also expressions //in general, but as for now their meaning is totally different than //in React.To(...). arguments = methodCall.Arguments.Take(1); } GetUsedProperties(container, methodCall.Object, continuedChild, options); foreach (var arg in arguments) { GetUsedProperties(container, arg, continuedChild, options); } return; } var conditional = expression as ConditionalExpression; if (conditional != null) { GetUsedProperties(container, conditional.Test, continuedChild, options); GetUsedProperties(container, conditional.IfFalse, continuedChild, options); GetUsedProperties(container, conditional.IfTrue, continuedChild, options); return; } var invocation = expression as InvocationExpression; if (invocation != null) { continuedChild = ChildInformation.MethodBreak; GetUsedProperties(container, invocation.Expression, continuedChild, options); foreach (var arg in invocation.Arguments) { GetUsedProperties(container, arg, continuedChild, options); } return; } //var newArray = expression as NewArrayExpression; //if (newArray != null) //{ // return; //} string msg = string.Format( "Expressions of type {0} are not supported.", expression.GetType()); throw new NotSupportedException(msg); }
public static TraversalOptions DoNotTrackLastChild(TraversalOptions options) { options.TrackLastChild = false; return(options); }
public static TraversalOptions DoNotTrackFields(TraversalOptions options) { options.TrackFields = false; return(options); }
private static bool CheckForMarkerMethodCalls(MethodCallExpression methodCall, ref TraversalOptions options) { if (methodCall.Object != null && !methodCall.Method.CustomAttributes.Any(attr => attr.AttributeType == typeof(ExtensionAttribute))) { //assume that marker methods are always static //(i.e. extensions methods) return(false); } return(markerMethods.ApplyIfRegistered(methodCall.Method, ref options, methodCall.Arguments)); }
public static TraversalOptions TrackItems(TraversalOptions options, string[] propertiesTracked) { return(TrackItems(options, PropertyTrackingInfo.FromStringArray(propertiesTracked))); }
private static void ApplyMarkerCall(FluentTargetMethod markerMethod, ref TraversalOptions options, IEnumerable <object> arguments = null) { markerMethods.Apply(markerMethod.Method, ref options, arguments); }
public static TraversalOptions TrackItems(TraversalOptions options) { return(TrackItems(options, PropertyTrackingInfo.TrackAll)); }
/// <summary> /// Marks a collection object /// that all contained items implementing INPC should /// be tracked as well. /// </summary> /// <returns>The same instance of the <see cref="ExpressionPreconfiguration"/> /// type, which holds preconfiguration options</returns> public ExpressionPreconfiguration TrackItems() { this.Options = MarkerMethods.TrackItems(this.Options, PropertyTrackingInfo.TrackAll); return(this); }
/// <summary> /// Marks a collection object /// that all contained items implementing INPC should /// be tracked as well. /// </summary> /// <returns>The same instance of the <see cref="ExpressionPreconfiguration"/> /// type, which holds preconfiguration options</returns> public ExpressionPreconfiguration TrackItems <TItem>(params Expression <Func <TItem, object> >[] propertiesToTrack) { this.Options = MarkerMethods.TrackItems(this.Options, propertiesToTrack); return(this); }
public static TraversalOptions TrackFields(TraversalOptions options) { options.TrackFields = true; return(options); }
public static TraversalOptions TrackLastChild(TraversalOptions options) { options.TrackLastChild = true; return(options); }
/// <summary> /// Marks all fields in follow-up expression as trackable, /// given that they implement the /// <see cref="INotifyPropertyChanged"/> interface. /// </summary> /// <returns>The same instance of the <see cref="ExpressionPreconfiguration"/> /// type, which holds preconfiguration options</returns> public ExpressionPreconfiguration TrackFields() { this.Options = MarkerMethods.TrackFields(this.Options); return(this); }