// 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
{
///
/// Represents a hierarchy of strings and provides an enumerator that iterates over it as a sequence.
///
public class BufferEntryCollection : IEnumerable
{
// Specifies the maximum size we'll allow for direct conversion from
// char arrays to string.
private const int MaxCharToStringLength = 1024;
private readonly List _buffer = new List();
public IReadOnlyList BufferEntries
{
get { return _buffer; }
}
///
/// Adds a string value to the buffer.
///
/// The value to add.
public void Add(string value)
{
_buffer.Add(value);
}
///
/// Adds a subarray of characters to the buffer.
///
/// The array to add.
/// The character position in the array at which to start copying data.
/// The number of characters to copy.
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;
}
}
///
/// Adds an instance of to the buffer.
///
/// The buffer collection to add.
public void Add([NotNull] BufferEntryCollection buffer)
{
_buffer.Add(buffer.BufferEntries);
}
///
public IEnumerator GetEnumerator()
{
return new BufferEntryEnumerator(_buffer);
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
private sealed class BufferEntryEnumerator : IEnumerator
{
private readonly Stack> _enumerators = new Stack>();
private readonly List _initialBuffer;
public BufferEntryEnumerator(List buffer)
{
_initialBuffer = buffer;
Reset();
}
public IEnumerator 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;
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();
}
}
}
}
}