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