Beispiel #1
0
        /// <summary>
        /// Binds the table element property to the property expression stack.
        /// </summary>
        /// <param name="propertyInfo">The property information.</param>
        /// <param name="context">The page builder context.</param>
        /// <param name="variableList">The variable list.</param>
        /// <param name="propertyExpressions">The property expressions.</param>
        /// <param name="attribute">The attribute.</param>
        /// <param name="customAttributes">The custom attributes.</param>
        private void BindTableElementProperty(
            PropertyInfo propertyInfo,
            PageBuilderContext context,
            ICollection <ParameterExpression> variableList,
            List <Expression> propertyExpressions,
            ElementLocatorAttribute attribute,
            IEnumerable <object> customAttributes)
        {
            // Get any types and methods
            var propertyType   = propertyInfo.PropertyType;
            var cellType       = propertyType.GetGenericArguments().First();
            var enumeratorType = typeof(IEnumerable <>).MakeGenericType(cellType);
            var assignItemInfo = propertyType.GetMethod("SetDriver", BindingFlags.Instance | BindingFlags.InvokeMethod | BindingFlags.NonPublic);

            // Create any variables and other experssions
            var driverVariable = Expression.Variable(enumeratorType, "driver");

            variableList.Add(driverVariable);
            var driverExpression = this.CreateElementListItem(context, this.GetTableDriverType(), variableList, propertyExpressions, attribute, customAttributes);

            // Add any necessary property expressions
            var itemVariable = context.CurrentElement.Expression;

            propertyExpressions.Add(Expression.Assign(itemVariable, Expression.Property(context.Document.Expression, propertyInfo)));
            propertyExpressions.Add(Expression.IfThen(
                                        Expression.Equal(itemVariable, Expression.Constant(null, propertyType)),
                                        Expression.Assign(itemVariable, Expression.New(propertyType))));
            propertyExpressions.Add(Expression.Assign(driverVariable, Expression.Convert(driverExpression, enumeratorType)));
            propertyExpressions.Add(Expression.Call(itemVariable, assignItemInfo, new Expression[] { driverVariable }));
        }
        public void TestPageBuilderContextWhenConstructedReturnsTheConstructedValues()
        {
            var browser = new ExpressionData(null, typeof(object));
            var document = new ExpressionData(null, typeof(object));
            var parentElement = new ExpressionData(null, typeof(object));

            var context = new PageBuilderContext(browser, parentElement, document);

            Assert.AreSame(browser, context.Browser);
            Assert.AreSame(document, context.Document);
            Assert.AreSame(parentElement, context.ParentElement);
            Assert.IsNull(context.RootLocator);
            Assert.IsNull(context.CurrentElement);
        }
Beispiel #3
0
        /// <summary>
        /// Creates the new item expression that creates the object and initial mapping.
        /// </summary>
        /// <param name="elementType">Type of the element.</param>
        /// <returns>The initial creation lambda expression.</returns>
        /// <exception cref="System.InvalidOperationException">Thrown if the constructor is invalid.</exception>
        private Expression <Func <TParent, IBrowser, Action <TOutput>, TOutput> > CreateNewItemExpression(Type elementType)
        {
            var browserParameter = Expression.Parameter(typeof(IBrowser), "browser");
            var browserArgument  = new ExpressionData(browserParameter, typeof(IBrowser), "browser");

            var parentParameter = Expression.Parameter(typeof(TParent), "parent");
            var parentArgument  = new ExpressionData(parentParameter, typeof(TParent), "rootContext");

            var docVariable  = Expression.Variable(elementType);
            var documentData = new ExpressionData(docVariable, elementType, elementType.Name);

            var context = new PageBuilderContext(browserArgument, parentArgument, documentData);

            var constructor = this.GetConstructor(elementType, context);

            if (constructor == null)
            {
                throw this.CreateConstructorException(null, elementType);
            }

            var actionParameter = Expression.Parameter(typeof(Action <TOutput>), "action");


            //Spin though properties and make an initializer for anything we can set that has an attribute
            var pageMethodInfo = new Action <TOutput, Action <TOutput> >(this.AssignPageAttributes).GetMethodInfo();
            var expressions    = new List <Expression>
            {
                Expression.Assign(docVariable, Expression.New(constructor.Item1, constructor.Item2)),
                Expression.Call(
                    Expression.Constant(this),
                    pageMethodInfo,
                    Expression.Convert(docVariable, typeof(TOutput)),
                    actionParameter)
            };



            this.MapObjectProperties(expressions, elementType, context);
            expressions.Add(docVariable);

            var methodCall = Expression.Block(new[] { docVariable }, expressions);

            return(Expression.Lambda <Func <TParent, IBrowser, Action <TOutput>, TOutput> >(methodCall, parentParameter, browserParameter, actionParameter));
        }
Beispiel #4
0
        /// <summary>
        /// Creates the element list item.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="propertyType">Type of the property.</param>
        /// <param name="variableList">The variable list.</param>
        /// <param name="propertyExpressions">The property expressions.</param>
        /// <param name="attribute">The attribute.</param>
        /// <param name="customAttributes">The custom attributes.</param>
        /// <returns>The expression used to create the item.</returns>
        private NewExpression CreateElementListItem(
            PageBuilderContext context,
            Type propertyType,
            ICollection <ParameterExpression> variableList,
            List <Expression> propertyExpressions,
            ElementLocatorAttribute attribute,
            IEnumerable <object> customAttributes)
        {
            // Special case for lists
            Type concreteType;

            if (propertyType.IsClass && !propertyType.IsAbstract)
            {
                concreteType = propertyType;
            }
            else
            {
                var collectionType = this.GetElementCollectionType();
                concreteType = collectionType.MakeGenericType(propertyType.GetGenericArguments());
            }

            var constructor = concreteType.GetConstructors().First();

            var parentListType     = constructor.GetParameters().First().ParameterType;
            var parentListVariable = Expression.Variable(parentListType, "collectionParent");

            variableList.Add(parentListVariable);

            // Save the current context item to create the parent, then set it back.
            var currentItem = context.CurrentElement;

            context.CurrentElement = new ExpressionData(parentListVariable, parentListType);
            propertyExpressions.AddRange(this.CreateHtmlObject(context, attribute, customAttributes));
            context.CurrentElement = currentItem;

            return(Expression.New(constructor, parentListVariable, context.Browser.Expression));
        }
Beispiel #5
0
        /// <summary>
        /// Creates the HTML object for each property that is part of the parent.
        /// </summary>
        /// <param name="context">The context.</param>
        /// <param name="attribute">The attribute.</param>
        /// <param name="nativeAttributes">The native attributes.</param>
        /// <returns>The expressions needed to create the list</returns>
        private IEnumerable <Expression> CreateHtmlObject(PageBuilderContext context, ElementLocatorAttribute attribute, IEnumerable nativeAttributes)
        {
            var buildElement = context.CurrentElement ?? context.Document;
            var objectType   = this.GetPropertyProxyType(buildElement.Type);

            var propConstructor = this.GetConstructor(objectType, context);

            if (propConstructor == null)
            {
                throw this.CreateConstructorException(buildElement.Name, objectType);
            }

            var itemVariable = buildElement.Expression;

            return(new[]
            {
                (Expression)Expression.Assign(itemVariable, Expression.New(propConstructor.Item1, propConstructor.Item2)),
                Expression.Call(Expression.Constant(this),
                                this.assignMethodInfo,
                                Expression.Convert(itemVariable, typeof(TElement)),
                                Expression.Constant(attribute, typeof(ElementLocatorAttribute)),
                                Expression.Constant(nativeAttributes, typeof(object[])))
            });
        }
Beispiel #6
0
        /// <summary>
        /// Maps the object properties for the given object.
        /// </summary>
        /// <param name="expressions">The parent expression list.</param>
        /// <param name="objectType">Type of the object.</param>
        /// <param name="context">The page builder context.</param>
        private void MapObjectProperties(ICollection <Expression> expressions, Type objectType, PageBuilderContext context)
        {
            // ReSharper disable LoopCanBeConvertedToQuery
            foreach (var propertyInfo in objectType.GetProperties().Where(p =>
                                                                          p.SetMethod != null && p.CanWrite && p.PropertyType.IsSupportedPropertyType(typeof(TElement))))
            {
                var propertyType     = propertyInfo.PropertyType;
                var attribute        = propertyInfo.GetCustomAttributes(typeof(ElementLocatorAttribute), false).FirstOrDefault() as ElementLocatorAttribute;
                var customAttributes = this.GetCustomAttributes(propertyInfo);
                if (attribute == null && customAttributes.Length == 0)
                {
                    continue;
                }

                // Final Properties
                var itemVariable = Expression.Variable(propertyType);
                context.CurrentElement = new ExpressionData(itemVariable, propertyType, propertyInfo.Name);

                var variableList = new List <ParameterExpression> {
                    itemVariable
                };
                var propertyExpressions = new List <Expression>();

                // Special case for table driver wrapper
                if (propertyType.IsTableElementType())
                {
                    this.BindTableElementProperty(propertyInfo, context, variableList, propertyExpressions, attribute, customAttributes);
                }
                else if (propertyType.IsElementListType())
                {
                    var listItem = this.CreateElementListItem(context, propertyType, variableList, propertyExpressions, attribute, customAttributes);
                    propertyExpressions.Add(Expression.Assign(itemVariable, listItem));
                }
                else
                {
                    //Normal path starts here
                    //New up property and then check it for inner properties.
                    var childContext = context.CreateChildContext(context.CurrentElement);

                    propertyExpressions.AddRange(this.CreateHtmlObject(childContext, attribute, customAttributes));
                    this.MapObjectProperties(propertyExpressions, propertyType, childContext);
                }

                propertyExpressions.Add(Expression.Assign(Expression.Property(context.Document.Expression, propertyInfo), itemVariable));
                expressions.Add(Expression.Block(variableList, propertyExpressions));
            }
        }
Beispiel #7
0
        /// <summary>
        /// Gets the constructor.
        /// </summary>
        /// <param name="itemType">Type of the item.</param>
        /// <param name="context">The page builder context.</param>
        /// <returns>The constructor information that matches.</returns>
        private Tuple <ConstructorInfo, IEnumerable <Expression> > GetConstructor(Type itemType, PageBuilderContext context)
        {
            ConstructorInfo emptyConstructor = null;

            foreach (var constructorInfo in itemType.GetConstructors(BindingFlags.CreateInstance | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
                     .OrderByDescending(c => c.GetParameters().Length))
            {
                var parameters = constructorInfo.GetParameters();
                if (parameters.Length == 0)
                {
                    emptyConstructor = constructorInfo;
                    continue;
                }

                var slots = new Expression[parameters.Length];
                slots.Initialize();

                for (var i = 0; i < parameters.Length; i++)
                {
                    var parameter     = parameters[i];
                    var parameterType = parameter.ParameterType;

                    if (context.Browser.Type.IsAssignableFrom(parameterType))
                    {
                        slots[i] = Expression.Convert(context.Browser.Expression, context.Browser.Type);
                    }
                    else
                    {
                        slots[i] = this.FillConstructorParameter(parameterType, context.ParentElement, context.RootLocator);
                    }
                }

                if (slots.All(s => s != null))
                {
                    return(new Tuple <ConstructorInfo, IEnumerable <Expression> >(constructorInfo, slots));
                }
            }

            return(this.AllowEmptyConstructor && emptyConstructor != null
                       ? new Tuple <ConstructorInfo, IEnumerable <Expression> >(emptyConstructor, new List <Expression>(0))
                       : null);
        }
        public void TestCreateChildContextWhenMultipleContextsAreCreatedThenTheRootContextIsTheFirstParent()
        {
            var browser = new ExpressionData(null, typeof(object));
            var document = new ExpressionData(null, typeof(object));
            var parentElement = new ExpressionData(null, typeof(object));
            var context = new PageBuilderContext(browser, parentElement, document);

            var child1 = new ExpressionData(null, typeof(object));
            var child2 = new ExpressionData(null, typeof(object));
            var childContext1 = context.CreateChildContext(child1);
            var childContext2 = childContext1.CreateChildContext(child2);

            Assert.AreSame(document, childContext1.ParentElement);
            Assert.AreSame(parentElement, childContext1.RootLocator);
            
            Assert.AreSame(child1, childContext2.ParentElement);
            Assert.AreSame(parentElement, childContext2.RootLocator);
        }