// 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.AspNet.Html; using Microsoft.AspNet.Mvc.ViewFeatures; namespace Microsoft.AspNet.Mvc.Razor { /// /// An that is backed by a unbuffered writer (over the Response stream) and a buffered /// . When Flush or FlushAsync is invoked, the writer /// copies all content from the buffered writer to the unbuffered one and switches to writing to the unbuffered /// writer for all further write operations. /// public class RazorTextWriter : HtmlTextWriter { /// /// Creates a new instance of . /// /// The to write output to when this instance /// is no longer buffering. /// The to buffer output to. /// The HTML encoder. public RazorTextWriter(TextWriter unbufferedWriter, IHtmlContentBuilder buffer, HtmlEncoder encoder) { UnbufferedWriter = unbufferedWriter; HtmlEncoder = encoder; BufferedWriter = new HtmlContentWrapperTextWriter(buffer, unbufferedWriter.Encoding); TargetWriter = BufferedWriter; } /// public override Encoding Encoding { get { return BufferedWriter.Encoding; } } /// public bool IsBuffering { get; private set; } = true; /// /// Gets the buffered content. /// public IHtmlContent Buffer => BufferedWriter.ContentBuilder; // Internal for unit testing internal HtmlContentWrapperTextWriter BufferedWriter { get; } private TextWriter UnbufferedWriter { get; } private TextWriter TargetWriter { get; set; } private HtmlEncoder HtmlEncoder { get; } /// public override void Write(char value) { TargetWriter.Write(value); } /// public override void Write(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 || (index + count > buffer.Length)) { throw new ArgumentOutOfRangeException(nameof(count)); } TargetWriter.Write(buffer, index, count); } /// public override void Write(string value) { if (!string.IsNullOrEmpty(value)) { TargetWriter.Write(value); } } /// public override void Write(IHtmlContent value) { var htmlTextWriter = TargetWriter as HtmlTextWriter; if (htmlTextWriter == null) { value.WriteTo(TargetWriter, HtmlEncoder); } else { htmlTextWriter.Write(value); } } /// public override Task WriteAsync(char value) { return TargetWriter.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)); } return TargetWriter.WriteAsync(buffer, index, count); } /// public override Task WriteAsync(string value) { return TargetWriter.WriteAsync(value); } /// public override void WriteLine() { TargetWriter.WriteLine(); } /// public override void WriteLine(string value) { TargetWriter.WriteLine(value); } /// public override Task WriteLineAsync(char value) { return TargetWriter.WriteLineAsync(value); } /// public override Task WriteLineAsync(char[] value, int start, int offset) { return TargetWriter.WriteLineAsync(value, start, offset); } /// public override Task WriteLineAsync(string value) { return TargetWriter.WriteLineAsync(value); } /// public override Task WriteLineAsync() { return TargetWriter.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 (IsBuffering) { IsBuffering = false; TargetWriter = UnbufferedWriter; Buffer.WriteTo(UnbufferedWriter, HtmlEncoder); } UnbufferedWriter.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 Task FlushAsync() { if (IsBuffering) { IsBuffering = false; TargetWriter = UnbufferedWriter; Buffer.WriteTo(UnbufferedWriter, HtmlEncoder); } return UnbufferedWriter.FlushAsync(); } } }