Пример #1
0
        private static void HandleFieldConstant(Set <UsedPropertyChain> container,
                                                MemberExpression memberExpression,
                                                ChildInformation child,
                                                TraversalOptions options)
        {
            Func <object> constantObjectRetriever = CompileMemberExpression(memberExpression);

            HandleFieldConstant(container, constantObjectRetriever, child, options);
        }
Пример #2
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
        }
Пример #3
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);
        }
Пример #4
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);
        }
Пример #5
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);
                }
            }
        }