// 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();
}
}
}