| File: Buffers\ViewBufferTextWriter.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj (Microsoft.AspNetCore.Mvc.ViewFeatures) |
// 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; using System.Text.Encodings.Web; using Microsoft.AspNetCore.Html; namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Buffers; /// <summary> /// <para> /// A <see cref="TextWriter"/> that is backed by a unbuffered writer (over the Response stream) and/or a /// <see cref="ViewBuffer"/> /// </para> /// <para> /// When <c>Flush</c> or <c>FlushAsync</c> is invoked, the writer copies all content from the buffer to /// the writer and switches to writing to the unbuffered writer for all further write operations. /// </para> /// </summary> internal sealed class ViewBufferTextWriter : TextWriter { private readonly TextWriter _inner; private readonly HtmlEncoder _htmlEncoder; /// <summary> /// Creates a new instance of <see cref="ViewBufferTextWriter"/>. /// </summary> /// <param name="buffer">The <see cref="ViewBuffer"/> for buffered output.</param> /// <param name="encoding">The <see cref="System.Text.Encoding"/>.</param> public ViewBufferTextWriter(ViewBuffer buffer, Encoding encoding) { ArgumentNullException.ThrowIfNull(buffer); ArgumentNullException.ThrowIfNull(encoding); Buffer = buffer; Encoding = encoding; } /// <summary> /// Creates a new instance of <see cref="ViewBufferTextWriter"/>. /// </summary> /// <param name="buffer">The <see cref="ViewBuffer"/> for buffered output.</param> /// <param name="encoding">The <see cref="System.Text.Encoding"/>.</param> /// <param name="htmlEncoder">The HTML encoder.</param> /// <param name="inner"> /// The inner <see cref="TextWriter"/> to write output to when this instance is no longer buffering. /// </param> public ViewBufferTextWriter(ViewBuffer buffer, Encoding encoding, HtmlEncoder htmlEncoder, TextWriter inner) { ArgumentNullException.ThrowIfNull(buffer); ArgumentNullException.ThrowIfNull(encoding); ArgumentNullException.ThrowIfNull(htmlEncoder); ArgumentNullException.ThrowIfNull(inner); Buffer = buffer; Encoding = encoding; _htmlEncoder = htmlEncoder; _inner = inner; } /// <inheritdoc /> public override Encoding Encoding { get; } /// <summary> /// Gets the <see cref="ViewBuffer"/>. /// </summary> public ViewBuffer Buffer { get; } /// <summary> /// Gets a value that indicates if <see cref="Flush"/> or <see cref="FlushAsync" /> was invoked. /// </summary> public bool Flushed { get; private set; } /// <inheritdoc /> public override void Write(char value) { Buffer.AppendHtml(value.ToString()); } /// <inheritdoc /> public override void Write(char[] buffer, int index, int count) { ArgumentNullException.ThrowIfNull(buffer); ArgumentOutOfRangeException.ThrowIfNegative(index); ArgumentOutOfRangeException.ThrowIfNegative(count); ArgumentOutOfRangeException.ThrowIfGreaterThan(count, buffer.Length - index); Buffer.AppendHtml(new string(buffer, index, count)); } /// <inheritdoc /> public override void Write(string value) { if (string.IsNullOrEmpty(value)) { return; } Buffer.AppendHtml(value); } /// <inheritdoc /> public override void Write(object value) { if (value == null) { return; } if (value is IHtmlContentContainer container) { Write(container); } else if (value is IHtmlContent htmlContent) { Write(htmlContent); } else { Write(value.ToString()); } } /// <summary> /// Writes an <see cref="IHtmlContent"/> value. /// </summary> /// <param name="value">The <see cref="IHtmlContent"/> value.</param> public void Write(IHtmlContent value) { if (value == null) { return; } Buffer.AppendHtml(value); } /// <summary> /// Writes an <see cref="IHtmlContentContainer"/> value. /// </summary> /// <param name="value">The <see cref="IHtmlContentContainer"/> value.</param> public void Write(IHtmlContentContainer value) { if (value == null) { return; } value.MoveTo(Buffer); } /// <inheritdoc /> public override void WriteLine(object value) { if (value == null) { return; } if (value is IHtmlContentContainer container) { Write(container); Write(NewLine); } else if (value is IHtmlContent htmlContent) { Write(htmlContent); Write(NewLine); } else { Write(value.ToString()); Write(NewLine); } } /// <inheritdoc /> public override Task WriteAsync(char value) { Buffer.AppendHtml(value.ToString()); return Task.CompletedTask; } /// <inheritdoc /> public override Task WriteAsync(char[] buffer, int index, int count) { ArgumentNullException.ThrowIfNull(buffer); ArgumentOutOfRangeException.ThrowIfNegative(index); if (count < 0 || (buffer.Length - index < count)) { throw new ArgumentOutOfRangeException(nameof(count)); } Buffer.AppendHtml(new string(buffer, index, count)); return Task.CompletedTask; } /// <inheritdoc /> public override Task WriteAsync(string value) { Buffer.AppendHtml(value); return Task.CompletedTask; } /// <inheritdoc /> public override void WriteLine() { Buffer.AppendHtml(NewLine); } /// <inheritdoc /> public override void WriteLine(string value) { Buffer.AppendHtml(value); Buffer.AppendHtml(NewLine); } /// <inheritdoc /> public override Task WriteLineAsync(char value) { Buffer.AppendHtml(value.ToString()); Buffer.AppendHtml(NewLine); return Task.CompletedTask; } /// <inheritdoc /> public override Task WriteLineAsync(char[] value, int start, int offset) { Buffer.AppendHtml(new string(value, start, offset)); Buffer.AppendHtml(NewLine); return Task.CompletedTask; } /// <inheritdoc /> public override Task WriteLineAsync(string value) { Buffer.AppendHtml(value); Buffer.AppendHtml(NewLine); return Task.CompletedTask; } /// <inheritdoc /> public override Task WriteLineAsync() { Buffer.AppendHtml(NewLine); return Task.CompletedTask; } /// <summary> /// Copies the buffered content to the unbuffered writer and invokes flush on it. /// </summary> public override void Flush() { if (_inner == null || _inner is ViewBufferTextWriter) { return; } Flushed = true; Buffer.WriteTo(_inner, _htmlEncoder); Buffer.Clear(); _inner.Flush(); } /// <summary> /// Copies the buffered content to the unbuffered writer and invokes flush on it. /// </summary> /// <returns>A <see cref="Task"/> that represents the asynchronous copy and flush operations.</returns> public override async Task FlushAsync() { if (_inner == null || _inner is ViewBufferTextWriter) { return; } Flushed = true; await Buffer.WriteToAsync(_inner, _htmlEncoder); Buffer.Clear(); await _inner.FlushAsync(); } }