Lean  $LEAN_TAG$
StreamReaderEnumerable.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14 */
15 
16 using System;
17 using System.Collections;
18 using System.Collections.Generic;
19 using System.IO;
20 using System.Threading;
21 
22 namespace QuantConnect.Util
23 {
24  /// <summary>
25  /// Converts a <see cref="StreamReader"/> into an enumerable of string
26  /// </summary>
27  public class StreamReaderEnumerable : IEnumerable<string>, IDisposable
28  {
29  private int _disposed;
30  private int _createdEnumerator;
31  private readonly StreamReader _reader;
32  private readonly IDisposable[] _disposables;
33 
34  /// <summary>
35  /// Initializes a new instance of the <see cref="StreamReaderEnumerable"/> class
36  /// </summary>
37  /// <param name="stream">The stream to be read</param>
38  /// <param name="disposables">Allows specifying other resources that should be disposed when this instance is disposed</param>
39  public StreamReaderEnumerable(Stream stream, params IDisposable[] disposables)
40  {
41  _disposables = disposables;
42 
43  // this StreamReader constructor gives ownership of the stream to the StreamReader
44  // which is mediated by the LeaveOpen property, so when _reader is disposed, stream
45  // will also be disposed
46  _reader = new StreamReader(stream);
47  }
48 
49  /// <summary>
50  /// Initializes a new instance of the <see cref="StreamReaderEnumerable"/> class
51  /// </summary>
52  /// <param name="reader">The stream reader instance to convert to an enumerable of string</param>
53  /// <param name="disposables">Allows specifying other resources that should be disposed when this instance is disposed</param>
54  public StreamReaderEnumerable(StreamReader reader, params IDisposable[] disposables)
55  {
56  _reader = reader;
57  _disposables = disposables;
58  }
59 
60  /// <summary>Returns an enumerator that iterates through the collection.</summary>
61  /// <returns>A <see cref="T:System.Collections.Generic.IEnumerator`1" /> that can be used to iterate through the collection.</returns>
62  /// <filterpriority>1</filterpriority>
63  public IEnumerator<string> GetEnumerator()
64  {
65  // can't share the underlying stream instance -- barf
66  if (Interlocked.CompareExchange(ref _createdEnumerator, 1, 0) == 1)
67  {
68  throw new InvalidOperationException("A StreamReaderEnumerable may only be enumerated once. Consider using memoization or materialization.");
69  }
70 
71  return new Enumerator(this);
72  }
73 
74  /// <summary>Returns an enumerator that iterates through a collection.</summary>
75  /// <returns>An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.</returns>
76  /// <filterpriority>2</filterpriority>
77  IEnumerator IEnumerable.GetEnumerator()
78  {
79  return GetEnumerator();
80  }
81 
82  /// <summary>Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.</summary>
83  /// <filterpriority>2</filterpriority>
84  public void Dispose()
85  {
86  if (Interlocked.CompareExchange(ref _disposed, 1, 0) == 1)
87  {
88  return;
89  }
90 
91  _reader.DisposeSafely();
92  if (_disposables != null)
93  {
94  foreach (var disposable in _disposables)
95  {
96  disposable.DisposeSafely();
97  }
98  }
99  }
100 
101  /// <summary>Allows an object to try to free resources and perform other cleanup operations before it is reclaimed by garbage collection.</summary>
103  {
104  // be sure to clean up unmanaged resources via finalizer if
105  // dispose wasn't explicitly called by consuming code
106  Dispose();
107  }
108 
109  private class Enumerator : IEnumerator<string>
110  {
111  private readonly StreamReaderEnumerable _enumerable;
112 
113  public string Current { get; private set; }
114 
115  object IEnumerator.Current => Current;
116 
117  public Enumerator(StreamReaderEnumerable enumerable)
118  {
119  _enumerable = enumerable;
120  }
121 
122  public bool MoveNext()
123  {
124  var line = _enumerable._reader.ReadLine();
125  if (line == null)
126  {
127  return false;
128  }
129 
130  Current = line;
131  return true;
132  }
133 
134  public void Reset()
135  {
136  if (!_enumerable._reader.BaseStream.CanSeek)
137  {
138  throw new InvalidOperationException("The underlying stream is unseekable");
139  }
140 
141  _enumerable._reader.BaseStream.Seek(0, SeekOrigin.Begin);
142  }
143 
144  public void Dispose()
145  {
146  _enumerable.Dispose();
147  }
148 
149  ~Enumerator()
150  {
151  _enumerable.Dispose();
152  }
153  }
154  }
155 }