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