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); } } } } }
/// <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)); } }
public IAbstractPropertyTemplate BuildPropertyTemplate(IPropertyDescriptor property, IEnumerable <IAbstractControl> templateControls, DothtmlElementNode wrapperElement) { return(new ResolvedPropertyTemplate((DotvvmProperty)property, templateControls.Cast <ResolvedControl>().ToList()) { DothtmlNode = wrapperElement }); }
public IAbstractPropertyControlCollection BuildPropertyControlCollection(IPropertyDescriptor property, IEnumerable <IAbstractControl> controls, DothtmlElementNode wrapperElement) { return(new ResolvedPropertyControlCollection((DotvvmProperty)property, controls.Cast <ResolvedControl>().ToList()) { DothtmlNode = wrapperElement }); }
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); }