/// <summary>
        /// Binds placeholders' content to the related properties on a template definition
        /// </summary>
        /// <param name="template">The template.</param>
        /// <param name="pageContentToRender">The page rendering job.</param>
        /// <param name="placeholderProperties">The placeholder properties.</param>
        /// <param name="functionContextContainer">The function context container, if not null, nested functions fill be evaluated.</param>
        public static void BindPlaceholders(IPageTemplate template,
                                            PageContentToRender pageContentToRender,
                                            IDictionary <string, PropertyInfo> placeholderProperties,
                                            FunctionContextContainer functionContextContainer)
        {
            Verify.ArgumentNotNull(template, "template");
            Verify.ArgumentNotNull(pageContentToRender, "pageContentToRender");
            Verify.ArgumentNotNull(placeholderProperties, "placeholderProperties");

            foreach (var placeholderContent in pageContentToRender.Contents)
            {
                string placeholderId = placeholderContent.PlaceHolderId;

                if (!placeholderProperties.ContainsKey(placeholderId))
                {
                    continue;
                }

                XhtmlDocument placeholderXhtml = PageRenderer.ParsePlaceholderContent(placeholderContent);

                if (functionContextContainer != null)
                {
                    using (Profiler.Measure("Evaluating placeholder '{0}'".FormatWith(placeholderId)))
                    {
                        PageRenderer.ExecuteEmbeddedFunctions(placeholderXhtml.Root, functionContextContainer);
                    }

                    PageRenderer.NormalizeXhtmlDocument(placeholderXhtml);
                }

                PageRenderer.ResolveRelativePaths(placeholderXhtml);

                PropertyInfo property = placeholderProperties[placeholderId];

                if (!property.ReflectedType.IsAssignableFrom(template.GetType()))
                {
                    string propertyName = property.Name;
                    property = template.GetType().GetProperty(property.Name);
                    Verify.IsNotNull(property, "Failed to find placeholder property '{0}'", propertyName);
                }

                property.SetValue(template, placeholderXhtml, new object[0]);
            }
        }
        /// <summary>
        /// Binds placeholders' content to the related properties on a template definition
        /// </summary>
        /// <param name="template">The template.</param>
        /// <param name="pageContentToRender">The page rendering job.</param>
        /// <param name="placeholderProperties">The placeholder properties.</param>
        /// <param name="functionContextContainer">The function context container, if not null, nested functions fill be evaluated.</param>
        public static void BindPlaceholders(IPageTemplate template,
                                            PageContentToRender pageContentToRender,
                                            IDictionary <string, PropertyInfo> placeholderProperties,
                                            FunctionContextContainer functionContextContainer)
        {
            Verify.ArgumentNotNull(template, "template");
            Verify.ArgumentNotNull(pageContentToRender, "pageContentToRender");
            Verify.ArgumentNotNull(placeholderProperties, "placeholderProperties");

            foreach (var placeholderContent in pageContentToRender.Contents)
            {
                string placeholderId = placeholderContent.PlaceHolderId;

                if (!placeholderProperties.ContainsKey(placeholderId))
                {
                    continue;
                }

                XhtmlDocument placeholderXhtml = PageRenderer.ParsePlaceholderContent(placeholderContent);

                if (functionContextContainer != null)
                {
                    bool allFunctionsExecuted;

                    using (Profiler.Measure($"Evaluating placeholder '{placeholderId}'"))
                    {
                        allFunctionsExecuted = PageRenderer.ExecuteCacheableFunctions(placeholderXhtml.Root, functionContextContainer);
                    }

                    if (allFunctionsExecuted)
                    {
                        using (Profiler.Measure("Normalizing XHTML document"))
                        {
                            PageRenderer.NormalizeXhtmlDocument(placeholderXhtml);
                        }
                    }
                }

                PageRenderer.ResolveRelativePaths(placeholderXhtml);

                PropertyInfo property = placeholderProperties[placeholderId];

                if (!property.ReflectedType.IsInstanceOfType(template))
                {
                    string propertyName = property.Name;
                    property = template.GetType().GetProperty(property.Name);
                    Verify.IsNotNull(property, "Failed to find placeholder property '{0}'", propertyName);
                }

                property.SetValue(template, placeholderXhtml);
            }
        }
        /// <summary>
        /// Builds a page template descriptor. Extracts template's properties and content placeholder properties.
        /// </summary>
        /// <param name="templateDefinition">The template definition.</param>
        /// <param name="descriptorConstructor">The descriptor constructor.</param>
        /// <param name="placeholderProperties">The placeholder properties.</param>
        /// <returns></returns>
        public static DescriptorType BuildPageTemplateDescriptor <DescriptorType>(IPageTemplate templateDefinition,
                                                                                  Func <DescriptorType> descriptorConstructor,
                                                                                  out IDictionary <string, PropertyInfo> placeholderProperties)
            where DescriptorType : PageTemplateDescriptor
        {
            Verify.ArgumentNotNull(templateDefinition, "templateDefinition");
            Verify.ArgumentNotNull(descriptorConstructor, "descriptorConstructor");

            DescriptorType pageTemplate = descriptorConstructor();

            Verify.ArgumentCondition(pageTemplate != null, "descriptorConstructor", "Not null object expected");

            if (templateDefinition.TemplateId == null || templateDefinition.TemplateId == Guid.Empty)
            {
                throw new InvalidOperationException("TemplateId has not been correctly defined");
            }

            pageTemplate.Id    = templateDefinition.TemplateId;
            pageTemplate.Title = templateDefinition.TemplateTitle;

            string defaultPlaceholderId = null;

            var placeholders = new List <PlaceholderDescriptor>();

            placeholderProperties = new Dictionary <string, PropertyInfo>();

            var type = templateDefinition.GetType();

            while (type.GetInterfaces().Contains(typeof(IPageTemplate)))
            {
                foreach (var property in type.GetProperties())
                {
                    if (property.ReflectedType != property.DeclaringType)
                    {
                        continue;
                    }

                    var placeholderAttributes = property.GetCustomAttributes(typeof(PlaceholderAttribute), true);
                    if (placeholderAttributes.Length == 0)
                    {
                        continue;
                    }

                    Verify.That(placeholderAttributes.Length == 1, "Multiple '{0}' attributes defined on property", typeof(PlaceholderAttribute), property.Name);

                    var placeholderAttribute = (PlaceholderAttribute)placeholderAttributes[0];

                    string placeholderId    = placeholderAttribute.Id ?? property.Name;
                    string placeholderLabel = placeholderAttribute.Title ?? property.Name;

                    if (placeholderProperties.ContainsKey(placeholderId))
                    {
                        throw new InvalidOperationException("Placeholder '{0}' defined multiple times".FormatWith(placeholderId));
                    }

                    var containerClasses = ContainerClassManager.ParseToList(placeholderAttribute.ContainerClasses).ToList();

                    placeholderProperties.Add(placeholderId, property);
                    placeholders.Add(new PlaceholderDescriptor {
                        Id = placeholderId, Title = placeholderLabel, ContainerClasses = containerClasses
                    });


                    if (placeholderAttribute.IsDefault)
                    {
                        Verify.IsNull(defaultPlaceholderId, "More than one placeholder is marked as default");

                        defaultPlaceholderId = placeholderId;
                    }
                }

                type = type.BaseType;
            }

            if (defaultPlaceholderId == null && placeholders.Any())
            {
                defaultPlaceholderId = placeholders.First().Id;
            }

            pageTemplate.DefaultPlaceholderId    = defaultPlaceholderId;
            pageTemplate.PlaceholderDescriptions = placeholders;

            return(pageTemplate);
        }