// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using Microsoft.AspNetCore.Razor.Language.Intermediate; using Microsoft.AspNetCore.Razor.Language.Legacy; namespace Microsoft.AspNetCore.Razor.Language.CodeGeneration { internal class PreallocatedAttributeTargetExtension : IPreallocatedAttributeTargetExtension { public string TagHelperAttributeTypeName { get; set; } = "Microsoft.AspNetCore.Razor.TagHelpers.TagHelperAttribute"; public string EncodedHtmlStringTypeName { get; set; } = "Microsoft.AspNetCore.Html.HtmlString"; public string ExecutionContextVariableName { get; set; } = "__tagHelperExecutionContext"; public string ExecutionContextAddHtmlAttributeMethodName { get; set; } = "AddHtmlAttribute"; public string ExecutionContextAddTagHelperAttributeMethodName { get; set; } = "AddTagHelperAttribute"; public string FormatInvalidIndexerAssignmentMethodName { get; set; } = "InvalidTagHelperIndexerAssignment"; public void WriteDeclarePreallocatedTagHelperHtmlAttribute(CSharpRenderingContext context, DeclarePreallocatedTagHelperHtmlAttributeIntermediateNode node) { context.Writer .Write("private static readonly global::") .Write(TagHelperAttributeTypeName) .Write(" ") .Write(node.VariableName) .Write(" = ") .WriteStartNewObject("global::" + TagHelperAttributeTypeName) .WriteStringLiteral(node.Name); if (node.AttributeStructure == AttributeStructure.Minimized) { context.Writer.WriteEndMethodInvocation(); } else { context.Writer .WriteParameterSeparator() .WriteStartNewObject("global::" + EncodedHtmlStringTypeName) .WriteStringLiteral(node.Value) .WriteEndMethodInvocation(endLine: false) .WriteParameterSeparator() .Write($"global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.{node.AttributeStructure}") .WriteEndMethodInvocation(); } } public void WriteAddPreallocatedTagHelperHtmlAttribute(CSharpRenderingContext context, AddPreallocatedTagHelperHtmlAttributeIntermediateNode node) { context.Writer .WriteStartInstanceMethodInvocation(ExecutionContextVariableName, ExecutionContextAddHtmlAttributeMethodName) .Write(node.VariableName) .WriteEndMethodInvocation(); } public void WriteDeclarePreallocatedTagHelperAttribute(CSharpRenderingContext context, DeclarePreallocatedTagHelperAttributeIntermediateNode node) { context.Writer .Write("private static readonly global::") .Write(TagHelperAttributeTypeName) .Write(" ") .Write(node.VariableName) .Write(" = ") .WriteStartNewObject("global::" + TagHelperAttributeTypeName) .WriteStringLiteral(node.Name) .WriteParameterSeparator() .WriteStringLiteral(node.Value) .WriteParameterSeparator() .Write($"global::Microsoft.AspNetCore.Razor.TagHelpers.HtmlAttributeValueStyle.{node.AttributeStructure}") .WriteEndMethodInvocation(); } public void WriteSetPreallocatedTagHelperProperty(CSharpRenderingContext context, SetPreallocatedTagHelperPropertyIntermediateNode node) { var tagHelperVariableName = GetTagHelperVariableName(node.TagHelperTypeName); var propertyName = node.Descriptor.GetPropertyName(); var propertyValueAccessor = GetTagHelperPropertyAccessor(node.IsIndexerNameMatch, tagHelperVariableName, node.AttributeName, node.Descriptor); var attributeValueAccessor = $"{node.VariableName}.Value" /* ORIGINAL: TagHelperAttributeValuePropertyName */; // Ensure that the property we're trying to set has initialized its dictionary bound properties. if (node.IsIndexerNameMatch && context.TagHelperRenderingContext.VerifiedPropertyDictionaries.Add($"{node.TagHelperTypeName}.{propertyName}")) { // Throw a reasonable Exception at runtime if the dictionary property is null. context.Writer .Write("if (") .Write(tagHelperVariableName) .Write(".") .Write(propertyName) .WriteLine(" == null)"); using (context.Writer.BuildScope()) { // System is in Host.NamespaceImports for all MVC scenarios. No need to generate FullName // of InvalidOperationException type. context.Writer .Write("throw ") .WriteStartNewObject(nameof(InvalidOperationException)) .WriteStartMethodInvocation(FormatInvalidIndexerAssignmentMethodName) .WriteStringLiteral(node.AttributeName) .WriteParameterSeparator() .WriteStringLiteral(node.TagHelperTypeName) .WriteParameterSeparator() .WriteStringLiteral(propertyName) .WriteEndMethodInvocation(endLine: false) // End of method call .WriteEndMethodInvocation(); // End of new expression / throw statement } } context.Writer .WriteStartAssignment(propertyValueAccessor) .Write("(string)") .Write(attributeValueAccessor) .WriteLine(";") .WriteStartInstanceMethodInvocation(ExecutionContextVariableName, ExecutionContextAddTagHelperAttributeMethodName) .Write(node.VariableName) .WriteEndMethodInvocation(); } private static string GetTagHelperVariableName(string tagHelperTypeName) => "__" + tagHelperTypeName.Replace('.', '_'); private static string GetTagHelperPropertyAccessor( bool isIndexerNameMatch, string tagHelperVariableName, string attributeName, BoundAttributeDescriptor descriptor) { var propertyAccessor = $"{tagHelperVariableName}.{descriptor.GetPropertyName()}"; if (isIndexerNameMatch) { var dictionaryKey = attributeName.Substring(descriptor.IndexerNamePrefix.Length); propertyAccessor += $"[\"{dictionaryKey}\"]"; } return propertyAccessor; } } }