diff options
Diffstat (limited to 'Software/Visual_Studio/Tango.CSV/CsvFileReader.cs')
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/CsvFileReader.cs | 425 |
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(); + } + } + } +} |
