/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Threading;
namespace QuantConnect.Util
{
///
/// Converts a into an enumerable of string
///
public class StreamReaderEnumerable : IEnumerable, IDisposable
{
private int _disposed;
private int _createdEnumerator;
private readonly StreamReader _reader;
private readonly IDisposable[] _disposables;
///
/// Initializes a new instance of the class
///
/// The stream to be read
/// Allows specifying other resources that should be disposed when this instance is disposed
public StreamReaderEnumerable(Stream stream, params IDisposable[] disposables)
{
_disposables = disposables;
// this StreamReader constructor gives ownership of the stream to the StreamReader
// which is mediated by the LeaveOpen property, so when _reader is disposed, stream
// will also be disposed
_reader = new StreamReader(stream);
}
///
/// Initializes a new instance of the class
///
/// The stream reader instance to convert to an enumerable of string
/// Allows specifying other resources that should be disposed when this instance is disposed
public StreamReaderEnumerable(StreamReader reader, params IDisposable[] disposables)
{
_reader = reader;
_disposables = disposables;
}
/// Returns an enumerator that iterates through the collection.
/// A that can be used to iterate through the collection.
/// 1
public IEnumerator GetEnumerator()
{
// can't share the underlying stream instance -- barf
if (Interlocked.CompareExchange(ref _createdEnumerator, 1, 0) == 1)
{
throw new InvalidOperationException("A StreamReaderEnumerable may only be enumerated once. Consider using memoization or materialization.");
}
return new Enumerator(this);
}
/// Returns an enumerator that iterates through a collection.
/// An object that can be used to iterate through the collection.
/// 2
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// 2
public void Dispose()
{
if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1)
{
return;
}
_reader.DisposeSafely();
if (_disposables != null)
{
foreach (var disposable in _disposables)
{
disposable.DisposeSafely();
}
}
}
/// Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.
~StreamReaderEnumerable()
{
// be sure to clean up unmanaged resources via finalizer if
// dispose wasn't explicitly called by consuming code
Dispose();
}
private class Enumerator : IEnumerator
{
private readonly StreamReaderEnumerable _enumerable;
public string Current { get; private set; }
object IEnumerator.Current => Current;
public Enumerator(StreamReaderEnumerable enumerable)
{
_enumerable = enumerable;
}
public bool MoveNext()
{
var line = _enumerable._reader.ReadLine();
if (line == null)
{
return false;
}
Current = line;
return true;
}
public void Reset()
{
if (!_enumerable._reader.BaseStream.CanSeek)
{
throw new InvalidOperationException("The underlying stream is unseekable");
}
_enumerable._reader.BaseStream.Seek(0, SeekOrigin.Begin);
}
public void Dispose()
{
_enumerable.Dispose();
}
~Enumerator()
{
_enumerable.Dispose();
}
}
}
}