File: TagHelpers\TagHelperAttribute.cs
Web Access
Project: src\src\Razor\Razor\src\Microsoft.AspNetCore.Razor.csproj (Microsoft.AspNetCore.Razor)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Html;
 
namespace Microsoft.AspNetCore.Razor.TagHelpers;
 
/// <summary>
/// An HTML tag helper attribute.
/// </summary>
public class TagHelperAttribute : IHtmlContentContainer
{
    /// <summary>
    /// Instantiates a new instance of <see cref="TagHelperAttribute"/> with the specified <paramref name="name"/>.
    /// <see cref="ValueStyle"/> is set to <see cref="HtmlAttributeValueStyle.Minimized"/> and <see cref="Value"/> to
    /// <c>null</c>.
    /// </summary>
    /// <param name="name">The <see cref="Name"/> of the attribute.</param>
    public TagHelperAttribute(string name)
        : this(name, value: null, valueStyle: HtmlAttributeValueStyle.Minimized)
    {
    }
 
    /// <summary>
    /// Instantiates a new instance of <see cref="TagHelperAttribute"/> with the specified <paramref name="name"/>
    /// and <paramref name="value"/>. <see cref="ValueStyle"/> is set to <see cref="HtmlAttributeValueStyle.DoubleQuotes"/>.
    /// </summary>
    /// <param name="name">The <see cref="Name"/> of the attribute.</param>
    /// <param name="value">The <see cref="Value"/> of the attribute.</param>
    public TagHelperAttribute(string name, object value)
        : this(name, value, valueStyle: HtmlAttributeValueStyle.DoubleQuotes)
    {
    }
 
    /// <summary>
    /// Instantiates a new instance of <see cref="TagHelperAttribute"/> with the specified <paramref name="name"/>,
    /// <paramref name="value"/> and <paramref name="valueStyle"/>.
    /// </summary>
    /// <param name="name">The <see cref="Name"/> of the new instance.</param>
    /// <param name="value">The <see cref="Value"/> of the new instance.</param>
    /// <param name="valueStyle">The <see cref="ValueStyle"/> of the new instance.</param>
    /// <remarks>If <paramref name="valueStyle"/> is <see cref="HtmlAttributeValueStyle.Minimized"/>,
    /// <paramref name="value"/> is ignored when this instance is rendered.</remarks>
    public TagHelperAttribute(string name, object value, HtmlAttributeValueStyle valueStyle)
    {
        ArgumentNullException.ThrowIfNull(name);
 
        Name = name;
        Value = value;
        ValueStyle = valueStyle;
    }
 
    /// <summary>
    /// Gets the name of the attribute.
    /// </summary>
    public string Name { get; }
 
    /// <summary>
    /// Gets the value of the attribute.
    /// </summary>
    public object Value { get; }
 
    /// <summary>
    /// Gets the value style of the attribute.
    /// </summary>
    public HtmlAttributeValueStyle ValueStyle { get; }
 
    /// <inheritdoc />
    /// <remarks><see cref="Name"/> is compared case-insensitively.</remarks>
    public bool Equals(TagHelperAttribute other)
    {
        return
            other != null &&
            string.Equals(Name, other.Name, StringComparison.OrdinalIgnoreCase) &&
            ValueStyle == other.ValueStyle &&
            (ValueStyle == HtmlAttributeValueStyle.Minimized || Equals(Value, other.Value));
    }
 
    /// <inheritdoc />
    public void WriteTo(TextWriter writer, HtmlEncoder encoder)
    {
        ArgumentNullException.ThrowIfNull(writer);
        ArgumentNullException.ThrowIfNull(encoder);
 
        writer.Write(Name);
 
        if (ValueStyle == HtmlAttributeValueStyle.Minimized)
        {
            return;
        }
 
        var valuePrefix = GetAttributeValuePrefix(ValueStyle);
        if (valuePrefix != null)
        {
            writer.Write(valuePrefix);
        }
 
        var htmlContent = Value as IHtmlContent;
        if (htmlContent != null)
        {
            htmlContent.WriteTo(writer, encoder);
        }
        else if (Value != null)
        {
            encoder.Encode(writer, Value.ToString());
        }
 
        var valueSuffix = GetAttributeValueSuffix(ValueStyle);
        if (valueSuffix != null)
        {
            writer.Write(valueSuffix);
        }
    }
 
    /// <inheritdoc />
    public void CopyTo(IHtmlContentBuilder destination)
    {
        ArgumentNullException.ThrowIfNull(destination);
 
        destination.AppendHtml(Name);
 
        if (ValueStyle == HtmlAttributeValueStyle.Minimized)
        {
            return;
        }
 
        var valuePrefix = GetAttributeValuePrefix(ValueStyle);
        if (valuePrefix != null)
        {
            destination.AppendHtml(valuePrefix);
        }
 
        string valueAsString;
        IHtmlContentContainer valueAsHtmlContainer;
        IHtmlContent valueAsHtmlContent;
        if ((valueAsString = Value as string) != null)
        {
            destination.Append(valueAsString);
        }
        else if ((valueAsHtmlContainer = Value as IHtmlContentContainer) != null)
        {
            valueAsHtmlContainer.CopyTo(destination);
        }
        else if ((valueAsHtmlContent = Value as IHtmlContent) != null)
        {
            destination.AppendHtml(valueAsHtmlContent);
        }
        else if (Value != null)
        {
            destination.Append(Value.ToString());
        }
 
        var valueSuffix = GetAttributeValueSuffix(ValueStyle);
        if (valueSuffix != null)
        {
            destination.AppendHtml(valueSuffix);
        }
    }
 
    /// <inheritdoc />
    public void MoveTo(IHtmlContentBuilder destination)
    {
        ArgumentNullException.ThrowIfNull(destination);
 
        destination.AppendHtml(Name);
 
        if (ValueStyle == HtmlAttributeValueStyle.Minimized)
        {
            return;
        }
 
        var valuePrefix = GetAttributeValuePrefix(ValueStyle);
        if (valuePrefix != null)
        {
            destination.AppendHtml(valuePrefix);
        }
 
        string valueAsString;
        IHtmlContentContainer valueAsHtmlContainer;
        IHtmlContent valueAsHtmlContent;
        if ((valueAsString = Value as string) != null)
        {
            destination.Append(valueAsString);
        }
        else if ((valueAsHtmlContainer = Value as IHtmlContentContainer) != null)
        {
            valueAsHtmlContainer.MoveTo(destination);
        }
        else if ((valueAsHtmlContent = Value as IHtmlContent) != null)
        {
            destination.AppendHtml(valueAsHtmlContent);
        }
        else if (Value != null)
        {
            destination.Append(Value.ToString());
        }
 
        var valueSuffix = GetAttributeValueSuffix(ValueStyle);
        if (valueSuffix != null)
        {
            destination.AppendHtml(valueSuffix);
        }
    }
 
    /// <inheritdoc />
    public override bool Equals(object obj)
    {
        var other = obj as TagHelperAttribute;
 
        return Equals(other);
    }
 
    /// <inheritdoc />
    public override int GetHashCode()
    {
        var hashCode = new HashCode();
        hashCode.Add(Name, StringComparer.Ordinal);
 
        if (ValueStyle != HtmlAttributeValueStyle.Minimized)
        {
            hashCode.Add(Value);
        }
 
        hashCode.Add(ValueStyle);
 
        return hashCode.ToHashCode();
    }
 
    private static string GetAttributeValuePrefix(HtmlAttributeValueStyle valueStyle)
    {
        switch (valueStyle)
        {
            case HtmlAttributeValueStyle.DoubleQuotes:
                return "=\"";
            case HtmlAttributeValueStyle.SingleQuotes:
                return "='";
            case HtmlAttributeValueStyle.NoQuotes:
                return "=";
        }
 
        return null;
    }
 
    private static string GetAttributeValueSuffix(HtmlAttributeValueStyle valueStyle)
    {
        switch (valueStyle)
        {
            case HtmlAttributeValueStyle.DoubleQuotes:
                return "\"";
            case HtmlAttributeValueStyle.SingleQuotes:
                return "'";
        }
 
        return null;
    }
}