public void WriteSetPreallocatedTagHelperProperty_IndexerAttribute_MultipleValues() { // Arrange var extension = new PreallocatedAttributeTargetExtension(); var context = TestCodeRenderingContext.CreateRuntime(); var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test"); tagHelperBuilder.TypeName("FooTagHelper"); var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); builder .Name("Foo") .TypeName("System.Collections.Generic.Dictionary<System.String, System.String>") .AsDictionaryAttribute("pre-", "System.String") .PropertyName("FooProp"); var boundAttribute = builder.Build(); var tagHelper = tagHelperBuilder.Build(); var tagHelperNode = new TagHelperIntermediateNode(); var node1 = new PreallocatedTagHelperPropertyIntermediateNode() { AttributeName = "pre-Bar", FieldName = "__FooTagHelper", VariableName = "_tagHelper0s", BoundAttribute = boundAttribute, IsIndexerNameMatch = true, PropertyName = "FooProp", TagHelper = tagHelper, }; var node2 = new PreallocatedTagHelperPropertyIntermediateNode() { AttributeName = "pre-Foo", FieldName = "__FooTagHelper", VariableName = "_tagHelper1", BoundAttribute = boundAttribute, IsIndexerNameMatch = true, PropertyName = "FooProp", TagHelper = tagHelper, }; tagHelperNode.Children.Add(node1); tagHelperNode.Children.Add(node2); Push(context, tagHelperNode); // Act extension.WriteTagHelperProperty(context, node2); // Assert var csharp = context.CodeWriter.GenerateCode(); Assert.Equal( @"__FooTagHelper.FooProp[""Foo""] = (string)_tagHelper1.Value; __tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1); ", csharp, ignoreLineEndingDifferences: true); }
public void VisitExtension(DefaultTagHelperPropertyIntermediateNode node) { if (!(node.BoundAttribute.IsStringProperty || (node.IsIndexerNameMatch && node.BoundAttribute.IsIndexerStringProperty)) || node.Children.Count != 1 || !(node.Children.First() is HtmlContentIntermediateNode)) { return; } var htmlContentNode = node.Children.First() as HtmlContentIntermediateNode; var plainTextValue = GetContent(htmlContentNode); PreallocatedTagHelperPropertyValueIntermediateNode declaration = null; for (var i = 0; i < _classDeclaration.Children.Count; i++) { var current = _classDeclaration.Children[i]; if (current is PreallocatedTagHelperPropertyValueIntermediateNode existingDeclaration) { if (string.Equals(existingDeclaration.AttributeName, node.AttributeName, StringComparison.Ordinal) && string.Equals(existingDeclaration.Value, plainTextValue, StringComparison.Ordinal) && existingDeclaration.AttributeStructure == node.AttributeStructure) { declaration = existingDeclaration; break; } } } if (declaration == null) { var variableCount = _classDeclaration.Children.Count - _variableCountOffset; var preAllocatedAttributeVariableName = PreAllocatedAttributeVariablePrefix + variableCount; declaration = new PreallocatedTagHelperPropertyValueIntermediateNode() { VariableName = preAllocatedAttributeVariableName, AttributeName = node.AttributeName, Value = plainTextValue, AttributeStructure = node.AttributeStructure, }; _classDeclaration.Children.Insert(_preallocatedDeclarationCount++, declaration); } var setPreallocatedProperty = new PreallocatedTagHelperPropertyIntermediateNode(node) { VariableName = declaration.VariableName, }; var nodeIndex = Parent.Children.IndexOf(node); Parent.Children[nodeIndex] = setPreallocatedProperty; }
private static string GetPropertyAccessor(PreallocatedTagHelperPropertyIntermediateNode node) { var propertyAccessor = $"{node.FieldName}.{node.PropertyName}"; if (node.IsIndexerNameMatch) { var dictionaryKey = node.AttributeName.Substring(node.BoundAttribute.IndexerNamePrefix.Length); propertyAccessor += $"[\"{dictionaryKey}\"]"; } return(propertyAccessor); }
public void WriteTagHelperProperty(CodeRenderingContext context, PreallocatedTagHelperPropertyIntermediateNode node) { var tagHelperNode = context.Parent as TagHelperIntermediateNode; if (tagHelperNode == null) { var message = Resources.FormatIntermediateNodes_InvalidParentNode(node.GetType(), typeof(TagHelperIntermediateNode)); throw new InvalidOperationException(message); } // Ensure that the property we're trying to set has initialized its dictionary bound properties. if (node.IsIndexerNameMatch && object.ReferenceEquals(FindFirstUseOfIndexer(tagHelperNode, node), node)) { // Throw a reasonable Exception at runtime if the dictionary property is null. context.CodeWriter .Write("if (") .Write(node.FieldName) .Write(".") .Write(node.PropertyName) .WriteLine(" == null)"); using (context.CodeWriter.BuildScope()) { // System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName // of InvalidOperationException type. context.CodeWriter .Write("throw ") .WriteStartNewObject(nameof(InvalidOperationException)) .WriteStartMethodInvocation(FormatInvalidIndexerAssignmentMethodName) .WriteStringLiteral(node.AttributeName) .WriteParameterSeparator() .WriteStringLiteral(node.TagHelper.GetTypeName()) .WriteParameterSeparator() .WriteStringLiteral(node.PropertyName) .WriteEndMethodInvocation(endLine: false) // End of method call .WriteEndMethodInvocation(); // End of new expression / throw statement } } context.CodeWriter .WriteStartAssignment(GetPropertyAccessor(node)) .Write("(string)") .Write($"{node.VariableName}.Value") .WriteLine(";") .WriteStartInstanceMethodInvocation(ExecutionContextVariableName, ExecutionContextAddTagHelperAttributeMethodName) .Write(node.VariableName) .WriteEndMethodInvocation(); }
public void WriteTagHelperProperty_RendersCorrectly() { // Arrange var extension = new PreallocatedAttributeTargetExtension(); var context = TestCodeRenderingContext.CreateRuntime(); var tagHelperBuilder = new DefaultTagHelperDescriptorBuilder(TagHelperConventions.DefaultKind, "FooTagHelper", "Test"); tagHelperBuilder.TypeName("FooTagHelper"); var builder = new DefaultBoundAttributeDescriptorBuilder(tagHelperBuilder, TagHelperConventions.DefaultKind); builder .Name("Foo") .TypeName("System.String") .PropertyName("FooProp"); var descriptor = builder.Build(); var tagHelperNode = new TagHelperIntermediateNode(); var node = new PreallocatedTagHelperPropertyIntermediateNode() { AttributeName = descriptor.Name, BoundAttribute = descriptor, FieldName = "__FooTagHelper", PropertyName = "FooProp", VariableName = "_tagHelper1", }; tagHelperNode.Children.Add(node); Push(context, tagHelperNode); // Act extension.WriteTagHelperProperty(context, node); // Assert var csharp = context.CodeWriter.GenerateCode(); Assert.Equal( @"__FooTagHelper.FooProp = (string)_tagHelper1.Value; __tagHelperExecutionContext.AddTagHelperAttribute(_tagHelper1); ", csharp, ignoreLineEndingDifferences: true); }
private static PreallocatedTagHelperPropertyIntermediateNode FindFirstUseOfIndexer( TagHelperIntermediateNode tagHelperNode, PreallocatedTagHelperPropertyIntermediateNode propertyNode) { Debug.Assert(tagHelperNode.Children.Contains(propertyNode)); Debug.Assert(propertyNode.IsIndexerNameMatch); for (var i = 0; i < tagHelperNode.Children.Count; i++) { if (tagHelperNode.Children[i] is PreallocatedTagHelperPropertyIntermediateNode otherPropertyNode && otherPropertyNode.TagHelper.Equals(propertyNode.TagHelper) && otherPropertyNode.BoundAttribute.Equals(propertyNode.BoundAttribute) && otherPropertyNode.IsIndexerNameMatch) { return(otherPropertyNode); } } // This is unreachable, we should find 'propertyNode' in the list of children. throw new InvalidOperationException(); }