예제 #1
0
        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));
        }
예제 #2
0
 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);
 }
예제 #3
0
 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));
 }
예제 #4
0
 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");
     }
 }
예제 #5
0
        private static void HandleFieldConstant(Set <UsedPropertyChain> container,
                                                MemberExpression memberExpression,
                                                ChildInformation child,
                                                TraversalOptions options)
        {
            Func <object> constantObjectRetriever = CompileMemberExpression(memberExpression);

            HandleFieldConstant(container, constantObjectRetriever, child, options);
        }
예제 #6
0
        /// <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;
        }
예제 #7
0
        /// <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;
        }
예제 #8
0
        /// <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);
        }
예제 #9
0
 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);
 }
예제 #10
0
        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
        }
예제 #11
0
        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);
        }
예제 #12
0
        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);
        }
예제 #13
0
 /// <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);
 }
예제 #14
0
        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));
        }
예제 #15
0
        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);
                }
            }
        }
예제 #16
0
        /// <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);
        }
예제 #17
0
 public static TraversalOptions DoNotTrackLastChild(TraversalOptions options)
 {
     options.TrackLastChild = false;
     return(options);
 }
예제 #18
0
 public static TraversalOptions DoNotTrackFields(TraversalOptions options)
 {
     options.TrackFields = false;
     return(options);
 }
예제 #19
0
        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));
        }
예제 #20
0
 public static TraversalOptions TrackItems(TraversalOptions options, string[] propertiesTracked)
 {
     return(TrackItems(options, PropertyTrackingInfo.FromStringArray(propertiesTracked)));
 }
예제 #21
0
 private static void ApplyMarkerCall(FluentTargetMethod markerMethod, ref TraversalOptions options, IEnumerable <object> arguments = null)
 {
     markerMethods.Apply(markerMethod.Method, ref options, arguments);
 }
예제 #22
0
 public static TraversalOptions TrackItems(TraversalOptions options)
 {
     return(TrackItems(options, PropertyTrackingInfo.TrackAll));
 }
예제 #23
0
 /// <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);
 }
예제 #24
0
 /// <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);
 }
예제 #25
0
 public static TraversalOptions TrackFields(TraversalOptions options)
 {
     options.TrackFields = true;
     return(options);
 }
예제 #26
0
 public static TraversalOptions TrackLastChild(TraversalOptions options)
 {
     options.TrackLastChild = true;
     return(options);
 }
예제 #27
0
 /// <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);
 }