aspnetcore/src/Microsoft.AspNet.Mvc.Razor/BufferEntryCollection.cs

176 lines
5.5 KiB
C#

// Copyright (c) Microsoft Open Technologies, Inc. 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.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace Microsoft.AspNet.Mvc.Razor
{
/// <summary>
/// Represents a hierarchy of strings and provides an enumerator that iterates over it as a sequence.
/// </summary>
public class BufferEntryCollection : IEnumerable<string>
{
// Specifies the maximum size we'll allow for direct conversion from
// char arrays to string.
private const int MaxCharToStringLength = 1024;
private readonly List<object> _buffer = new List<object>();
public IReadOnlyList<object> BufferEntries
{
get { return _buffer; }
}
/// <summary>
/// Adds a string value to the buffer.
/// </summary>
/// <param name="value">The value to add.</param>
public void Add(string value)
{
_buffer.Add(value);
}
/// <summary>
/// Adds a subarray of characters to the buffer.
/// </summary>
/// <param name="value">The array to add.</param>
/// <param name="index">The character position in the array at which to start copying data.</param>
/// <param name="count">The number of characters to copy.</param>
public void Add([NotNull] char[] value, int index, int count)
{
if (index < 0)
{
throw new ArgumentOutOfRangeException("index");
}
if (count < 0)
{
throw new ArgumentOutOfRangeException("count");
}
if (value.Length - index < count)
{
throw new ArgumentOutOfRangeException("count");
}
while (count > 0)
{
// Split large char arrays into 1KB strings.
var currentCount = Math.Min(count, MaxCharToStringLength);
Add(new string(value, index, currentCount));
index += currentCount;
count -= currentCount;
}
}
/// <summary>
/// Adds an instance of <see cref="BufferEntryCollection"/> to the buffer.
/// </summary>
/// <param name="buffer">The buffer collection to add.</param>
public void Add([NotNull] BufferEntryCollection buffer)
{
_buffer.Add(buffer.BufferEntries);
}
/// <inheritdoc />
public IEnumerator<string> GetEnumerator()
{
return new BufferEntryEnumerator(_buffer);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private sealed class BufferEntryEnumerator : IEnumerator<string>
{
private readonly Stack<IEnumerator<object>> _enumerators = new Stack<IEnumerator<object>>();
private readonly List<object> _initialBuffer;
public BufferEntryEnumerator(List<object> buffer)
{
_initialBuffer = buffer;
Reset();
}
public IEnumerator<object> CurrentEnumerator
{
get
{
return _enumerators.Peek();
}
}
public string Current
{
get
{
var currentEnumerator = CurrentEnumerator;
Debug.Assert(currentEnumerator != null);
return (string)currentEnumerator.Current;
}
}
object IEnumerator.Current
{
get
{
return Current;
}
}
public bool MoveNext()
{
var currentEnumerator = CurrentEnumerator;
if (currentEnumerator.MoveNext())
{
var current = currentEnumerator.Current;
var buffer = current as List<object>;
if (buffer != null)
{
// If the next item is a collection, recursively call in to it.
var enumerator = buffer.GetEnumerator();
_enumerators.Push(enumerator);
return MoveNext();
}
return true;
}
else if (_enumerators.Count > 1)
{
// The current enumerator is exhausted and we have a parent.
// Pop the current enumerator out and continue with it's parent.
var enumerator = _enumerators.Pop();
enumerator.Dispose();
return MoveNext();
}
// We've exactly one element in our stack which cannot move next.
return false;
}
public void Reset()
{
DisposeEnumerators();
_enumerators.Push(_initialBuffer.GetEnumerator());
}
public void Dispose()
{
DisposeEnumerators();
}
private void DisposeEnumerators()
{
while (_enumerators.Count > 0)
{
_enumerators.Pop().Dispose();
}
}
}
}
}