// 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;
namespace Microsoft.AspNetCore.Razor.Evolution
{
///
/// The Razor template source.
///
public abstract class RazorSourceDocument
{
internal const int LargeObjectHeapLimitInChars = 40 * 1024; // 40K Unicode chars is 80KB which is less than the large object heap limit.
internal static readonly RazorSourceDocument[] EmptyArray = new RazorSourceDocument[0];
///
/// Encoding of the file that the text was read from.
///
public abstract Encoding Encoding { get; }
///
/// Path of the file the content was read from.
///
public abstract string FileName { get; }
///
/// Gets a character at given position.
///
/// The position to get the character from.
public abstract char this[int position] { get; }
///
/// Gets the length of the text in characters.
///
public abstract int Length { get; }
///
/// Gets the .
///
public abstract RazorSourceLineCollection Lines { get; }
///
/// Copies a range of characters from the to the specified .
///
/// The index of the first character in this instance to copy.
/// The destination buffer.
/// The index in destination at which the copy operation begins.
/// The number of characters in this instance to copy to destination.
public abstract void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count);
///
/// Reads the from the specified .
///
/// The to read from.
/// The file name of the template.
/// The .
public static RazorSourceDocument ReadFrom(Stream stream, string fileName)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
return ReadFromInternal(stream, fileName, encoding: null);
}
///
/// Reads the from the specified .
///
/// The to read from.
/// The file name of the template.
/// The to use to read the .
/// The .
public static RazorSourceDocument ReadFrom(Stream stream, string fileName, Encoding encoding)
{
if (stream == null)
{
throw new ArgumentNullException(nameof(stream));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
return ReadFromInternal(stream, fileName, encoding);
}
///
/// Reads the from the specified .
///
/// The to read from.
/// The .
public static RazorSourceDocument ReadFrom(RazorProjectItem projectItem)
{
if (projectItem == null)
{
throw new ArgumentNullException(nameof(projectItem));
}
var path = projectItem.PhysicalPath;
if (string.IsNullOrEmpty(path))
{
path = projectItem.Path;
}
using (var inputStream = projectItem.Read())
{
return ReadFrom(inputStream, path);
}
}
///
/// Creates a from the specified .
///
/// The template content.
/// The file name of the .
/// The .
/// Uses
public static RazorSourceDocument Create(string content, string fileName)
=> Create(content, fileName, Encoding.UTF8);
///
/// Creates a from the specified .
///
/// The template content.
/// The file name of the .
/// The of the file was read from.
/// The .
public static RazorSourceDocument Create(string content, string fileName, Encoding encoding)
{
if (content == null)
{
throw new ArgumentNullException(nameof(content));
}
if (encoding == null)
{
throw new ArgumentNullException(nameof(encoding));
}
return new DefaultRazorSourceDocument(content, encoding, fileName);
}
private static RazorSourceDocument ReadFromInternal(Stream stream, string fileName, Encoding encoding)
{
var streamLength = (int)stream.Length;
var content = string.Empty;
var contentEncoding = encoding ?? Encoding.UTF8;
if (streamLength > 0)
{
var bufferSize = Math.Min(streamLength, LargeObjectHeapLimitInChars);
var reader = new StreamReader(
stream,
contentEncoding,
detectEncodingFromByteOrderMarks: true,
bufferSize: bufferSize,
leaveOpen: true);
using (reader)
{
reader.Peek(); // Just to populate the encoding
if (encoding == null)
{
contentEncoding = reader.CurrentEncoding;
}
else if (encoding != reader.CurrentEncoding)
{
throw new InvalidOperationException(
Resources.FormatMismatchedContentEncoding(
encoding.EncodingName,
reader.CurrentEncoding.EncodingName));
}
if (streamLength > LargeObjectHeapLimitInChars)
{
// If the resulting string would end up on the large object heap, then use LargeTextRazorSourceDocument.
return new LargeTextRazorSourceDocument(
reader,
LargeObjectHeapLimitInChars,
contentEncoding,
fileName);
}
content = reader.ReadToEnd();
}
}
return new DefaultRazorSourceDocument(content, contentEncoding, fileName);
}
}
}