aboutsummaryrefslogtreecommitdiffstats
path: root/Software/Visual_Studio/Tango.CSV/CsvFileReader.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Software/Visual_Studio/Tango.CSV/CsvFileReader.cs')
-rw-r--r--Software/Visual_Studio/Tango.CSV/CsvFileReader.cs425
1 files changed, 425 insertions, 0 deletions
diff --git a/Software/Visual_Studio/Tango.CSV/CsvFileReader.cs b/Software/Visual_Studio/Tango.CSV/CsvFileReader.cs
new file mode 100644
index 000000000..1ae9404a7
--- /dev/null
+++ b/Software/Visual_Studio/Tango.CSV/CsvFileReader.cs
@@ -0,0 +1,425 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Globalization;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using System.Threading;
+using System.Collections.Concurrent;
+using System.Threading.Tasks;
+
+namespace Tango.CSV
+{
+ /// <summary>
+ /// Represents a CSV file reader.
+ /// </summary>
+ /// <typeparam name="T"></typeparam>
+ /// <seealso cref="Tango.CSV.CsvFile" />
+ /// <seealso cref="System.Collections.Generic.IEnumerable{T}" />
+ /// <seealso cref="System.Collections.Generic.IEnumerator{T}" />
+ internal class CsvFileReader<T> : CsvFile, IEnumerable<T>, IEnumerator<T>
+ where T : new()
+ {
+ private readonly Dictionary<Type, List<Action<T, String>>> allSetters = new Dictionary<Type, List<Action<T, String>>>();
+ private string[] columns;
+ private char curChar;
+ private int len;
+ private string line;
+ private int pos;
+ private T record;
+ private readonly char fieldSeparator;
+ private readonly TextReader textReader;
+ private readonly char textQualifier;
+ private readonly StringBuilder parseFieldResult = new StringBuilder();
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvFileReader{T}"/> class.
+ /// </summary>
+ /// <param name="csvSource">The CSV source.</param>
+ public CsvFileReader(CsvSource csvSource)
+ : this(csvSource, null)
+ {
+ }
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="CsvFileReader{T}"/> class.
+ /// </summary>
+ /// <param name="csvSource">The CSV source.</param>
+ /// <param name="csvDefinition">The CSV definition.</param>
+ public CsvFileReader(CsvSource csvSource, CsvDefinition csvDefinition)
+ {
+ var streamReader = csvSource.TextReader as StreamReader;
+ if (streamReader != null)
+ this.BaseStream = streamReader.BaseStream;
+ if (csvDefinition == null)
+ csvDefinition = DefaultCsvDefinition;
+ this.fieldSeparator = csvDefinition.FieldSeparator;
+ this.textQualifier = csvDefinition.TextQualifier;
+
+ this.textReader = csvSource.TextReader;// new FileStream(csvSource.TextReader, FileMode.Open);
+
+ this.ReadHeader(csvDefinition.Header);
+
+ }
+
+ /// <summary>
+ /// Gets the element in the collection at the current position of the enumerator.
+ /// </summary>
+ public T Current
+ {
+ get { return this.record; }
+ }
+
+ /// <summary>
+ /// Gets a value indicating whether this <see cref="CsvFileReader{T}"/> is EOF.
+ /// </summary>
+ public bool Eof
+ {
+ get { return this.line == null; }
+ }
+
+ /// <summary>
+ /// Gets the element in the collection at the current position of the enumerator.
+ /// </summary>
+ object IEnumerator.Current
+ {
+ get { return this.Current; }
+ }
+
+ /// <summary>
+ /// Releases unmanaged and - optionally - managed resources.
+ /// </summary>
+ /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing)
+ {
+ // free managed resources
+ this.textReader.Dispose();
+ }
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through a collection.
+ /// </summary>
+ /// <returns>
+ /// An <see cref="T:System.Collections.IEnumerator" /> object that can be used to iterate through the collection.
+ /// </returns>
+ IEnumerator IEnumerable.GetEnumerator()
+ {
+ return this.GetEnumerator();
+ }
+
+ /// <summary>
+ /// Returns an enumerator that iterates through the collection.
+ /// </summary>
+ /// <returns>
+ /// An enumerator that can be used to iterate through the collection.
+ /// </returns>
+ public IEnumerator<T> GetEnumerator()
+ {
+ return this;
+ }
+
+ /// <summary>
+ /// Advances the enumerator to the next element of the collection.
+ /// </summary>
+ /// <returns>
+ /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection.
+ /// </returns>
+ public bool MoveNext()
+ {
+ this.ReadNextLine();
+ if (this.line == null && (this.line = this.textReader.ReadLine()) == null)
+ {
+ this.record = default(T);
+ }
+ else
+ {
+ this.record = new T();
+ Type recordType = typeof(T);
+ List<Action<T, String>> setters;
+ if (!this.allSetters.TryGetValue(recordType, out setters))
+ {
+ setters = this.CreateSetters();
+ this.allSetters[recordType] = setters;
+ }
+
+ var fieldValues = new string[setters.Count];
+ for (int i = 0; i < setters.Count; i++)
+ {
+ fieldValues[i] = this.ParseField();
+ if (this.curChar == this.fieldSeparator)
+ this.NextChar();
+ else
+ break;
+ }
+ for (int i = 0; i < setters.Count; i++)
+ {
+ var setter = setters[i];
+ if (setter != null)
+ {
+ setter(this.record, fieldValues[i]);
+ }
+ }
+ }
+ return (this.record != null);
+ }
+
+ /// <summary>
+ /// Sets the enumerator to its initial position, which is before the first element in the collection.
+ /// </summary>
+ /// <exception cref="System.NotImplementedException">Cannot reset CsvFileReader enumeration.</exception>
+ public void Reset()
+ {
+ throw new NotImplementedException("Cannot reset CsvFileReader enumeration.");
+ }
+
+ /// <summary>
+ /// Emits the set value action.
+ /// </summary>
+ /// <param name="mi">The mi.</param>
+ /// <param name="func">The function.</param>
+ /// <returns></returns>
+ /// <exception cref="System.NotImplementedException"></exception>
+ private static Action<T, string> EmitSetValueAction(MemberInfo mi, Func<string, object> func)
+ {
+ ParameterExpression paramExpObj = Expression.Parameter(typeof(object), "obj");
+ ParameterExpression paramExpT = Expression.Parameter(typeof(T), "instance");
+
+ {
+ var pi = mi as PropertyInfo;
+ if (pi != null)
+ {
+ if (CsvFile.UseLambdas)
+ {
+ var callExpr = Expression.Call(
+ paramExpT,
+ pi.GetSetMethod(),
+ Expression.ConvertChecked(paramExpObj, pi.PropertyType));
+ var setter = Expression.Lambda<Action<T, object>>(
+ callExpr,
+ paramExpT,
+ paramExpObj).Compile();
+ return (o, s) => setter(o, func(s));
+ }
+ return (o, v) => pi.SetValue(o, (object)func(v), null);
+
+ }
+ }
+ {
+ var fi = mi as FieldInfo;
+ if (fi != null)
+ {
+ if (CsvFile.UseLambdas)
+ {
+ //ParameterExpression valueExp = Expression.Parameter(typeof(string), "value");
+ var valueExp = Expression.ConvertChecked(paramExpObj, fi.FieldType);
+
+ // Expression.Property can be used here as well
+ MemberExpression fieldExp = Expression.Field(paramExpT, fi);
+ BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);
+
+ var setter = Expression.Lambda<Action<T, object>>
+ (assignExp, paramExpT, paramExpObj).Compile();
+
+ return (o, s) => setter(o, func(s));
+ }
+ return ((o, v) => fi.SetValue(o, func(v)));
+ }
+ }
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Finds the setter.
+ /// </summary>
+ /// <param name="c">The c.</param>
+ /// <param name="staticMember">if set to <c>true</c> [static member].</param>
+ /// <returns></returns>
+ private static Action<T, string> FindSetter(string c, bool staticMember)
+ {
+ var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | (staticMember ? BindingFlags.Static : BindingFlags.Instance);
+ Action<T, string> action = null;
+ PropertyInfo pi = typeof(T).GetProperty(c, flags);
+ if (pi != null)
+ {
+ var pFunc = StringToObject(pi.PropertyType);
+ action = EmitSetValueAction(pi, pFunc);
+ }
+ FieldInfo fi = typeof(T).GetField(c, flags);
+ if (fi != null)
+ {
+ var fFunc = StringToObject(fi.FieldType);
+ action = EmitSetValueAction(fi, fFunc);
+ }
+ return action;
+ }
+
+ /// <summary>
+ /// Strings to object.
+ /// </summary>
+ /// <param name="propertyType">Type of the property.</param>
+ /// <returns></returns>
+ /// <exception cref="System.NotImplementedException"></exception>
+ private static Func<string, object> StringToObject(Type propertyType)
+ {
+ if (propertyType == typeof(string))
+ return (s) => s ?? String.Empty;
+ else if (propertyType == typeof(Int32))
+ return (s) => String.IsNullOrEmpty(s) ? 0 : Int32.Parse(s);
+ if (propertyType == typeof(DateTime))
+ return (s) => String.IsNullOrEmpty(s) ? DateTimeZero : DateTime.Parse(s);
+ else
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Creates the setters.
+ /// </summary>
+ /// <returns></returns>
+ private List<Action<T, string>> CreateSetters()
+ {
+ var list = new List<Action<T, string>>();
+ for (int i = 0; i < this.columns.Length; i++)
+ {
+ string columnName = this.columns[i];
+ Action<T, string> action = null;
+ if (columnName.IndexOf(' ') >= 0)
+ columnName = columnName.Replace(" ", "");
+ action = FindSetter(columnName, false) ?? FindSetter(columnName, true);
+
+ list.Add(action);
+ }
+ return list;
+ }
+
+ /// <summary>
+ /// Next character.
+ /// </summary>
+ private void NextChar()
+ {
+ if (this.pos < this.len)
+ {
+ this.pos++;
+ this.curChar = this.pos < this.len ? this.line[this.pos] : '\0';
+ }
+ }
+
+ /// <summary>
+ /// Parses the end of line.
+ /// </summary>
+ /// <exception cref="System.NotImplementedException"></exception>
+ private void ParseEndOfLine()
+ {
+ throw new NotImplementedException();
+ }
+
+ /// <summary>
+ /// Parses the field.
+ /// </summary>
+ /// <returns></returns>
+ private string ParseField()
+ {
+ parseFieldResult.Length = 0;
+ if (this.line == null || this.pos >= this.len)
+ return null;
+ while (this.curChar == ' ' || this.curChar == '\t')
+ {
+ this.NextChar();
+ }
+ if (this.curChar == this.textQualifier)
+ {
+ this.NextChar();
+ while (this.curChar != 0)
+ {
+ if (this.curChar == this.textQualifier)
+ {
+ this.NextChar();
+ if (this.curChar == this.textQualifier)
+ {
+ this.NextChar();
+ parseFieldResult.Append(this.textQualifier);
+ }
+ else
+ return parseFieldResult.ToString();
+ }
+ else if (this.curChar == '\0')
+ {
+ if (this.line == null)
+ return parseFieldResult.ToString();
+ this.ReadNextLine();
+ }
+ else
+ {
+ parseFieldResult.Append(this.curChar);
+ this.NextChar();
+ }
+ }
+ }
+ else
+ {
+ while (this.curChar != 0 && this.curChar != this.fieldSeparator && this.curChar != '\r' && this.curChar != '\n')
+ {
+ parseFieldResult.Append(this.curChar);
+ this.NextChar();
+ }
+ }
+ return parseFieldResult.ToString();
+ }
+
+ /// <summary>
+ /// Reads the header.
+ /// </summary>
+ /// <param name="header">The header.</param>
+ private void ReadHeader(string header)
+ {
+ if (header == null)
+ {
+ this.ReadNextLine();
+ }
+ else
+ {
+ // we read the first line from the given header
+ this.line = header;
+ this.pos = -1;
+ this.len = this.line.Length;
+ this.NextChar();
+ }
+
+ var readColumns = new List<string>();
+ string columnName;
+ while ((columnName = this.ParseField()) != null)
+ {
+ readColumns.Add(columnName);
+ if (this.curChar == this.fieldSeparator)
+ this.NextChar();
+ else
+ break;
+ }
+ this.columns = readColumns.ToArray();
+ }
+
+ /// <summary>
+ /// Reads the next line.
+ /// </summary>
+ private void ReadNextLine()
+ {
+ this.line = this.textReader.ReadLine();
+ this.pos = -1;
+ if (this.line == null)
+ {
+ this.len = 0;
+ this.curChar = '\0';
+ }
+ else
+ {
+ this.len = this.line.Length;
+ this.NextChar();
+ }
+ }
+ }
+}