diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Razor.Runtime/Properties/Resources.Designer.cs index 3b10a1fd0b..b9da9197b7 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Razor.Runtime/Properties/Resources.Designer.cs @@ -362,38 +362,6 @@ namespace Microsoft.AspNetCore.Razor.Runtime return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperDescriptorFactory_InvalidAttributePrefixNotNull"), p0, p1, p2, p3, p4); } - /// - /// Cannot add a '{0}' with a null '{1}'. - /// - internal static string TagHelperAttributeList_CannotAddWithNullName - { - get { return GetString("TagHelperAttributeList_CannotAddWithNullName"); } - } - - /// - /// Cannot add a '{0}' with a null '{1}'. - /// - internal static string FormatTagHelperAttributeList_CannotAddWithNullName(object p0, object p1) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddWithNullName"), p0, p1); - } - - /// - /// Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'. - /// - internal static string TagHelperAttributeList_CannotAddAttribute - { - get { return GetString("TagHelperAttributeList_CannotAddAttribute"); } - } - - /// - /// Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'. - /// - internal static string FormatTagHelperAttributeList_CannotAddAttribute(object p0, object p1, object p2, object p3) - { - return string.Format(CultureInfo.CurrentCulture, GetString("TagHelperAttributeList_CannotAddAttribute"), p0, p1, p2, p3); - } - /// /// Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character. /// diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Resources.resx b/src/Microsoft.AspNetCore.Razor.Runtime/Resources.resx index cfc6719220..a3d55d9bcf 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/Resources.resx +++ b/src/Microsoft.AspNetCore.Razor.Runtime/Resources.resx @@ -183,12 +183,6 @@ Invalid tag helper bound property '{0}.{1}'. '{2}.{3}' must be null unless property type implements '{4}'. - - Cannot add a '{0}' with a null '{1}'. - - - Cannot add a {0} with inconsistent names. The {1} property '{2}' must match the location '{3}'. - Invalid '{0}' tag name '{1}' for tag helper '{2}'. Tag helpers cannot restrict child elements that contain a '{3}' character. diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs b/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs index f17a1536a2..9126524c52 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs +++ b/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperExecutionContext.cs @@ -94,7 +94,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers _endTagHelperWritingScope = endTagHelperWritingScope; TagMode = tagMode; - HTMLAttributes = new TagHelperAttributeList(); + HtmlAttributes = new TagHelperAttributeList(); AllAttributes = new TagHelperAttributeList(); TagName = tagName; Items = items; @@ -125,7 +125,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers /// /// HTML attributes. /// - public TagHelperAttributeList HTMLAttributes { get; } + public TagHelperAttributeList HtmlAttributes { get; } /// /// bound attributes and HTML attributes. @@ -173,7 +173,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers } /// - /// Tracks the minimized HTML attribute in and . + /// Tracks the minimized HTML attribute in and . /// /// The minimized HTML attribute name. public void AddMinimizedHtmlAttribute(string name) @@ -183,22 +183,13 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers throw new ArgumentNullException(nameof(name)); } - HTMLAttributes.Add( - new TagHelperAttribute - { - Name = name, - Minimized = true - }); - AllAttributes.Add( - new TagHelperAttribute - { - Name = name, - Minimized = true - }); + var attribute = new TagHelperAttribute(name); + HtmlAttributes.Add(attribute); + AllAttributes.Add(attribute); } /// - /// Tracks the HTML attribute in and . + /// Tracks the HTML attribute in and . /// /// The HTML attribute name. /// The HTML attribute value. @@ -209,7 +200,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers throw new ArgumentNullException(nameof(name)); } - HTMLAttributes.Add(name, value); + HtmlAttributes.Add(name, value); AllAttributes.Add(name, value); } diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperRunner.cs b/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperRunner.cs index 91f3057525..7e11d150d2 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperRunner.cs +++ b/src/Microsoft.AspNetCore.Razor.Runtime/Runtime/TagHelpers/TagHelperRunner.cs @@ -41,7 +41,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers var tagHelperOutput = new TagHelperOutput( executionContext.TagName, - executionContext.HTMLAttributes, + executionContext.HtmlAttributes, executionContext.GetChildContentAsync) { TagMode = executionContext.TagMode, diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/IReadOnlyTagHelperAttribute.cs b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/IReadOnlyTagHelperAttribute.cs deleted file mode 100644 index f7883299b8..0000000000 --- a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/IReadOnlyTagHelperAttribute.cs +++ /dev/null @@ -1,29 +0,0 @@ -// 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; - -namespace Microsoft.AspNetCore.Razor.TagHelpers -{ - /// - /// A read-only HTML tag helper attribute. - /// - public interface IReadOnlyTagHelperAttribute : IEquatable - { - /// - /// Gets the name of the attribute. - /// - string Name { get; } - - /// - /// Gets the value of the attribute. - /// - object Value { get; } - - /// - /// Gets an indication whether the attribute is minimized or not. - /// - /// If true, will be ignored. - bool Minimized { get; } - } -} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/ReadOnlyTagHelperAttributeList.cs b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/ReadOnlyTagHelperAttributeList.cs index bf4ad973fd..441c1e8451 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/ReadOnlyTagHelperAttributeList.cs +++ b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/ReadOnlyTagHelperAttributeList.cs @@ -2,73 +2,52 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; namespace Microsoft.AspNetCore.Razor.TagHelpers { /// - /// A read-only collection of s. + /// A read-only collection of s. /// - /// - /// The type of s in the collection. + /// + /// The type of s in the collection. /// - public class ReadOnlyTagHelperAttributeList : IReadOnlyList - where TAttribute : IReadOnlyTagHelperAttribute + public abstract class ReadOnlyTagHelperAttributeList : ReadOnlyCollection { - private static readonly IReadOnlyList EmptyList = -#if NET451 - new TAttribute[0]; -#else - Array.Empty(); -#endif + private static readonly IReadOnlyList EmptyList = new TagHelperAttribute[0]; /// - /// Instantiates a new instance of with an empty + /// Instantiates a new instance of with an empty /// collection. /// protected ReadOnlyTagHelperAttributeList() + : base(new List()) { - Attributes = new List(); } /// - /// Instantiates a new instance of with the specified + /// Instantiates a new instance of with the specified /// . /// /// The collection to wrap. - public ReadOnlyTagHelperAttributeList(IEnumerable attributes) + public ReadOnlyTagHelperAttributeList(IEnumerable attributes) + : base(new List(attributes)) { - if (attributes == null) - { - throw new ArgumentNullException(nameof(attributes)); - } - - Attributes = new List(attributes); } /// - /// The underlying collection of s. - /// - /// Intended for use in a non-read-only subclass. Changes to this will - /// affect all getters that provides. - protected List Attributes { get; } - - /// - public TAttribute this[int index] => Attributes[index]; - - /// - /// Gets the first with + /// Gets the first with /// matching . /// /// - /// The of the to get. + /// The of the to get. /// - /// The first with + /// The first with /// matching . /// /// is compared case-insensitively. - public TAttribute this[string name] + public TagHelperAttribute this[string name] { get { @@ -78,106 +57,47 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers } // Perf: Avoid allocating enumerator - for (var i = 0; i < Attributes.Count; i++) + for (var i = 0; i < Items.Count; i++) { - if (NameEquals(name, Attributes[i])) + if (NameEquals(name, Items[i])) { - return Attributes[i]; + return Items[i]; } } - return default(TAttribute); + return null; } } - /// - public int Count => Attributes.Count; - /// - /// Determines whether a matching exists in the - /// collection. + /// Determines whether a with + /// matching exists in the collection. /// - /// The to locate. + /// The of the + /// to get. /// - /// true if an matching exists in the - /// collection; otherwise, false. - /// - /// - /// s is compared case-insensitively. - /// - public bool Contains(TAttribute item) - { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - return Attributes.Contains(item); - } - - /// - /// Determines whether a with the same - /// exists in the collection. - /// - /// The of the - /// to get. - /// - /// true if a with the same - /// exists in the collection; otherwise, false. + /// true if a with the same + /// exists in the collection; otherwise, false. /// /// is compared case-insensitively. public bool ContainsName(string name) { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - // Perf: Avoid allocating enumerator - for (var i = 0; i < Attributes.Count; i++) - { - if (NameEquals(name, Attributes[i])) - { - return true; - } - } - - return false; + return this[name] != null; } /// - /// Searches for a matching in the collection and - /// returns the zero-based index of the first occurrence. - /// - /// The to locate. - /// The zero-based index of the first occurrence of a matching - /// in the collection, if found; otherwise, –1. - /// - /// s is compared case-insensitively. - /// - public int IndexOf(TAttribute item) - { - if (item == null) - { - throw new ArgumentNullException(nameof(item)); - } - - return Attributes.IndexOf(item); - } - - /// - /// Retrieves the first with + /// Retrieves the first with /// matching . /// - /// The of the - /// to get. - /// When this method returns, the first with - /// matching , if found; otherwise, + /// The of the + /// to get. + /// When this method returns, the first with + /// matching , if found; otherwise, /// null. - /// true if a with the same - /// exists in the collection; otherwise, false. + /// true if a with the same + /// exists in the collection; otherwise, false. /// is compared case-insensitively. - public bool TryGetAttribute(string name, out TAttribute attribute) + public bool TryGetAttribute(string name, out TagHelperAttribute attribute) { if (name == null) { @@ -190,17 +110,17 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers } /// - /// Retrieves s in the collection with - /// matching . + /// Retrieves s in the collection with + /// matching . /// - /// The of the - /// s to get. - /// When this method returns, the s with - /// matching . - /// true if at least one with the same - /// exists in the collection; otherwise, false. + /// The of the + /// s to get. + /// When this method returns, the s with + /// matching . + /// true if at least one with the same + /// exists in the collection; otherwise, false. /// is compared case-insensitively. - public bool TryGetAttributes(string name, out IReadOnlyList attributes) + public bool TryGetAttributes(string name, out IReadOnlyList attributes) { if (name == null) { @@ -208,17 +128,17 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers } // Perf: Avoid allocating enumerator - List matchedAttributes = null; - for (var i = 0; i < Attributes.Count; i++) + List matchedAttributes = null; + for (var i = 0; i < Items.Count; i++) { - if (NameEquals(name, Attributes[i])) + if (NameEquals(name, Items[i])) { if (matchedAttributes == null) { - matchedAttributes = new List(); + matchedAttributes = new List(); } - matchedAttributes.Add(Attributes[i]); + matchedAttributes.Add(Items[i]); } } attributes = matchedAttributes ?? EmptyList; @@ -226,16 +146,30 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers return matchedAttributes != null; } - /// - IEnumerator IEnumerable.GetEnumerator() + /// + /// Searches for a who's + /// case-insensitively matches and returns the zero-based index of the first + /// occurrence. + /// + /// The to locate in the collection. + /// The zero-based index of the first matching within the collection, + /// if found; otherwise, -1. + public int IndexOfName(string name) { - return GetEnumerator(); - } + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } - /// - public IEnumerator GetEnumerator() - { - return Attributes.GetEnumerator(); + for (var i = 0; i < Items.Count; i++) + { + if (NameEquals(name, Items[i])) + { + return i; + } + } + + return -1; } /// @@ -246,7 +180,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers /// The attribute to compare against. /// true if case-insensitively matches s /// . - protected static bool NameEquals(string name, TAttribute attribute) + protected static bool NameEquals(string name, TagHelperAttribute attribute) { if (attribute == null) { diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperAttribute.cs b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperAttribute.cs index a96d1086b9..259e428d54 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperAttribute.cs +++ b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperAttribute.cs @@ -2,83 +2,77 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.Extensions.Internal; namespace Microsoft.AspNetCore.Razor.TagHelpers { /// /// An HTML tag helper attribute. /// - public class TagHelperAttribute : IReadOnlyTagHelperAttribute + public class TagHelperAttribute { - private static readonly int TypeHashCode = typeof(TagHelperAttribute).GetHashCode(); - /// - /// Instantiates a new instance of . + /// Instantiates a new instance of with the specified . + /// is set to true and to null. /// - public TagHelperAttribute() + /// The of the attribute. + public TagHelperAttribute(string name) + : this(name, value: null, minimized: true) { } - /// - /// Instantiates a new instance of with values provided by the given - /// . - /// - /// A whose values should be copied. - public TagHelperAttribute(IReadOnlyTagHelperAttribute attribute) - : this(attribute?.Name, attribute?.Value) - { - if (attribute == null) - { - throw new ArgumentNullException(nameof(attribute)); - } - - Minimized = attribute.Minimized; - } - /// /// Instantiates a new instance of with the specified - /// and . + /// and . is set to false. /// /// The of the attribute. /// The of the attribute. public TagHelperAttribute(string name, object value) + : this(name, value, minimized: false) { + } + + /// + /// Instantiates a new instance of with the specified , + /// and . + /// + /// The of the new instance. + /// The of the new instance. + /// The value of the new instance. + /// If is true, is ignored when this + /// instance is rendered. + public TagHelperAttribute(string name, object value, bool minimized) + { + if (name == null) + { + throw new ArgumentNullException(nameof(name)); + } + Name = name; Value = value; + Minimized = minimized; } /// - /// Gets or sets the name of the attribute. + /// Gets the name of the attribute. /// - public string Name { get; set; } + public string Name { get; } /// - /// Gets or sets the value of the attribute. + /// Gets the value of the attribute. /// - public object Value { get; set; } + public object Value { get; } /// - /// Gets or sets an indication whether the attribute is minimized or not. + /// Gets an indication whether the attribute is minimized or not. /// /// If true, will be ignored. - public bool Minimized { get; set; } + public bool Minimized { get; } - /// - /// Converts the specified into a . - /// - /// The of the created . - /// Created s is set to null. - public static implicit operator TagHelperAttribute(string value) - { - return new TagHelperAttribute - { - Value = value - }; - } /// /// is compared case-insensitively. - public bool Equals(IReadOnlyTagHelperAttribute other) + public bool Equals(TagHelperAttribute other) { return other != null && @@ -90,7 +84,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers /// public override bool Equals(object obj) { - var other = obj as IReadOnlyTagHelperAttribute; + var other = obj as TagHelperAttribute; return Equals(other); } @@ -98,7 +92,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers /// public override int GetHashCode() { - return TypeHashCode; + var hashCodeCombiner = HashCodeCombiner.Start(); + hashCodeCombiner.Add(Name, StringComparer.Ordinal); + hashCodeCombiner.Add(Value); + hashCodeCombiner.Add(Minimized); + + return hashCodeCombiner.CombinedHash; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperAttributeList.cs b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperAttributeList.cs index 419e92c87a..73dde2f667 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperAttributeList.cs +++ b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperAttributeList.cs @@ -3,14 +3,13 @@ using System; using System.Collections.Generic; -using Microsoft.AspNetCore.Razor.Runtime; namespace Microsoft.AspNetCore.Razor.TagHelpers { /// /// A collection of s. /// - public class TagHelperAttributeList : ReadOnlyTagHelperAttributeList, IList + public class TagHelperAttributeList : ReadOnlyTagHelperAttributeList, IList { /// /// Instantiates a new instance of with an empty collection. @@ -51,123 +50,76 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers throw new ArgumentNullException(nameof(value)); } - if (value.Name == null) - { - throw new ArgumentException( - Resources.FormatTagHelperAttributeList_CannotAddWithNullName( - typeof(TagHelperAttribute).FullName, - nameof(TagHelperAttribute.Name)), - nameof(value)); - } - - Attributes[index] = value; + Items[index] = value; } } /// - /// Gets the first with matching - /// . When setting, replaces the first matching - /// with the specified and removes any additional - /// matching s. If a matching is not found, - /// adds the specified to the end of the collection. - /// + /// Replaces the first with matching + /// and removes any additional matching s. If a + /// matching is not found, adds a with + /// and to the end of the collection. /// - /// The of the to get or set. + /// The of the to set. /// - /// The first with matching - /// . - /// - /// is compared case-insensitively. When setting, - /// s must be null or - /// case-insensitively match the specified . - /// - /// - /// var attributes = new TagHelperAttributeList(); - /// - /// // Will "value" be converted to a TagHelperAttribute with a null Name - /// attributes["name"] = "value"; - /// - /// // TagHelperAttribute.Name must match the specified name. - /// attributes["name"] = new TagHelperAttribute("name", "value"); - /// - /// - public new TagHelperAttribute this[string name] + /// + /// The to set. + /// + /// is compared case-insensitively. + public void SetAttribute(string name, object value) { - get - { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } + var attribute = new TagHelperAttribute(name, value); + SetAttribute(attribute); + } - return base[name]; + /// + /// Replaces the first with matching + /// 's and removes any additional matching + /// s. If a matching is not found, adds the + /// specified to the end of the collection. + /// + /// + /// The to set. + /// + /// 's is compared + /// case-insensitively. + public void SetAttribute(TagHelperAttribute attribute) + { + if (attribute == null) + { + throw new ArgumentNullException(nameof(attribute)); } - set + + var attributeReplaced = false; + + // Perf: Avoid allocating enumerator + for (var i = 0; i < Items.Count; i++) { - if (name == null) + if (NameEquals(attribute.Name, Items[i])) { - throw new ArgumentNullException(nameof(name)); - } - - if (value == null) - { - throw new ArgumentNullException(nameof(value)); - } - - // Name will be null if user attempts to set the attribute via an implicit conversion: - // output.Attributes["someName"] = "someValue" - if (value.Name == null) - { - value.Name = name; - } - else if (!NameEquals(name, value)) - { - throw new ArgumentException( - Resources.FormatTagHelperAttributeList_CannotAddAttribute( - nameof(TagHelperAttribute), - nameof(TagHelperAttribute.Name), - value.Name, - name), - nameof(name)); - } - - var attributeReplaced = false; - - // Perf: Avoid allocating enumerator - for (var i = 0; i < Attributes.Count; i++) - { - if (NameEquals(name, Attributes[i])) + // We replace the first attribute with the provided attribute, remove all the rest. + if (!attributeReplaced) { - // We replace the first attribute with the provided value, remove all the rest. - if (!attributeReplaced) - { - // We replace the first attribute we find with the same name. - Attributes[i] = value; - attributeReplaced = true; - } - else - { - Attributes.RemoveAt(i--); - } + // We replace the first attribute we find with the same name. + Items[i] = attribute; + attributeReplaced = true; + } + else + { + Items.RemoveAt(i--); } } + } - // If we didn't replace an attribute value we should add value to the end of the collection. - if (!attributeReplaced) - { - Add(value); - } + // If we didn't replace an attribute value we should add value to the end of the collection. + if (!attributeReplaced) + { + Add(attribute); } } /// - bool ICollection.IsReadOnly - { - get - { - return false; - } - } + bool ICollection.IsReadOnly => false; /// /// Adds a to the end of the collection with the specified @@ -177,18 +129,11 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers /// The of the attribute to add. public void Add(string name, object value) { - if (name == null) - { - throw new ArgumentNullException(nameof(name)); - } - - Attributes.Add(new TagHelperAttribute(name, value)); + var attribute = new TagHelperAttribute(name, value); + Items.Add(attribute); } /// - /// - /// 's must not be null. - /// public void Add(TagHelperAttribute attribute) { if (attribute == null) @@ -196,22 +141,10 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers throw new ArgumentNullException(nameof(attribute)); } - if (attribute.Name == null) - { - throw new ArgumentException( - Resources.FormatTagHelperAttributeList_CannotAddWithNullName( - typeof(TagHelperAttribute).FullName, - nameof(TagHelperAttribute.Name)), - nameof(attribute)); - } - - Attributes.Add(attribute); + Items.Add(attribute); } /// - /// - /// 's must not be null. - /// public void Insert(int index, TagHelperAttribute attribute) { if (attribute == null) @@ -219,27 +152,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers throw new ArgumentNullException(nameof(attribute)); } - if (attribute.Name == null) - { - throw new ArgumentException( - Resources.FormatTagHelperAttributeList_CannotAddWithNullName( - typeof(TagHelperAttribute).FullName, - nameof(TagHelperAttribute.Name)), - nameof(attribute)); - } - - Attributes.Insert(index, attribute); - } - - /// - public void CopyTo(TagHelperAttribute[] array, int index) - { - if (array == null) - { - throw new ArgumentNullException(nameof(array)); - } - - Attributes.CopyTo(array, index); + Items.Insert(index, attribute); } /// @@ -253,13 +166,13 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers throw new ArgumentNullException(nameof(attribute)); } - return Attributes.Remove(attribute); + return Items.Remove(attribute); } /// public void RemoveAt(int index) { - Attributes.RemoveAt(index); + Items.RemoveAt(index); } /// @@ -282,11 +195,11 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers // Perf: Avoid allocating enumerator var removedAtLeastOne = false; - for (var i = Attributes.Count - 1; i >= 0; i--) + for (var i = Items.Count - 1; i >= 0; i--) { - if (NameEquals(name, Attributes[i])) + if (NameEquals(name, Items[i])) { - Attributes.RemoveAt(i); + Items.RemoveAt(i); removedAtLeastOne = true; } } @@ -297,7 +210,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers /// public void Clear() { - Attributes.Clear(); + Items.Clear(); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperContext.cs b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperContext.cs index 5f7d590e5d..6cc4d1c437 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperContext.cs +++ b/src/Microsoft.AspNetCore.Razor.Runtime/TagHelpers/TagHelperContext.cs @@ -11,8 +11,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers /// public class TagHelperContext { - private ReadOnlyTagHelperAttributeList _allAttributes; - private IEnumerable _allAttributesData; + private ReadOnlyTagHelperAttributeList _allAttributes; + private IEnumerable _allAttributesData; /// /// Instantiates a new . @@ -22,7 +22,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers /// The unique identifier for the source element this /// applies to. public TagHelperContext( - IEnumerable allAttributes, + IEnumerable allAttributes, IDictionary items, string uniqueId) { @@ -49,13 +49,13 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers /// /// Every attribute associated with the current HTML element. /// - public ReadOnlyTagHelperAttributeList AllAttributes + public ReadOnlyTagHelperAttributeList AllAttributes { get { if (_allAttributes == null) { - _allAttributes = new ReadOnlyTagHelperAttributeList(_allAttributesData); + _allAttributes = new TagHelperAttributeList(_allAttributesData); } return _allAttributes; diff --git a/src/Microsoft.AspNetCore.Razor.Runtime/project.json b/src/Microsoft.AspNetCore.Razor.Runtime/project.json index e1da270db5..1bed60923f 100644 --- a/src/Microsoft.AspNetCore.Razor.Runtime/project.json +++ b/src/Microsoft.AspNetCore.Razor.Runtime/project.json @@ -20,6 +20,10 @@ "Microsoft.Extensions.CopyOnWriteDictionary.Sources": { "type": "build", "version": "1.0.0-*" + }, + "Microsoft.Extensions.HashCodeCombiner.Sources": { + "type": "build", + "version": "1.0.0-*" } }, "frameworks": { @@ -28,12 +32,7 @@ "System.Xml": "4.0.0.0", "System.Xml.Linq": "4.0.0.0" }, - "dependencies": { - "Microsoft.Extensions.HashCodeCombiner.Sources": { - "type": "build", - "version": "1.0.0-*" - } - } + "dependencies": {} }, "dotnet5.4": { "dependencies": { diff --git a/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs b/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs index aa10345f4f..78ca8ed104 100644 --- a/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs +++ b/src/Microsoft.AspNetCore.Razor/CodeGenerators/CSharpTagHelperCodeRenderer.cs @@ -244,7 +244,7 @@ namespace Microsoft.AspNetCore.Razor.CodeGenerators var renderedBoundAttributeNames = new HashSet(StringComparer.OrdinalIgnoreCase); // Go through the HTML attributes in source order, assigning to properties or indexers or adding to - // TagHelperExecutionContext.HTMLAttributes' as we go. + // TagHelperExecutionContext.HtmlAttributes' as we go. foreach (var attribute in chunkAttributes) { var attributeName = attribute.Key; diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/CaseSensitiveTagHelperAttributeComparer.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/CaseSensitiveTagHelperAttributeComparer.cs index 894d2a65c1..9943d38e51 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/CaseSensitiveTagHelperAttributeComparer.cs +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/CaseSensitiveTagHelperAttributeComparer.cs @@ -6,7 +6,7 @@ using System.Collections.Generic; namespace Microsoft.AspNetCore.Razor.TagHelpers { - public class CaseSensitiveTagHelperAttributeComparer : IEqualityComparer + public class CaseSensitiveTagHelperAttributeComparer : IEqualityComparer { public readonly static CaseSensitiveTagHelperAttributeComparer Default = new CaseSensitiveTagHelperAttributeComparer(); @@ -15,7 +15,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers { } - public bool Equals(IReadOnlyTagHelperAttribute attributeX, IReadOnlyTagHelperAttribute attributeY) + public bool Equals(TagHelperAttribute attributeX, TagHelperAttribute attributeY) { if (attributeX == attributeY) { @@ -29,7 +29,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers (attributeX.Minimized || Equals(attributeX.Value, attributeY.Value)); } - public int GetHashCode(IReadOnlyTagHelperAttribute attribute) + public int GetHashCode(TagHelperAttribute attribute) { return attribute.GetHashCode(); } diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperExecutionContextTest.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperExecutionContextTest.cs index ff0ce1a794..08cef8556e 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperExecutionContextTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperExecutionContextTest.cs @@ -289,13 +289,13 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers { // Arrange var executionContext = new TagHelperExecutionContext("p", TagMode.StartTagAndEndTag); - executionContext.HTMLAttributes[originalName] = "hello"; + executionContext.HtmlAttributes.SetAttribute(originalName, "hello"); // Act - executionContext.HTMLAttributes[updatedName] = "something else"; + executionContext.HtmlAttributes.SetAttribute(updatedName, "something else"); // Assert - var attribute = Assert.Single(executionContext.HTMLAttributes); + var attribute = Assert.Single(executionContext.HtmlAttributes); Assert.Equal(new TagHelperAttribute(originalName, "something else"), attribute); } @@ -305,10 +305,10 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers { // Arrange var executionContext = new TagHelperExecutionContext("p", tagMode: TagMode.StartTagAndEndTag); - executionContext.AllAttributes.Add(originalName, value: false); + executionContext.AllAttributes.SetAttribute(originalName, value: false); // Act - executionContext.AllAttributes[updatedName].Value = true; + executionContext.AllAttributes.SetAttribute(updatedName, true); // Assert var attribute = Assert.Single(executionContext.AllAttributes); @@ -316,7 +316,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers } [Fact] - public void AddHtmlAttribute_MaintainsHTMLAttributes() + public void AddHtmlAttribute_MaintainsHtmlAttributes() { // Arrange var executionContext = new TagHelperExecutionContext("p", TagMode.StartTagAndEndTag); @@ -333,19 +333,19 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers // Assert Assert.Equal( expectedAttributes, - executionContext.HTMLAttributes, + executionContext.HtmlAttributes, CaseSensitiveTagHelperAttributeComparer.Default); } [Fact] - public void AddMinimizedHtmlAttribute_MaintainsHTMLAttributes() + public void AddMinimizedHtmlAttribute_MaintainsHtmlAttributes() { // Arrange var executionContext = new TagHelperExecutionContext("input", tagMode: TagMode.StartTagOnly); var expectedAttributes = new TagHelperAttributeList { - ["checked"] = new TagHelperAttribute { Name = "checked", Minimized = true }, - ["visible"] = new TagHelperAttribute { Name = "visible", Minimized = true } + new TagHelperAttribute("checked"), + new TagHelperAttribute("visible"), }; // Act @@ -355,12 +355,12 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers // Assert Assert.Equal( expectedAttributes, - executionContext.HTMLAttributes, + executionContext.HtmlAttributes, CaseSensitiveTagHelperAttributeComparer.Default); } [Fact] - public void AddMinimizedHtmlAttribute_MaintainsHTMLAttributes_SomeMinimized() + public void AddMinimizedHtmlAttribute_MaintainsHtmlAttributes_SomeMinimized() { // Arrange var executionContext = new TagHelperExecutionContext("input", tagMode: TagMode.SelfClosing); @@ -369,8 +369,8 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers { "class", "btn" }, { "foo", "bar" } }; - expectedAttributes.Add(new TagHelperAttribute { Name = "checked", Minimized = true }); - expectedAttributes.Add(new TagHelperAttribute { Name = "visible", Minimized = true }); + expectedAttributes.Add(new TagHelperAttribute(name: "checked")); + expectedAttributes.Add(new TagHelperAttribute(name: "visible")); // Act executionContext.AddHtmlAttribute("class", "btn"); @@ -381,7 +381,7 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers // Assert Assert.Equal( expectedAttributes, - executionContext.HTMLAttributes, + executionContext.HtmlAttributes, CaseSensitiveTagHelperAttributeComparer.Default); } diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperRunnerTest.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperRunnerTest.cs index 76a6f53f89..7150164e17 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperRunnerTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/Runtime/TagHelpers/TagHelperRunnerTest.cs @@ -216,10 +216,10 @@ namespace Microsoft.AspNetCore.Razor.Runtime.TagHelpers output.TagName = "foo"; - TagHelperAttribute classAttribute; - if (output.Attributes.TryGetAttribute("class", out classAttribute)) + var classIndex = output.Attributes.IndexOfName("class"); + if (classIndex != -1) { - classAttribute.Value = "somethingelse"; + output.Attributes[classIndex] = new TagHelperAttribute("class", "somethingelse"); } output.Attributes.Add("hello", "world"); diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/ReadOnlyTagHelperAttributeListTest.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/ReadOnlyTagHelperAttributeListTest.cs index db132bfe03..99e35be6de 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/ReadOnlyTagHelperAttributeListTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/ReadOnlyTagHelperAttributeListTest.cs @@ -10,6 +10,50 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers { public class ReadOnlyTagHelperAttributeListTest { + public static TheoryData IndexOfNameData + { + get + { + var first = new TagHelperAttribute("First", "First Value"); + var second = new TagHelperAttribute("Second", "Second Value"); + var third = new TagHelperAttribute("Third", "Third Value"); + + return new TheoryData< + IEnumerable, // initialAttributes + string, // nameToLookup + int> // expectedIndex + { + { new[] { first }, first.Name, 0 }, + { new[] { first, second }, first.Name, 0 }, + { new[] { first, second }, second.Name.ToUpper(), 1 }, + { new[] { first, second, third}, second.Name, 1 }, + { new[] { first, second, third }, third.Name.ToLower(), 2 }, + { new[] { first, first, second, third}, first.Name, 0 }, + + // Bad lookups + { new[] { first, second, third}, "bad", -1 }, + { new[] { first, first, first}, first.Name + "bad", -1 }, + }; + } + } + + [Theory] + [MemberData(nameof(IndexOfNameData))] + public void IndexOfName_ReturnsExpectedValue( + IEnumerable initialAttributes, + string nameToLookup, + int expectedIndex) + { + // Arrange + var attributes = new TagHelperAttributeList(initialAttributes); + + // Act + var index = attributes.IndexOfName(nameToLookup); + + // Assert + Assert.Equal(expectedIndex, index); + } + public static TheoryData IntIndexerData { get @@ -19,9 +63,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers var third = new TagHelperAttribute("Third", "Third Value"); return new TheoryData< - IEnumerable, // initialAttributes + IEnumerable, // initialAttributes int, // indexToLookup - IReadOnlyTagHelperAttribute> // expectedAttribute + TagHelperAttribute> // expectedAttribute { { new[] { first }, 0, first }, { new[] { first, second }, 0, first }, @@ -36,12 +80,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(IntIndexerData))] public void IntIndexer_ReturnsExpectedAttribute( - IEnumerable initialAttributes, + IEnumerable initialAttributes, int indexToLookup, - IReadOnlyTagHelperAttribute expectedAttribute) + TagHelperAttribute expectedAttribute) { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList(initialAttributes); + var attributes = new TagHelperAttributeList(initialAttributes); // Act var attribute = attributes[indexToLookup]; @@ -63,7 +107,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers public void IntIndexer_ThrowsIfInvalidIndex(int index) { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList( + var attributes = new TagHelperAttributeList( new[] { new TagHelperAttribute("a", "av"), @@ -85,9 +129,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers var C = new TagHelperAttribute("CName", "CName Value"); return new TheoryData< - IEnumerable, // initialAttributes + IEnumerable, // initialAttributes string, // nameToLookup - IReadOnlyTagHelperAttribute> // expectedAttribute + TagHelperAttribute> // expectedAttribute { { new[] { A }, "AName", A }, { new[] { A }, "AnAmE", A }, @@ -128,12 +172,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(StringIndexerData))] public void StringIndexer_ReturnsExpectedAttribute( - IEnumerable initialAttributes, + IEnumerable initialAttributes, string nameToLookup, - IReadOnlyTagHelperAttribute expectedAttribute) + TagHelperAttribute expectedAttribute) { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList(initialAttributes); + var attributes = new TagHelperAttributeList(initialAttributes); // Act var attribute = attributes[nameToLookup]; @@ -146,12 +190,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers public void Count_ReturnsNumberOfAttributes() { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList( + var attributes = new TagHelperAttributeList( new[] { - new TagHelperAttribute(), - new TagHelperAttribute(), - new TagHelperAttribute() + new TagHelperAttribute("A"), + new TagHelperAttribute("B"), + new TagHelperAttribute("C") }); // Act @@ -172,8 +216,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers var C = new TagHelperAttribute("CName", "CName Value"); return new TheoryData< - IEnumerable, // initialAttributes - IReadOnlyTagHelperAttribute, // attributeToLookup + IEnumerable, // initialAttributes + TagHelperAttribute, // attributeToLookup bool> // expected { { new[] { A }, A, true }, @@ -255,12 +299,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(ContainsData))] public void Contains_ReturnsExpectedResult( - IEnumerable initialAttributes, - IReadOnlyTagHelperAttribute attributeToLookup, + IEnumerable initialAttributes, + TagHelperAttribute attributeToLookup, bool expected) { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList(initialAttributes); + var attributes = new TagHelperAttributeList(initialAttributes); // Act var contains = attributes.Contains(attributeToLookup); @@ -280,7 +324,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers var C = new TagHelperAttribute("CName", "CName Value"); return new TheoryData< - IEnumerable, // initialAttributes + IEnumerable, // initialAttributes string, // nameToLookup bool> // expected { @@ -331,12 +375,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(ContainsNameData))] public void ContainsName_ReturnsExpectedResult( - IEnumerable initialAttributes, + IEnumerable initialAttributes, string nameToLookup, bool expected) { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList(initialAttributes); + var attributes = new TagHelperAttributeList(initialAttributes); // Act var contains = attributes.ContainsName(nameToLookup); @@ -356,8 +400,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers var C = new TagHelperAttribute("CName", "CName Value"); return new TheoryData< - IEnumerable, // initialAttributes - IReadOnlyTagHelperAttribute, // attributeToLookup + IEnumerable, // initialAttributes + TagHelperAttribute, // attributeToLookup int> // expected { { new[] { A }, A, 0 }, @@ -439,12 +483,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(IndexOfData))] public void IndexOf_ReturnsExpectedResult( - IEnumerable initialAttributes, - IReadOnlyTagHelperAttribute attributeToLookup, + IEnumerable initialAttributes, + TagHelperAttribute attributeToLookup, int expected) { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList(initialAttributes); + var attributes = new TagHelperAttributeList(initialAttributes); // Act var index = attributes.IndexOf(attributeToLookup); @@ -464,9 +508,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers var C = new TagHelperAttribute("CName", "CName Value"); return new TheoryData< - IEnumerable, // initialAttributes + IEnumerable, // initialAttributes string, // nameToLookup - IReadOnlyTagHelperAttribute, // expectedAttribute + TagHelperAttribute, // expectedAttribute bool> // expectedResult { { new[] { A }, "AName", A, true }, @@ -508,14 +552,14 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(TryGetAttributeData))] public void TryGetAttribute_ReturnsExpectedValueAndAttribute( - IEnumerable initialAttributes, + IEnumerable initialAttributes, string nameToLookup, - IReadOnlyTagHelperAttribute expectedAttribute, + TagHelperAttribute expectedAttribute, bool expectedResult) { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList(initialAttributes); - IReadOnlyTagHelperAttribute attribute; + var attributes = new TagHelperAttributeList(initialAttributes); + TagHelperAttribute attribute; // Act var result = attributes.TryGetAttribute(nameToLookup, out attribute); @@ -536,9 +580,9 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers var C = new TagHelperAttribute("CName", "CName Value"); return new TheoryData< - IEnumerable, // initialAttributes + IEnumerable, // initialAttributes string, // nameToLookup - IEnumerable, // expectedAttributes + IEnumerable, // expectedAttributes bool> // expectedResult { { new[] { A }, "AName", new[] { A }, true }, @@ -563,16 +607,16 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers { new[] { C, B, A3, A }, "AName", new[] { A3, A }, true }, // Null expected lookups - { new[] { A }, "_AName_", Enumerable.Empty(), false }, - { new[] { A }, "completely different", Enumerable.Empty(), false }, - { new[] { A, B }, "_AName_", Enumerable.Empty(), false }, - { new[] { A, B }, "completely different", Enumerable.Empty(), false }, - { new[] { A, B, C }, "_BName_", Enumerable.Empty(), false }, - { new[] { A, B, C }, "way different", Enumerable.Empty(), false }, - { new[] { A, A2, B, C }, "_cnamE_", Enumerable.Empty(), false }, - { new[] { A, A2, B, C }, "way different", Enumerable.Empty(), false }, - { new[] { A, A2, A3, B, C }, "_cnamE_", Enumerable.Empty(), false }, - { new[] { A, A2, A3, B, C }, "different", Enumerable.Empty(), false }, + { new[] { A }, "_AName_", Enumerable.Empty(), false }, + { new[] { A }, "completely different", Enumerable.Empty(), false }, + { new[] { A, B }, "_AName_", Enumerable.Empty(), false }, + { new[] { A, B }, "completely different", Enumerable.Empty(), false }, + { new[] { A, B, C }, "_BName_", Enumerable.Empty(), false }, + { new[] { A, B, C }, "way different", Enumerable.Empty(), false }, + { new[] { A, A2, B, C }, "_cnamE_", Enumerable.Empty(), false }, + { new[] { A, A2, B, C }, "way different", Enumerable.Empty(), false }, + { new[] { A, A2, A3, B, C }, "_cnamE_", Enumerable.Empty(), false }, + { new[] { A, A2, A3, B, C }, "different", Enumerable.Empty(), false }, }; } } @@ -580,14 +624,14 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(TryGetAttributesData))] public void TryGetAttributes_ReturnsExpectedValueAndAttribute( - IEnumerable initialAttributes, + IEnumerable initialAttributes, string nameToLookup, - IEnumerable expectedAttributes, + IEnumerable expectedAttributes, bool expectedResult) { // Arrange - var attributes = new ReadOnlyTagHelperAttributeList(initialAttributes); - IReadOnlyList resolvedAttributes; + var attributes = new TagHelperAttributeList(initialAttributes); + IReadOnlyList resolvedAttributes; // Act var result = attributes.TryGetAttributes(nameToLookup, out resolvedAttributes); @@ -632,7 +676,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers public void ModifyingUnderlyingAttributes_AffectsExposedAttributes() { // Arrange - var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty()); + var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty()); var expectedAttributes = new[] { new TagHelperAttribute("A", "AV"), @@ -650,12 +694,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(IntIndexerData))] public void ModifyingUnderlyingAttributes_IntIndexer_ReturnsExpectedResult( - IEnumerable initialAttributes, + IEnumerable initialAttributes, int indexToLookup, - IReadOnlyTagHelperAttribute expectedAttribute) + TagHelperAttribute expectedAttribute) { // Arrange - var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty()); + var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty()); attributes.PublicAttributes.AddRange(initialAttributes); // Act @@ -668,12 +712,12 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers [Theory] [MemberData(nameof(StringIndexerData))] public void ModifyingUnderlyingAttributes_StringIndexer_ReturnsExpectedResult( - IEnumerable initialAttributes, + IEnumerable initialAttributes, string nameToLookup, - IReadOnlyTagHelperAttribute expectedAttribute) + TagHelperAttribute expectedAttribute) { // Arrange - var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty()); + var attributes = new TestableReadOnlyTagHelperAttributes(Enumerable.Empty()); attributes.PublicAttributes.AddRange(initialAttributes); // Act @@ -683,18 +727,18 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers Assert.Equal(expectedAttribute, attribute, CaseSensitiveTagHelperAttributeComparer.Default); } - private class TestableReadOnlyTagHelperAttributes : ReadOnlyTagHelperAttributeList + private class TestableReadOnlyTagHelperAttributes : ReadOnlyTagHelperAttributeList { - public TestableReadOnlyTagHelperAttributes(IEnumerable attributes) + public TestableReadOnlyTagHelperAttributes(IEnumerable attributes) : base(attributes) { } - public List PublicAttributes + public List PublicAttributes { get { - return Attributes; + return (List)Items; } } } diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperAttributeListTest.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperAttributeListTest.cs index b2b0b069b2..e29630d25b 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperAttributeListTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperAttributeListTest.cs @@ -133,64 +133,49 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers get { var A = new TagHelperAttribute("AName", "AName Value"); - var ASet = new TagHelperAttribute("aname", "AName Set Value"); + var ASet1 = new TagHelperAttribute("AName", "AName Set Value"); + var ASet2 = new TagHelperAttribute("AnAmE", "AName Set Value"); var A2 = new TagHelperAttribute("aname", "AName Second Value"); - var A2Set = new TagHelperAttribute("aname", "AName Second Set Value"); var A3 = new TagHelperAttribute("AName", "AName Third Value"); var A3Set = new TagHelperAttribute("aname", "AName Third Set Value"); var B = new TagHelperAttribute("BName", "BName Value"); - var BSet = new TagHelperAttribute("bname", "BName Set Value"); + var BSet1 = new TagHelperAttribute("BName", "BName Set Value"); + var BSet2 = new TagHelperAttribute("BnAmE", "BName Set Value"); var C = new TagHelperAttribute("CName", "CName Value"); - var CSet = new TagHelperAttribute("cname", "CName Set Value"); + var CSet1 = new TagHelperAttribute("CName", "CName Set Value"); + var CSet2 = new TagHelperAttribute("cnamE", "CName Set Value"); var set = new TagHelperAttribute("Set", "Set Value"); return new TheoryData< IEnumerable, // initialAttributes string, // keyToSet - TagHelperAttribute, // setValue + object, // setValue IEnumerable> // expectedAttributes { - { new[] { A }, "AName", ASet, new[] { ASet } }, - { new[] { A }, "AnAmE", ASet, new[] { ASet } }, - { new[] { A }, "AnAmE", "AV", new[] { new TagHelperAttribute("AnAmE", "AV") } }, - { new[] { A, B }, "AName", ASet, new[] { ASet, B } }, - { new[] { A, B }, "AnAmE", ASet, new[] { ASet, B } }, - { new[] { A, B }, "AnAmE", "AV", new[] { new TagHelperAttribute("AnAmE", "AV"), B } }, - { new[] { A, B }, "BName", BSet, new[] { A, BSet } }, - { new[] { A, B }, "BnAmE", BSet, new[] { A, BSet } }, - { new[] { A, B }, "BnAmE", "BV", new[] { A, new TagHelperAttribute("BnAmE", "BV") } }, - { new[] { A, B, C }, "BName", BSet, new[] { A, BSet, C } }, - { new[] { A, B, C }, "bname", BSet, new[] { A, BSet, C } }, - { new[] { A, B, C }, "bname", "BV", new[] { A, new TagHelperAttribute("bname", "BV"), C } }, - { new[] { A, B, C }, "CName", CSet, new[] { A, B, CSet } }, - { new[] { A, B, C }, "cnamE", CSet, new[] { A, B, CSet } }, - { new[] { A, B, C }, "cnamE", "CV", new[] { A, B, new TagHelperAttribute("cnamE", "CV") } }, - { Enumerable.Empty(), "Set", set, new[] { set } }, - { new[] { B }, "Set", set, new[] { B, set } }, - { new[] { B }, "Set", "Set Value", new[] { B, set } }, - { new[] { A, B }, "Set", set, new[] { A, B, set } }, - { new[] { A, B }, "Set", "Set Value", new[] { A, B, set } }, + { new[] { A }, "AName", ASet1.Value, new[] { ASet1 } }, + { new[] { A }, "AnAmE", ASet2.Value, new[] { ASet2 } }, + { new[] { A, B }, "AName", ASet1.Value, new[] { ASet1, B } }, + { new[] { A, B }, "AnAmE", ASet2.Value, new[] { ASet2, B } }, + { new[] { A, B }, "BName", BSet1.Value, new[] { A, BSet1 } }, + { new[] { A, B }, "BnAmE", BSet2.Value, new[] { A, BSet2 } }, + { new[] { A, B, C }, "BName", BSet1.Value, new[] { A, BSet1, C } }, + { new[] { A, B, C }, "BnAmE", BSet2.Value, new[] { A, BSet2, C } }, + { new[] { A, B, C }, "CName", CSet1.Value, new[] { A, B, CSet1 } }, + { new[] { A, B, C }, "cnamE", CSet2.Value, new[] { A, B, CSet2 } }, + { Enumerable.Empty(), "Set", set.Value, new[] { set } }, + { new[] { B }, "Set", set.Value, new[] { B, set } }, + { new[] { A, B }, "Set", set.Value, new[] { A, B, set } }, // Multiple elements same name - { new[] { A, B, A2, C }, "AName", ASet, new[] { ASet, B, C } }, - { new[] { A, B, A2, C }, "aname", ASet, new[] { ASet, B, C } }, - { new[] { A, B, A2, C }, "aname", "av", new[] { new TagHelperAttribute("aname", "av"), B, C } }, - { new[] { B, A2, A }, "aname", A2Set, new[] { B, A2Set } }, - { new[] { B, A2, A }, "aname", "av", new[] { B, new TagHelperAttribute("aname", "av") } }, - { new[] { B, A2, A, C }, "AName", A2Set, new[] { B, A2Set, C } }, - { new[] { B, A2, A, C }, "AName", "av", new[] { B, new TagHelperAttribute("AName", "av"), C } }, - { new[] { A, A3 }, "AName", ASet, new[] { ASet } }, - { new[] { A, A3 }, "AName", "av", new[] { new TagHelperAttribute("AName", "av") } }, - { new[] { A3, A }, "aname", A3Set, new[] { A3Set } }, - { new[] { A3, A }, "aname", "av", new[] { new TagHelperAttribute("aname", "av") } }, - { new[] { A, A2, A3 }, "AName", ASet, new[] { ASet } }, - { new[] { A, A2, A3 }, "AName", "av", new[] { new TagHelperAttribute("AName", "av") } }, - { new[] { C, B, A3, A }, "AName", A3Set, new[] { C, B, A3Set } }, - { new[] { C, B, A3, A }, "AName", "av", new[] { C, B, new TagHelperAttribute("AName", "av") } }, - { new[] { A, A2, A3 }, "BNamE", BSet, new[] { A, A2, A3, BSet } }, - { new[] { A, A2, A3 }, "bname", "BName Set Value", new[] { A, A2, A3, BSet } }, - { new[] { A, A2, A3, B, C }, "Set", set, new[] { A, A2, A3, B, C, set } }, - { new[] { A, A2, A3, B, C }, "Set", "Set Value", new[] { A, A2, A3, B, C, set } }, + { new[] { A, B, A2, C }, "AName", ASet1.Value, new[] { ASet1, B, C } }, + { new[] { A, B, A2, C }, "AnAmE", ASet2.Value, new[] { ASet2, B, C } }, + { new[] { B, A2, A }, "AName", ASet1.Value, new[] { B, ASet1 } }, + { new[] { B, A2, A, C }, "AnAmE", ASet2.Value, new[] { B, ASet2, C } }, + { new[] { A, A3 }, "aname", A3Set.Value, new[] { A3Set } }, + { new[] { A3, A }, "aname", A3Set.Value, new[] { A3Set } }, + { new[] { A, A2, A3 }, "AName", ASet1.Value, new[] { ASet1 } }, + { new[] { A, A2, A3 }, "BName", BSet1.Value, new[] { A, A2, A3, BSet1 } }, + { new[] { A, A2, A3, B, C }, "Set", set.Value, new[] { A, A2, A3, B, C, set } }, }; } } @@ -200,14 +185,14 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers public void StringIndexer_SetsAttributeAtExpectedLocation( IEnumerable initialAttributes, string keyToSet, - TagHelperAttribute setValue, + object setValue, IEnumerable expectedAttributes) { // Arrange var attributes = new TagHelperAttributeList(initialAttributes); // Act - attributes[keyToSet] = setValue; + attributes.SetAttribute(keyToSet, setValue); // Assert Assert.Equal(expectedAttributes, attributes, CaseSensitiveTagHelperAttributeComparer.Default); @@ -230,42 +215,6 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers }); } - public static TheoryData StringIndexerSetterThrowData - { - get - { - // attributes - return new TheoryData - { - { new TagHelperAttributeList() }, - { new TagHelperAttributeList { { "something", "a value" } } }, - { new TagHelperAttributeList { { "somethingelse", "a value" } } }, - { new TagHelperAttributeList { { "SomethingElse", "a value" } } }, - { new TagHelperAttributeList { { "something", "a value" }, { "somethingelse", "a value" } } }, - { new TagHelperAttributeList { { "SomethingElse", "a value" }, { "somethingelse", "a value" } } } - }; - } - } - - [Theory] - [MemberData(nameof(StringIndexerSetterThrowData))] - public void StringIndexer_Setter_ThrowsIfUnmatchingKey( - TagHelperAttributeList attributes) - { - // Arrange - var expectedMessage = $"Cannot add a {nameof(TagHelperAttribute)} with inconsistent names. The " + - $"{nameof(TagHelperAttribute.Name)} property 'somethingelse' must match the location 'something'." + - $"{Environment.NewLine}Parameter name: name"; - - // Act & Assert - var exception = Assert.Throws("name", () => - { - attributes["something"] = new TagHelperAttribute("somethingelse", "value"); - }); - Assert.Equal(expectedMessage, exception.Message); - } - - [Fact] public void ICollection_IsReadOnly_ReturnsFalse() { @@ -316,37 +265,6 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers Assert.Equal(expectedAttributes, attributes, CaseSensitiveTagHelperAttributeComparer.Default); } - [Fact] - public void Add_ThrowsWhenNameAndValueAreNull() - { - // Arrange - var attributes = new TagHelperAttributeList(); - var expectedMessage = $"Cannot add a '{typeof(TagHelperAttribute).FullName}' with a null " + - $"'{nameof(TagHelperAttribute.Name)}'.{Environment.NewLine}Parameter name: attribute"; - - // Act & Assert - var exception = Assert.Throws("attribute", - () => attributes.Add(new TagHelperAttribute())); - Assert.Equal(expectedMessage, exception.Message); - } - - [Fact] - public void Add_ThrowsWhenNameIsNull() - { - // Arrange - var attributes = new TagHelperAttributeList(); - var expectedMessage = $"Cannot add a '{typeof(TagHelperAttribute).FullName}' with a null " + - $"'{nameof(TagHelperAttribute.Name)}'.{Environment.NewLine}Parameter name: attribute"; - - // Act & Assert - var exception = Assert.Throws("attribute", - () => attributes.Add(new TagHelperAttribute - { - Value = "Anything" - })); - Assert.Equal(expectedMessage, exception.Message); - } - public static TheoryData InsertData { get @@ -391,23 +309,6 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers Assert.Equal(expectedAttributes, attributes, CaseSensitiveTagHelperAttributeComparer.Default); } - [Fact] - public void Insert_ThrowsWhenNameIsNull() - { - // Arrange - var attributes = new TagHelperAttributeList(); - var expectedMessage = $"Cannot add a '{typeof(TagHelperAttribute).FullName}' with a null " + - $"'{nameof(TagHelperAttribute.Name)}'.{Environment.NewLine}Parameter name: attribute"; - - // Act & Assert - var exception = Assert.Throws("attribute", - () => attributes.Insert(0, new TagHelperAttribute - { - Value = "Anything" - })); - Assert.Equal(expectedMessage, exception.Message); - } - [Fact] public void Insert_ThrowsWhenIndexIsOutOfRange() { @@ -585,17 +486,17 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers { new[] { C, B, A3, A }, new TagHelperAttribute("AName", "av"), new[] { C, B, A }, true }, // Null expected lookups - { Enumerable.Empty(), "_0_", Enumerable.Empty(), false }, - { new[] { A }, "_AName_", new[] { A }, false }, - { new[] { A }, "completely different", new[] { A }, false }, - { new[] { A, B }, "_AName_", new[] { A, B }, false }, - { new[] { A, B }, "completely different", new[] { A, B }, false }, - { new[] { A, B, C }, "_BName_", new[] { A, B, C }, false }, - { new[] { A, B, C }, "completely different", new[] { A, B, C }, false }, - { new[] { A, A2, B, C }, "_cnamE_", new[] { A, A2, B, C }, false }, - { new[] { A, A2, B, C }, "completely different", new[] { A, A2, B, C }, false }, - { new[] { A, A2, A3, B, C }, "_cnamE_", new[] { A, A2, A3, B, C }, false }, - { new[] { A, A2, A3, B, C }, "completely different", new[] { A, A2, A3, B, C }, false }, + { Enumerable.Empty(), new TagHelperAttribute("DoesNotExist", "_0_"), Enumerable.Empty(), false }, + { new[] { A }, new TagHelperAttribute("DoesNotExist", "_AName_"), new[] { A }, false }, + { new[] { A }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A }, false }, + { new[] { A, B }, new TagHelperAttribute("DoesNotExist", "_AName_"), new[] { A, B }, false }, + { new[] { A, B }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A, B }, false }, + { new[] { A, B, C }, new TagHelperAttribute("DoesNotExist", "_BName_"), new[] { A, B, C }, false }, + { new[] { A, B, C }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A, B, C }, false }, + { new[] { A, A2, B, C }, new TagHelperAttribute("DoesNotExist", "_cnamE_"), new[] { A, A2, B, C }, false }, + { new[] { A, A2, B, C }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A, A2, B, C }, false }, + { new[] { A, A2, A3, B, C }, new TagHelperAttribute("DoesNotExist", "_cnamE_"), new[] { A, A2, A3, B, C }, false }, + { new[] { A, A2, A3, B, C }, new TagHelperAttribute("DoesNotExist", "completely different"), new[] { A, A2, A3, B, C }, false }, }; } } diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperAttributeTest.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperAttributeTest.cs deleted file mode 100644 index 2f935b3a4f..0000000000 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperAttributeTest.cs +++ /dev/null @@ -1,36 +0,0 @@ -// 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 Xunit; - -namespace Microsoft.AspNetCore.Razor.TagHelpers -{ - public class TagHelperAttributeTest - { - public static TheoryData CopyConstructorData - { - get - { - return new TheoryData - { - new TagHelperAttribute("hello", "world") { Minimized = false }, - new TagHelperAttribute("checked", value: null) { Minimized = true }, - }; - } - } - - [Theory] - [MemberData(nameof(CopyConstructorData))] - public void CopyConstructorCopiesValuesAsExpected(IReadOnlyTagHelperAttribute readOnlyTagHelperAttribute) - { - // Act - var tagHelperAttribute = new TagHelperAttribute(readOnlyTagHelperAttribute); - - // Assert - Assert.Equal( - readOnlyTagHelperAttribute, - tagHelperAttribute, - CaseSensitiveTagHelperAttributeComparer.Default); - } - } -} diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperContextTest.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperContextTest.cs index 1c28af757d..42f010a268 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperContextTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperContextTest.cs @@ -3,9 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Razor.Runtime.TagHelpers; using Xunit; namespace Microsoft.AspNetCore.Razor.TagHelpers @@ -23,7 +20,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers // Act var context = new TagHelperContext( - allAttributes: Enumerable.Empty(), + allAttributes: new TagHelperAttributeList(), items: expectedItems, uniqueId: string.Empty); diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs index 02d02a5012..e82aae5c96 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/TagHelpers/TagHelperOutputTest.cs @@ -347,7 +347,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers (cachedResult, encoder) => Task.FromResult(new DefaultTagHelperContent())); // Act - tagHelperOutput.Attributes[updateName] = "super button"; + tagHelperOutput.Attributes.SetAttribute(updateName, "super button"); // Assert var attribute = Assert.Single(tagHelperOutput.Attributes); @@ -434,11 +434,7 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers tagName: "p", attributes: new TagHelperAttributeList() { - ["test"] = new TagHelperAttribute - { - Name = "test", - Minimized = true - }, + new TagHelperAttribute("test"), }, tagMode: TagMode.StartTagAndEndTag, preElement: null, @@ -453,16 +449,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers tagName: "p", attributes: new TagHelperAttributeList() { - ["test"] = new TagHelperAttribute - { - Name = "test", - Minimized = true - }, - ["test2"] = new TagHelperAttribute - { - Name = "test2", - Minimized = true - }, + new TagHelperAttribute("test"), + new TagHelperAttribute("test2"), }, tagMode: TagMode.StartTagAndEndTag, preElement: null, @@ -477,12 +465,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers tagName: "p", attributes: new TagHelperAttributeList() { - ["first"] = "unminimized", - ["test"] = new TagHelperAttribute - { - Name = "test", - Minimized = true - }, + new TagHelperAttribute("first", "unminimized"), + new TagHelperAttribute("test"), }, tagMode: TagMode.StartTagAndEndTag, preElement: null, @@ -497,12 +481,8 @@ namespace Microsoft.AspNetCore.Razor.TagHelpers tagName: "p", attributes: new TagHelperAttributeList() { - ["test"] = new TagHelperAttribute - { - Name = "test", - Minimized = true - }, - ["last"] = "unminimized", + new TagHelperAttribute("test"), + new TagHelperAttribute("last", "unminimized"), }, tagMode: TagMode.StartTagAndEndTag, preElement: null, diff --git a/test/Microsoft.AspNetCore.Razor.Runtime.Test/project.json b/test/Microsoft.AspNetCore.Razor.Runtime.Test/project.json index 0818fbe6bf..92eb3fbcc8 100644 --- a/test/Microsoft.AspNetCore.Razor.Runtime.Test/project.json +++ b/test/Microsoft.AspNetCore.Razor.Runtime.Test/project.json @@ -33,11 +33,7 @@ "dnxcore50": { "imports": "portable-net451+win8", "dependencies": { - "xunit.runner.aspnet": "2.0.0-aspnet-*", - "Microsoft.Extensions.HashCodeCombiner.Sources": { - "type": "build", - "version": "1.0.0-*" - } + "xunit.runner.aspnet": "2.0.0-aspnet-*" } } }