Example #1
0
 public static IEnumerable <string> Validate2(DothtmlElementNode control)
 {
     if (control.Attributes.Count != 2)
     {
         yield return($"The control has to have exactly two attributes");
     }
 }
        /// <summary>
        /// Processes the HTML element that represents a new object.
        /// </summary>
        private ResolvedControl ProcessObjectElement(DothtmlElementNode element, DataContextStack dataContext)
        {
            object[] constructorParameters;

            var controlMetadata = controlResolver.ResolveControl(element.TagPrefix, element.TagName, out constructorParameters);
            var control         = new ResolvedControl(controlMetadata, element, dataContext)
            {
                ContructorParameters = constructorParameters
            };

            var dataContextAttribute = element.Attributes.FirstOrDefault(a => a.AttributeName == "DataContext");

            if (dataContextAttribute != null)
            {
                ProcessAttribute(dataContextAttribute, control, dataContext);
            }
            if (control.Properties.ContainsKey(DotvvmBindableObject.DataContextProperty) && control.Properties[DotvvmBindableObject.DataContextProperty] is ResolvedPropertyBinding)
            {
                dataContext = new DataContextStack(
                    ((ResolvedPropertyBinding)control.Properties[DotvvmBindableObject.DataContextProperty]).Binding.GetExpression().Type,
                    dataContext);
                control.DataContextTypeStack = dataContext;
            }
            if (controlMetadata.DataContextConstraint != null && !controlMetadata.DataContextConstraint.IsAssignableFrom(dataContext.DataContextType))
            {
                throw new DotvvmCompilationException($"The control '{controlMetadata.Name}' requires a DataContext of type '{controlMetadata.DataContextConstraint.FullName}'!", element.Tokens);
            }

            // set properties from attributes
            foreach (var attribute in element.Attributes.Where(a => a.AttributeName != "DataContext"))
            {
                ProcessAttribute(attribute, control, dataContext);
            }

            var typeChange = DataContextChangeAttribute.GetDataContextExpression(dataContext, control);

            if (typeChange != null)
            {
                dataContext = new DataContextStack(typeChange, dataContext);
            }

            ProcessControlContent(element.Content, control);

            // check required properties
            var missingProperties = control.Metadata.Properties.Values.Where(p => p.MarkupOptions.Required && !control.Properties.ContainsKey(p));

            if (missingProperties.Any())
            {
                throw new DotvvmCompilationException($"The control '{ control.Metadata.Name }' is missing required properties: { string.Join(", ", missingProperties.Select(p => "'" + p.Name + "'")) }.", control.DothtmlNode.Tokens);
            }
            return(control);
        }
        /// <summary>
        /// Processes the element which contains property value.
        /// </summary>
        private IAbstractPropertySetter ProcessElementProperty(IAbstractControl control, IPropertyDescriptor property, IEnumerable <DothtmlNode> elementContent, DothtmlElementNode propertyWrapperElement)
        {
            // resolve data context
            var dataContext = control.DataContextTypeStack;

            dataContext = GetDataContextChange(dataContext, control, property);

            // the element is a property
            if (IsTemplateProperty(property))
            {
                // template
                return(treeBuilder.BuildPropertyTemplate(property, ProcessTemplate(control, elementContent, dataContext), propertyWrapperElement));
            }
            else if (IsCollectionProperty(property))
            {
                var collectionType = GetCollectionType(property);
                // collection of elements
                var collection =
                    FilterNodes <DothtmlElementNode>(elementContent, property)
                    .Select(childObject => ProcessObjectElement(childObject, dataContext));
                if (collectionType != null)
                {
                    collection = FilterOrError(collection,
                                               c => c.Metadata.Type.IsAssignableTo(collectionType),
                                               c => c.DothtmlNode.AddError($"Control type {c.Metadata.Type.FullName} can't be used in collection of type {collectionType.FullName}."));
                }

                return(treeBuilder.BuildPropertyControlCollection(property, collection.ToArray(), propertyWrapperElement));
            }
            else if (property.PropertyType.IsEqualTo(new ResolvedTypeDescriptor(typeof(string))))
            {
                // string property
                var strings = FilterNodes <DothtmlLiteralNode>(elementContent, property);
                var value   = string.Concat(strings.Select(s => s.Value));
                return(treeBuilder.BuildPropertyValue(property, value, propertyWrapperElement));
            }
            else if (IsControlProperty(property))
            {
                // new object
                var children = FilterNodes <DothtmlElementNode>(elementContent, property).ToList();
                if (children.Count > 1)
                {
                    foreach (var c in children.Skip(1))
                    {
                        c.AddError($"The property '{property.MarkupOptions.Name}' can have only one child element!");
                    }
                    children = children.Take(1).ToList();
                }
                if (children.Count == 1)
                {
                    return(treeBuilder.BuildPropertyControl(property, ProcessObjectElement(children[0], dataContext), propertyWrapperElement));
                }
                else
                {
                    return(treeBuilder.BuildPropertyControl(property, null, propertyWrapperElement));
                }
            }
            else
            {
                control.DothtmlNode.AddError($"The property '{property.FullName}' is not supported!");
                return(treeBuilder.BuildPropertyValue(property, null, propertyWrapperElement));
            }
        }
        /// <summary>
        /// Processes the HTML element that represents a new object.
        /// </summary>
        private IAbstractControl ProcessObjectElement(DothtmlElementNode element, IDataContextStack dataContext)
        {
            // build control
            var controlMetadata = controlResolver.ResolveControl(element.TagPrefix, element.TagName, out var constructorParameters);

            if (controlMetadata == null)
            {
                controlMetadata       = controlResolver.ResolveControl("", element.TagName, out constructorParameters);
                constructorParameters = new[] { element.FullTagName };
                element.AddError($"The control <{element.FullTagName}> could not be resolved! Make sure that the tagPrefix is registered in DotvvmConfiguration.Markup.Controls collection!");
            }
            var control = treeBuilder.BuildControl(controlMetadata, element, dataContext);

            control.ConstructorParameters = constructorParameters;

            // resolve data context
            var dataContextAttribute = element.Attributes.FirstOrDefault(a => a.AttributeName == "DataContext");

            if (dataContextAttribute != null)
            {
                ProcessAttribute(dataContextAttribute, control, dataContext);
            }

            IAbstractPropertySetter dataContextProperty;

            if (control.TryGetProperty(DotvvmBindableObject.DataContextProperty, out dataContextProperty) && dataContextProperty is IAbstractPropertyBinding)
            {
                var dataContextBinding = ((IAbstractPropertyBinding)dataContextProperty).Binding;
                if (dataContextBinding?.ResultType != null && dataContext != null)
                {
                    dataContext = CreateDataContextTypeStack(dataContextBinding?.ResultType, parentDataContextStack: dataContext);
                }
                else if (dataContext != null)
                {
                    dataContext = CreateDataContextTypeStack(null, dataContext);
                }
                else
                {
                    dataContext = null;
                }
                control.DataContextTypeStack = dataContext;
            }
            if (controlMetadata.DataContextConstraint != null && dataContext != null && !controlMetadata.DataContextConstraint.IsAssignableFrom(dataContext.DataContextType))
            {
                ((DothtmlNode)dataContextAttribute ?? element)
                .AddError($"The control '{controlMetadata.Type.Name}' requires a DataContext of type '{controlMetadata.DataContextConstraint.FullName}'!");
            }

            // set properties from attributes
            foreach (var attribute in element.Attributes.Where(a => a.AttributeName != "DataContext"))
            {
                ProcessAttribute(attribute, control, dataContext);
            }

            // process control contents
            ProcessControlContent(control, element.Content);

            // check required properties
            IAbstractPropertySetter missingProperty;
            var missingProperties = control.Metadata.AllProperties.Where(p => p.MarkupOptions.Required && !control.TryGetProperty(p, out missingProperty)).ToList();

            if (missingProperties.Any())
            {
                element.AddError($"The control '{ control.Metadata.Type.FullName }' is missing required properties: { string.Join(", ", missingProperties.Select(p => "'" + p.Name + "'")) }.");
            }
            return(control);
        }
        private void FixElement(StringBuilder edit, MetadataControlResolver metadataControlResolver, ITextBuffer textBuffer, DothtmlElementNode element)
        {
            // fix element name
            var metadata = metadataControlResolver.GetMetadata(element.FullTagName);

            if (metadata != null)
            {
                // we have found the metadata for the control
                if (metadata.FullTagName != element.FullTagName)
                {
                    // the used name differs from the correct, fix the tag name
                    edit.SetRange(element.TagPrefixNode.StartPosition, element.TagPrefixNode.Length, metadata.TagPrefix);
                    edit.SetRange(element.TagNameNode.StartPosition, element.TagNameNode.Length, metadata.TagName);

                    if (element.CorrespondingEndTag != null)
                    {
                        edit.SetRange(element.CorrespondingEndTag.TagPrefixNode.StartPosition, element.CorrespondingEndTag.TagPrefixNode.Length, metadata.TagPrefix);
                        edit.SetRange(element.CorrespondingEndTag.TagNameNode.StartPosition, element.CorrespondingEndTag.TagNameNode.Length, metadata.TagName);
                    }
                }

                // fix attribute names
                foreach (var attribute in element.Attributes)
                {
                    var property = metadata.GetProperty(attribute.AttributeName);
                    if (property != null && property.Name != attribute.AttributeName)
                    {
                        // the used name differs from the correct, fix the tag name
                        edit.SetRange(attribute.AttributeNameNode.StartPosition, attribute.AttributeNameNode.Length, property.Name);
                    }
                }

                // fix property elements
                foreach (var child in element.Content.OfType <DothtmlElementNode>())
                {
                    var property = metadata.GetProperty(child.FullTagName);
                    if (property != null && property.IsElement && property.Name != child.FullTagName)
                    {
                        // the used name differs from the correct, fix the tag name
                        edit.SetRange(child.TagPrefixNode.StartPosition, child.TagPrefixNode.Length, property.Name);
                        if (child.CorrespondingEndTag != null)
                        {
                            edit.SetRange(child.CorrespondingEndTag.TagNameNode.StartPosition, child.CorrespondingEndTag.TagNameNode.Length, property.Name);
                        }
                    }
                }
            }
        }
Example #6
0
        /// <summary>
        /// Processes the element which contains property value.
        /// </summary>
        private IAbstractPropertySetter ProcessElementProperty(IAbstractControl control, IPropertyDescriptor property, IEnumerable <DothtmlNode> elementContent, DothtmlElementNode propertyWrapperElement)
        {
            IEnumerable <IAbstractControl> filterByType(ITypeDescriptor type, IEnumerable <IAbstractControl> controls) =>
            FilterOrError(controls,
                          c => c.Metadata.Type.IsAssignableTo(type),
                          c => {
                // empty nodes are only filtered, non-empty nodes cause errors
                if (c.DothtmlNode.IsNotEmpty())
                {
                    c.DothtmlNode.AddError($"Control type {c.Metadata.Type.FullName} can't be used in collection of type {type.FullName}.");
                }
            });

            // resolve data context
            var dataContext = control.DataContextTypeStack;

            dataContext = GetDataContextChange(dataContext, control, property);

            // the element is a property
            if (IsTemplateProperty(property))
            {
                // template
                return(treeBuilder.BuildPropertyTemplate(property, ProcessTemplate(control, elementContent, dataContext), propertyWrapperElement));
            }
            else if (IsCollectionProperty(property))
            {
                var collectionType = GetCollectionType(property);
                // collection of elements
                var collection = elementContent.Select(childObject => ProcessNode(control, childObject, control.Metadata, dataContext));
                if (collectionType != null)
                {
                    collection = filterByType(collectionType, collection);
                }

                return(treeBuilder.BuildPropertyControlCollection(property, collection.ToArray(), propertyWrapperElement));
            }
            else if (property.PropertyType.IsEqualTo(new ResolvedTypeDescriptor(typeof(string))))
            {
                // string property
                var strings = FilterNodes <DothtmlLiteralNode>(elementContent, property);
                var value   = string.Concat(strings.Select(s => s.Value));
                return(treeBuilder.BuildPropertyValue(property, value, propertyWrapperElement));
            }
            else if (IsControlProperty(property))
            {
                var children = filterByType(property.PropertyType, elementContent.Select(childObject => ProcessNode(control, childObject, control.Metadata, dataContext))).ToArray();
                if (children.Length > 1)
                {
                    // try with the empty nodes are excluded
                    children = children.Where(c => c.DothtmlNode.IsNotEmpty()).ToArray();
                    if (children.Length > 1)
                    {
                        foreach (var c in children.Skip(1))
                        {
                            c.DothtmlNode.AddError($"The property '{property.MarkupOptions.Name}' can have only one child element!");
                        }
                    }
                }
                if (children.Length >= 1)
                {
                    return(treeBuilder.BuildPropertyControl(property, children[0], propertyWrapperElement));
                }
                else
                {
                    return(treeBuilder.BuildPropertyControl(property, null, propertyWrapperElement));
                }
            }
            else
            {
                control.DothtmlNode.AddError($"The property '{property.FullName}' is not supported!");
                return(treeBuilder.BuildPropertyValue(property, null, propertyWrapperElement));
            }
        }
Example #7
0
 public IAbstractPropertyTemplate BuildPropertyTemplate(IPropertyDescriptor property, IEnumerable <IAbstractControl> templateControls, DothtmlElementNode wrapperElement)
 {
     return(new ResolvedPropertyTemplate((DotvvmProperty)property, templateControls.Cast <ResolvedControl>().ToList())
     {
         DothtmlNode = wrapperElement
     });
 }
Example #8
0
 public IAbstractPropertyControlCollection BuildPropertyControlCollection(IPropertyDescriptor property, IEnumerable <IAbstractControl> controls, DothtmlElementNode wrapperElement)
 {
     return(new ResolvedPropertyControlCollection((DotvvmProperty)property, controls.Cast <ResolvedControl>().ToList())
     {
         DothtmlNode = wrapperElement
     });
 }
Example #9
0
 public IAbstractPropertyControl BuildPropertyControl(IPropertyDescriptor property, IAbstractControl control, DothtmlElementNode wrapperElement)
 {
     return(new ResolvedPropertyControl((DotvvmProperty)property, (ResolvedControl)control)
     {
         DothtmlNode = wrapperElement
     });
 }
 public void Visit(DothtmlElementNode element)
 {
     ResolveFromParent(element);
 }
Example #11
0
 public void Visit(DothtmlElementNode element)
 {
     ResolveFromParent(element);
 }