diff --git a/src/Microsoft.AspNetCore.Html.Abstractions/HtmlContentBuilder.cs b/src/Microsoft.AspNetCore.Html.Abstractions/HtmlContentBuilder.cs
index b79e400375..be218244b0 100644
--- a/src/Microsoft.AspNetCore.Html.Abstractions/HtmlContentBuilder.cs
+++ b/src/Microsoft.AspNetCore.Html.Abstractions/HtmlContentBuilder.cs
@@ -96,6 +96,70 @@ namespace Microsoft.AspNetCore.Html
return this;
}
+ ///
+ public void CopyTo(IHtmlContentBuilder destination)
+ {
+ if (destination == null)
+ {
+ throw new ArgumentNullException(nameof(destination));
+ }
+
+ for (var i = 0; i < Entries.Count; i++)
+ {
+ var entry = Entries[i];
+
+ string entryAsString;
+ IHtmlContentContainer entryAsContainer;
+ if ((entryAsString = entry as string) != null)
+ {
+ destination.Append(entryAsString);
+ }
+ else if ((entryAsContainer = entry as IHtmlContentContainer) != null)
+ {
+ // Since we're copying, do a deep flatten.
+ entryAsContainer.CopyTo(destination);
+ }
+ else
+ {
+ // Only string, IHtmlContent values can be added to the buffer.
+ destination.AppendHtml((IHtmlContent)entry);
+ }
+ }
+ }
+
+ ///
+ public void MoveTo(IHtmlContentBuilder destination)
+ {
+ if (destination == null)
+ {
+ throw new ArgumentNullException(nameof(destination));
+ }
+
+ for (var i = 0; i < Entries.Count; i++)
+ {
+ var entry = Entries[i];
+
+ string entryAsString;
+ IHtmlContentContainer entryAsContainer;
+ if ((entryAsString = entry as string) != null)
+ {
+ destination.Append(entryAsString);
+ }
+ else if ((entryAsContainer = entry as IHtmlContentContainer) != null)
+ {
+ // Since we're moving, do a deep flatten.
+ entryAsContainer.MoveTo(destination);
+ }
+ else
+ {
+ // Only string, IHtmlContent values can be added to the buffer.
+ destination.AppendHtml((IHtmlContent)entry);
+ }
+ }
+
+ Entries.Clear();
+ }
+
///
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
{
diff --git a/src/Microsoft.AspNetCore.Html.Abstractions/HtmlEncodedString.cs b/src/Microsoft.AspNetCore.Html.Abstractions/HtmlEncodedString.cs
index c50fba890c..bf38ac3682 100644
--- a/src/Microsoft.AspNetCore.Html.Abstractions/HtmlEncodedString.cs
+++ b/src/Microsoft.AspNetCore.Html.Abstractions/HtmlEncodedString.cs
@@ -17,17 +17,20 @@ namespace Microsoft.AspNetCore.Html
///
public static readonly IHtmlContent NewLine = new HtmlEncodedString(Environment.NewLine);
- private readonly string _value;
-
///
/// Creates a new .
///
/// The HTML encoded value.
public HtmlEncodedString(string value)
{
- _value = value;
+ Value = value;
}
+ ///
+ /// Gets the HTML encoded value.
+ ///
+ public string Value { get; }
+
///
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
{
@@ -41,13 +44,13 @@ namespace Microsoft.AspNetCore.Html
throw new ArgumentNullException(nameof(encoder));
}
- writer.Write(_value);
+ writer.Write(Value);
}
///
public override string ToString()
{
- return _value ?? string.Empty;
+ return Value ?? string.Empty;
}
}
}
diff --git a/src/Microsoft.AspNetCore.Html.Abstractions/HtmlTextWriter.cs b/src/Microsoft.AspNetCore.Html.Abstractions/HtmlTextWriter.cs
deleted file mode 100644
index c9158b49b6..0000000000
--- a/src/Microsoft.AspNetCore.Html.Abstractions/HtmlTextWriter.cs
+++ /dev/null
@@ -1,49 +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.IO;
-
-namespace Microsoft.AspNetCore.Html
-{
- ///
- /// A which supports special processing of .
- ///
- public abstract class HtmlTextWriter : TextWriter
- {
- ///
- /// Writes an value.
- ///
- /// The value.
- public abstract void Write(IHtmlContent value);
-
- ///
- public override void Write(object value)
- {
- var htmlContent = value as IHtmlContent;
- if (htmlContent == null)
- {
- base.Write(value);
- }
- else
- {
- Write(htmlContent);
- }
- }
-
- ///
- public override void WriteLine(object value)
- {
- var htmlContent = value as IHtmlContent;
- if (htmlContent == null)
- {
- base.Write(value);
- }
- else
- {
- Write(htmlContent);
- }
-
- base.WriteLine();
- }
- }
-}
diff --git a/src/Microsoft.AspNetCore.Html.Abstractions/IHtmlContentBuilder.cs b/src/Microsoft.AspNetCore.Html.Abstractions/IHtmlContentBuilder.cs
index 978fedc03f..912fe442aa 100644
--- a/src/Microsoft.AspNetCore.Html.Abstractions/IHtmlContentBuilder.cs
+++ b/src/Microsoft.AspNetCore.Html.Abstractions/IHtmlContentBuilder.cs
@@ -6,7 +6,7 @@ namespace Microsoft.AspNetCore.Html
///
/// A builder for HTML content.
///
- public interface IHtmlContentBuilder : IHtmlContent
+ public interface IHtmlContentBuilder : IHtmlContentContainer
{
///
/// Appends an instance.
diff --git a/src/Microsoft.AspNetCore.Html.Abstractions/IHtmlContentContainer.cs b/src/Microsoft.AspNetCore.Html.Abstractions/IHtmlContentContainer.cs
new file mode 100644
index 0000000000..f17811433c
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Html.Abstractions/IHtmlContentContainer.cs
@@ -0,0 +1,30 @@
+// 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.
+
+namespace Microsoft.AspNetCore.Html
+{
+ ///
+ /// Defines a contract for instances made up of several components which
+ /// can be copied into an .
+ ///
+ public interface IHtmlContentContainer : IHtmlContent
+ {
+ ///
+ /// Copies the contained content of this into .
+ ///
+ /// The .
+ void CopyTo(IHtmlContentBuilder builder);
+
+ ///
+ ///
+ /// Moves the contained content of this into .
+ ///
+ ///
+ /// After is called, this instance should be left
+ /// in an empty state.
+ ///
+ ///
+ /// The .
+ void MoveTo(IHtmlContentBuilder builder);
+ }
+}
diff --git a/test/Microsoft.AspNetCore.Html.Abstractions.Test/HtmlContentBuilderExtensionsTest.cs b/test/Microsoft.AspNetCore.Html.Abstractions.Test/HtmlContentBuilderExtensionsTest.cs
index 6b4307f690..08151b3675 100644
--- a/test/Microsoft.AspNetCore.Html.Abstractions.Test/HtmlContentBuilderExtensionsTest.cs
+++ b/test/Microsoft.AspNetCore.Html.Abstractions.Test/HtmlContentBuilderExtensionsTest.cs
@@ -392,6 +392,20 @@ namespace Microsoft.AspNetCore.Html.Test
return this;
}
+ public void CopyTo(IHtmlContentBuilder destination)
+ {
+ foreach (var entry in Entries)
+ {
+ destination.AppendHtml(entry);
+ }
+ }
+
+ public void MoveTo(IHtmlContentBuilder destination)
+ {
+ CopyTo(destination);
+ Clear();
+ }
+
public void WriteTo(TextWriter writer, HtmlEncoder encoder)
{
foreach (var entry in Entries)
diff --git a/test/Microsoft.AspNetCore.Html.Abstractions.Test/HtmlContentBuilderTest.cs b/test/Microsoft.AspNetCore.Html.Abstractions.Test/HtmlContentBuilderTest.cs
index 901512fdad..40219b19f7 100644
--- a/test/Microsoft.AspNetCore.Html.Abstractions.Test/HtmlContentBuilderTest.cs
+++ b/test/Microsoft.AspNetCore.Html.Abstractions.Test/HtmlContentBuilderTest.cs
@@ -1,6 +1,7 @@
// 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 System.IO;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Html;
@@ -105,6 +106,106 @@ namespace Microsoft.Extensions.Internal
Assert.Equal(0, content.Entries.Count);
}
+ [Fact]
+ public void CopyTo_CopiesAllItems()
+ {
+ // Arrange
+ var source = new HtmlContentBuilder();
+ source.AppendHtml(new TestHtmlContent("hello"));
+ source.Append("Test");
+
+ var destination = new HtmlContentBuilder();
+ destination.Append("some-content");
+
+ // Act
+ source.CopyTo(destination);
+
+ // Assert
+ Assert.Equal(2, source.Entries.Count);
+ Assert.Equal(3, destination.Entries.Count);
+
+ Assert.Equal("some-content", Assert.IsType(destination.Entries[0]));
+ Assert.Equal(new TestHtmlContent("hello"), Assert.IsType(destination.Entries[1]));
+ Assert.Equal("Test", Assert.IsType(destination.Entries[2]));
+ }
+
+ [Fact]
+ public void CopyTo_DoesDeepCopy()
+ {
+ // Arrange
+ var source = new HtmlContentBuilder();
+
+ var nested = new HtmlContentBuilder();
+ source.AppendHtml(nested);
+ nested.AppendHtml(new TestHtmlContent("hello"));
+ source.Append("Test");
+
+ var destination = new HtmlContentBuilder();
+ destination.Append("some-content");
+
+ // Act
+ source.CopyTo(destination);
+
+ // Assert
+ Assert.Equal(2, source.Entries.Count);
+ Assert.Equal(1, nested.Entries.Count);
+ Assert.Equal(3, destination.Entries.Count);
+
+ Assert.Equal("some-content", Assert.IsType(destination.Entries[0]));
+ Assert.Equal(new TestHtmlContent("hello"), Assert.IsType(destination.Entries[1]));
+ Assert.Equal("Test", Assert.IsType(destination.Entries[2]));
+ }
+
+ [Fact]
+ public void MoveTo_CopiesAllItems_AndClears()
+ {
+ // Arrange
+ var source = new HtmlContentBuilder();
+ source.AppendHtml(new TestHtmlContent("hello"));
+ source.Append("Test");
+
+ var destination = new HtmlContentBuilder();
+ destination.Append("some-content");
+
+ // Act
+ source.MoveTo(destination);
+
+ // Assert
+ Assert.Equal(0, source.Entries.Count);
+ Assert.Equal(3, destination.Entries.Count);
+
+ Assert.Equal("some-content", Assert.IsType(destination.Entries[0]));
+ Assert.Equal(new TestHtmlContent("hello"), Assert.IsType(destination.Entries[1]));
+ Assert.Equal("Test", Assert.IsType(destination.Entries[2]));
+ }
+
+ [Fact]
+ public void MoveTo_DoesDeepMove()
+ {
+ // Arrange
+ var source = new HtmlContentBuilder();
+
+ var nested = new HtmlContentBuilder();
+ source.AppendHtml(nested);
+ nested.AppendHtml(new TestHtmlContent("hello"));
+ source.Append("Test");
+
+ var destination = new HtmlContentBuilder();
+ destination.Append("some-content");
+
+ // Act
+ source.MoveTo(destination);
+
+ // Assert
+ Assert.Equal(0, source.Entries.Count);
+ Assert.Equal(0, nested.Entries.Count);
+ Assert.Equal(3, destination.Entries.Count);
+
+ Assert.Equal("some-content", Assert.IsType(destination.Entries[0]));
+ Assert.Equal(new TestHtmlContent("hello"), Assert.IsType(destination.Entries[1]));
+ Assert.Equal("Test", Assert.IsType(destination.Entries[2]));
+ }
+
[Fact]
public void WriteTo_WritesAllItems()
{
@@ -122,7 +223,7 @@ namespace Microsoft.Extensions.Internal
Assert.Equal("Written from TestHtmlContent: HelloHtmlEncode[[Test]]", writer.ToString());
}
- private class TestHtmlContent : IHtmlContent
+ private class TestHtmlContent : IHtmlContent, IEquatable
{
private string _content;
@@ -140,6 +241,27 @@ namespace Microsoft.Extensions.Internal
{
return "Written from TestHtmlContent: " + _content;
}
+
+ public override int GetHashCode()
+ {
+ return _content.GetHashCode();
+ }
+
+ public override bool Equals(object obj)
+ {
+ var other = obj as TestHtmlContent;
+ if (other != null)
+ {
+ return Equals(other);
+ }
+
+ return base.Equals(obj);
+ }
+
+ public bool Equals(TestHtmlContent other)
+ {
+ return string.Equals(_content, other._content);
+ }
}
}
}