/// <summary>
        /// Processes the attribute node.
        /// </summary>
        private void ProcessAttribute(DothtmlAttributeNode attribute, IAbstractControl control, IDataContextStack dataContext)
        {
            var name = attribute.AttributePrefix == null ? attribute.AttributeName : attribute.AttributePrefix + ":" + attribute.AttributeName;

            // find the property
            var property = controlResolver.FindProperty(control.Metadata, name);

            if (property != null)
            {
                if (property.IsBindingProperty || property.DataContextManipulationAttribute != null) // when DataContextManipulationAttribute is set, lets hope that author knows what is he doing.
                {
                    dataContext = GetDataContextChange(dataContext, control, property);
                }

                if (!property.MarkupOptions.MappingMode.HasFlag(MappingMode.Attribute))
                {
                    attribute.AddError($"The property '{property.FullName}' cannot be used as a control attribute!");
                    return;
                }

                // set the property
                if (attribute.ValueNode == null)
                {
                    // implicitly set boolean property
                    if (property.PropertyType.IsEqualTo(new ResolvedTypeDescriptor(typeof(bool))))
                    {
                        string error;
                        if (!treeBuilder.AddProperty(control, treeBuilder.BuildPropertyValue(property, true, attribute), out error))
                        {
                            attribute.AddError(error);
                        }
                    }
                    else if (property.MarkupOptions.AllowAttributeWithoutValue)
                    {
                        string error;
                        if (!treeBuilder.AddProperty(control, treeBuilder.BuildPropertyValue(property, (property as DotVVM.Framework.Binding.DotvvmProperty)?.DefaultValue, attribute), out error))
                        {
                            attribute.AddError(error);
                        }
                    }
                    else
                    {
                        attribute.AddError($"The attribute '{property.Name}' on the control '{control.Metadata.Type.FullName}' must have a value!");
                    }
                }
                else if (attribute.ValueNode is DothtmlValueBindingNode)
                {
                    // binding
                    var bindingNode = (attribute.ValueNode as DothtmlValueBindingNode).BindingNode;
                    if (property.IsVirtual && !property.IsBindingProperty && property.PropertyType.FullName != "System.Object")
                    {
                        attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain bindings because it's not DotvvmProperty.");
                    }
                    else if (!treatBindingAsHardCodedValue.Contains(bindingNode.Name))
                    {
                        if (!property.MarkupOptions.AllowBinding)
                        {
                            attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain {bindingNode.Name} binding.");
                        }
                    }
                    var    binding         = ProcessBinding(bindingNode, dataContext, property);
                    var    bindingProperty = treeBuilder.BuildPropertyBinding(property, binding, attribute);
                    string error;
                    if (!treeBuilder.AddProperty(control, bindingProperty, out error))
                    {
                        attribute.AddError(error);
                    }
                }
                else
                {
                    // hard-coded value in markup
                    if (!property.MarkupOptions.AllowHardCodedValue)
                    {
                        attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain hard coded value.");
                    }

                    var    textValue     = attribute.ValueNode as DothtmlValueTextNode;
                    var    value         = ConvertValue(WebUtility.HtmlDecode(textValue.Text), property.PropertyType);
                    var    propertyValue = treeBuilder.BuildPropertyValue(property, value, attribute);
                    string error;
                    if (!treeBuilder.AddProperty(control, propertyValue, out error))
                    {
                        attribute.AddError(error);
                    }
                }
            }
            else
            {
                attribute.AddError($"The control '{control.Metadata.Type}' does not have a property '{attribute.AttributeName}' and does not allow HTML attributes!");
            }
        }
        /// <summary>
        /// Processes the attribute node.
        /// </summary>
        private void ProcessAttribute(DothtmlAttributeNode attribute, IAbstractControl control, IDataContextStack dataContext)
        {
            if (attribute.AttributePrefix == "html")
            {
                if (!control.Metadata.HasHtmlAttributesCollection)
                {
                    attribute.AddError($"The control '{control.Metadata.Type.FullName}' cannot use HTML attributes!");
                }
                else
                {
                    try
                    {
                        treeBuilder.SetHtmlAttribute(control, ProcessAttributeValue(attribute, dataContext));
                    }
                    catch (NotSupportedException ex)
                    {
                        if (ex.InnerException == null)
                        {
                            throw;
                        }
                        else
                        {
                            attribute.AddError(ex.Message);
                        }
                    }
                }
                return;
            }

            if (!string.IsNullOrEmpty(attribute.AttributePrefix))
            {
                attribute.AddError("Attributes with XML namespaces are not supported!");
                return;
            }

            // find the property
            var property = FindProperty(control.Metadata, attribute.AttributeName);

            if (property != null)
            {
                if (control.HasProperty(property))
                {
                    attribute.AttributeNameNode.AddError($"control '{ ((DothtmlElementNode)control.DothtmlNode).FullTagName }' already has property '{ attribute.AttributeName }'.");
                }

                if (property.IsBindingProperty || property.DataContextManipulationAttribute != null) // when DataContextManipulationAttribute is set, lets hope that author knows what is he doing.
                {
                    dataContext = GetDataContextChange(dataContext, control, property);
                }

                if (!property.MarkupOptions.MappingMode.HasFlag(MappingMode.Attribute))
                {
                    attribute.AddError($"The property '{property.FullName}' cannot be used as a control attribute!");
                    return;
                }

                // set the property
                if (attribute.ValueNode == null)
                {
                    attribute.AddError($"The attribute '{property.Name}' on the control '{control.Metadata.Type.FullName}' must have a value!");
                }
                else if (attribute.ValueNode is DothtmlValueBindingNode)
                {
                    // binding
                    var bindingNode = (attribute.ValueNode as DothtmlValueBindingNode).BindingNode;
                    if (property.IsVirtual)
                    {
                        attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain bindings because it's not DotvvmProperty.");
                    }
                    else if (treatBindingAsHardCodedValue.Contains(bindingNode.Name))
                    {
                        if (!property.MarkupOptions.AllowHardCodedValue)
                        {
                            attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain {bindingNode.Name} binding, it can contain only hard-coded value or resource binding.");
                        }
                    }
                    else
                    {
                        if (!property.MarkupOptions.AllowBinding)
                        {
                            attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain {bindingNode.Name} binding.");
                        }
                    }
                    var binding         = ProcessBinding(bindingNode, dataContext);
                    var bindingProperty = treeBuilder.BuildPropertyBinding(property, binding, attribute);
                    treeBuilder.SetProperty(control, bindingProperty);
                }
                else
                {
                    // hard-coded value in markup
                    if (!property.MarkupOptions.AllowHardCodedValue)
                    {
                        attribute.ValueNode.AddError($"The property '{ property.FullName }' cannot contain hard coded value.");
                    }

                    var textValue     = attribute.ValueNode as DothtmlValueTextNode;
                    var value         = ConvertValue(textValue.Text, property.PropertyType);
                    var propertyValue = treeBuilder.BuildPropertyValue(property, value, attribute);
                    treeBuilder.SetProperty(control, propertyValue);
                }
            }
            else if (control.Metadata.HasHtmlAttributesCollection)
            {
                // if the property is not found, add it as an HTML attribute
                try
                {
                    treeBuilder.SetHtmlAttribute(control, ProcessAttributeValue(attribute, dataContext));
                }
                catch (NotSupportedException ex)
                {
                    if (ex.InnerException == null)
                    {
                        throw;
                    }
                    else
                    {
                        attribute.AddError(ex.Message);
                    }
                }
            }
            else
            {
                attribute.AddError($"The control '{control.Metadata.Type}' does not have a property '{attribute.AttributeName}' and does not allow HTML attributes!");
            }
        }