/// <summary>
        /// Evaluates the given expression and returns the result.  The expression can refer to a local variable (e.g. 'somevariable'),
        /// a path starting from a localVariable (e.g. 'somevariable.SomeProperty.SomeOtherProperty'), or a path starting from the root
        /// data object (e.g. if the root was of type 'Person' you could say 'FirstName', assuming the Person type has a property called 'FirstName').
        /// </summary>
        /// <param name="expressionText">The expression text</param>
        /// <returns>The resolved value as a .NET object</returns>
        public object EvaluateExpression(string expressionText)
        {
            object localVariableValue;
            string restOfExpressionText;

            ObjectPathExpression expression;
            object root;

            if (LocalVariables.TryParseLocalVariable(expressionText, out localVariableValue, out restOfExpressionText))
            {
                if (restOfExpressionText == null)
                {
                    return(localVariableValue);
                }
                else
                {
                    expression = ObjectPathExpression.Parse(restOfExpressionText);
                    root       = localVariableValue;
                }
            }
            else
            {
                expression = ObjectPathExpression.Parse(expressionText);
                root       = this.RootDataObject;
            }

            var ret = expression.Evaluate(root);

            return(ret);
        }
        private IDisposable DeepSubscribeInternal(string path, Action handler, Lifetime rootLifetime = null)
        {
            rootLifetime = rootLifetime ?? new Lifetime();
            var observableRoot = DeepObservableRoot ?? this;
            IObservableObject currentObservable = observableRoot;
            var pathExpression = ObjectPathExpression.Parse(path);
            List <IObjectPathElement> currentPath = new List <IObjectPathElement>();

            var anyChangeLifetime = new Lifetime();

            for (int i = 0; i < pathExpression.Elements.Count; i++)
            {
                var propertyElement = pathExpression.Elements[i] as PropertyPathElement;
                if (propertyElement == null)
                {
                    throw new NotSupportedException("Indexers are not supported for deep observability");
                }

                currentPath.Add(propertyElement);

                ObjectPathExpression.TraceNode eval;

                if (i == pathExpression.Elements.Count - 1 && propertyElement.PropertyName == ObservableObject.AnyProperty)
                {
                    eval = new ObjectPathExpression.TraceNode()
                    {
                        Value = null
                    };
                }
                else
                {
                    eval = new ObjectPathExpression(currentPath).EvaluateAndTraceInfo(observableRoot).Last();
                    if (i != pathExpression.Elements.Count - 1 && (eval.MemberInfo as PropertyInfo).PropertyType.GetInterfaces().Contains(typeof(IObservableObject)) == false)
                    {
                        throw new NotSupportedException($"element {eval.MemberInfo.Name} is not observable");
                    }
                }

                currentObservable.SubscribeForLifetime(propertyElement.PropertyName, () =>
                {
                    handler();
                    if (anyChangeLifetime.IsExpired == false)
                    {
                        anyChangeLifetime.Dispose();
                    }

                    if (rootLifetime.IsExpired == false)
                    {
                        DeepSubscribeInternal(path, handler, rootLifetime);
                    }
                }, EarliestOf(anyChangeLifetime, rootLifetime));

                currentObservable = eval.Value as IObservableObject;

                if (currentObservable == null)
                {
                    break;
                }
            }

            return(rootLifetime);
        }