// 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; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Html; namespace Microsoft.AspNetCore.Mvc.ViewFeatures.Internal { /// /// /// A that is backed by a unbuffered writer (over the Response stream) and/or a /// /// /// /// When Flush or FlushAsync 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. /// /// public class ViewBufferTextWriter : TextWriter { private readonly TextWriter _inner; private readonly HtmlEncoder _htmlEncoder; /// /// Creates a new instance of . /// /// The for buffered output. /// The . public ViewBufferTextWriter(ViewBuffer buffer, Encoding encoding) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (encoding == null) { throw new ArgumentNullException(nameof(encoding)); } Buffer = buffer; Encoding = encoding; } /// /// Creates a new instance of . /// /// The for buffered output. /// The . /// The HTML encoder. /// /// The inner to write output to when this instance is no longer buffering. /// public ViewBufferTextWriter(ViewBuffer buffer, Encoding encoding, HtmlEncoder htmlEncoder, TextWriter inner) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (encoding == null) { throw new ArgumentNullException(nameof(encoding)); } if (htmlEncoder == null) { throw new ArgumentNullException(nameof(htmlEncoder)); } if (inner == null) { throw new ArgumentNullException(nameof(inner)); } Buffer = buffer; Encoding = encoding; _htmlEncoder = htmlEncoder; _inner = inner; } /// public override Encoding Encoding { get; } /// public bool IsBuffering { get; private set; } = true; /// /// Gets the . /// public ViewBuffer Buffer { get; } /// public override void Write(char value) { if (IsBuffering) { Buffer.AppendHtml(value.ToString()); } else { _inner.Write(value); } } /// public override void Write(char[] buffer, int index, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (index < 0 || index >= buffer.Length) { throw new ArgumentOutOfRangeException(nameof(index)); } if (count < 0 || (buffer.Length - index < count)) { throw new ArgumentOutOfRangeException(nameof(count)); } if (IsBuffering) { Buffer.AppendHtml(new string(buffer, index, count)); } else { _inner.Write(buffer, index, count); } } /// public override void Write(string value) { if (string.IsNullOrEmpty(value)) { return; } if (IsBuffering) { Buffer.AppendHtml(value); } else { _inner.Write(value); } } /// public override void Write(object value) { if (value == null) { return; } IHtmlContentContainer container; IHtmlContent content; if ((container = value as IHtmlContentContainer) != null) { Write(container); } else if ((content = value as IHtmlContent) != null) { Write(content); } else { Write(value.ToString()); } } /// /// Writes an value. /// /// The value. public void Write(IHtmlContent value) { if (value == null) { return; } if (IsBuffering) { Buffer.AppendHtml(value); } else { value.WriteTo(_inner, _htmlEncoder); } } /// /// Writes an value. /// /// The value. public void Write(IHtmlContentContainer value) { if (value == null) { return; } if (IsBuffering) { value.MoveTo(Buffer); } else { value.WriteTo(_inner, _htmlEncoder); } } /// public override void WriteLine(object value) { if (value == null) { return; } IHtmlContentContainer container; IHtmlContent content; if ((container = value as IHtmlContentContainer) != null) { Write(container); Write(NewLine); } else if ((content = value as IHtmlContent) != null) { Write(content); Write(NewLine); } else { Write(value.ToString()); Write(NewLine); } } /// public override Task WriteAsync(char value) { if (IsBuffering) { Buffer.AppendHtml(value.ToString()); return Task.CompletedTask; } else { return _inner.WriteAsync(value); } } /// public override Task WriteAsync(char[] buffer, int index, int count) { if (buffer == null) { throw new ArgumentNullException(nameof(buffer)); } if (index < 0) { throw new ArgumentOutOfRangeException(nameof(index)); } if (count < 0 || (buffer.Length - index < count)) { throw new ArgumentOutOfRangeException(nameof(count)); } if (IsBuffering) { Buffer.AppendHtml(new string(buffer, index, count)); return Task.CompletedTask; } else { return _inner.WriteAsync(buffer, index, count); } } /// public override Task WriteAsync(string value) { if (IsBuffering) { Buffer.AppendHtml(value); return Task.CompletedTask; } else { return _inner.WriteAsync(value); } } /// public override void WriteLine() { if (IsBuffering) { Buffer.AppendHtml(NewLine); } else { _inner.WriteLine(); } } /// public override void WriteLine(string value) { if (IsBuffering) { Buffer.AppendHtml(value); Buffer.AppendHtml(NewLine); } else { _inner.WriteLine(value); } } /// public override Task WriteLineAsync(char value) { if (IsBuffering) { Buffer.AppendHtml(value.ToString()); Buffer.AppendHtml(NewLine); return Task.CompletedTask; } else { return _inner.WriteLineAsync(value); } } /// public override Task WriteLineAsync(char[] value, int start, int offset) { if (IsBuffering) { Buffer.AppendHtml(new string(value, start, offset)); Buffer.AppendHtml(NewLine); return Task.CompletedTask; } else { return _inner.WriteLineAsync(value, start, offset); } } /// public override Task WriteLineAsync(string value) { if (IsBuffering) { Buffer.AppendHtml(value); Buffer.AppendHtml(NewLine); return Task.CompletedTask; } else { return _inner.WriteLineAsync(value); } } /// public override Task WriteLineAsync() { if (IsBuffering) { Buffer.AppendHtml(NewLine); return Task.CompletedTask; } else { return _inner.WriteLineAsync(); } } /// /// Copies the buffered content to the unbuffered writer and invokes flush on it. /// Additionally causes this instance to no longer buffer and direct all write operations /// to the unbuffered writer. /// public override void Flush() { if (_inner == null || _inner is ViewBufferTextWriter) { return; } if (IsBuffering) { IsBuffering = false; Buffer.WriteTo(_inner, _htmlEncoder); Buffer.Clear(); } _inner.Flush(); } /// /// Copies the buffered content to the unbuffered writer and invokes flush on it. /// Additionally causes this instance to no longer buffer and direct all write operations /// to the unbuffered writer. /// /// A that represents the asynchronous copy and flush operations. public override async Task FlushAsync() { if (_inner == null || _inner is ViewBufferTextWriter) { return; } if (IsBuffering) { IsBuffering = false; await Buffer.WriteToAsync(_inner, _htmlEncoder); Buffer.Clear(); } await _inner.FlushAsync(); } } }