From 05fca4fe321600c4a9c0698b1e4c161e3ed79c9f Mon Sep 17 00:00:00 2001 From: Roy Ben-Shabat Date: Thu, 2 Aug 2018 15:05:44 +0300 Subject: Added Tango.CSV project! Implemented Single/Multi graph recording to CSV! --- Software/Visual_Studio/Tango.CSV/CsvDefinition.cs | 59 +++ Software/Visual_Studio/Tango.CSV/CsvDestination.cs | 77 ++++ Software/Visual_Studio/Tango.CSV/CsvFile.cs | 430 +++++++++++++++++++++ .../Tango.CSV/CsvFileLinqExtensions.cs | 46 +++ Software/Visual_Studio/Tango.CSV/CsvFileReader.cs | 425 ++++++++++++++++++++ .../Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs | 27 ++ Software/Visual_Studio/Tango.CSV/CsvSource.cs | 95 +++++ .../Tango.CSV/Properties/AssemblyInfo.cs | 9 + Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj | 99 +++++ 9 files changed, 1267 insertions(+) create mode 100644 Software/Visual_Studio/Tango.CSV/CsvDefinition.cs create mode 100644 Software/Visual_Studio/Tango.CSV/CsvDestination.cs create mode 100644 Software/Visual_Studio/Tango.CSV/CsvFile.cs create mode 100644 Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs create mode 100644 Software/Visual_Studio/Tango.CSV/CsvFileReader.cs create mode 100644 Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs create mode 100644 Software/Visual_Studio/Tango.CSV/CsvSource.cs create mode 100644 Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs create mode 100644 Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj (limited to 'Software/Visual_Studio/Tango.CSV') diff --git a/Software/Visual_Studio/Tango.CSV/CsvDefinition.cs b/Software/Visual_Studio/Tango.CSV/CsvDefinition.cs new file mode 100644 index 000000000..350c5ed8e --- /dev/null +++ b/Software/Visual_Studio/Tango.CSV/CsvDefinition.cs @@ -0,0 +1,59 @@ +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 +{ + /// + /// Represents a configuration. + /// + public class CsvDefinition + { + /// + /// Gets or sets the header. + /// + public string Header { get; set; } + + /// + /// Gets or sets the field separator. + /// + public char FieldSeparator { get; set; } + + /// + /// Gets or sets the text qualifier. + /// + public char TextQualifier { get; set; } + + /// + /// Gets or sets the columns. + /// + public IEnumerable Columns { get; set; } + + /// + /// Gets or sets the end of line. + /// + public string EndOfLine { get; set; } + + /// + /// Initializes a new instance of the class. + /// + public CsvDefinition() + { + if (CsvFile.DefaultCsvDefinition != null) + { + FieldSeparator = CsvFile.DefaultCsvDefinition.FieldSeparator; + TextQualifier = CsvFile.DefaultCsvDefinition.TextQualifier; + EndOfLine = CsvFile.DefaultCsvDefinition.EndOfLine; + } + } + } +} diff --git a/Software/Visual_Studio/Tango.CSV/CsvDestination.cs b/Software/Visual_Studio/Tango.CSV/CsvDestination.cs new file mode 100644 index 000000000..d88a34684 --- /dev/null +++ b/Software/Visual_Studio/Tango.CSV/CsvDestination.cs @@ -0,0 +1,77 @@ +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 +{ + /// + /// Represents a destination. + /// + public class CsvDestination + { + public StreamWriter StreamWriter; + + /// + /// Performs an implicit conversion from to . + /// + /// The path. + /// + /// The result of the conversion. + /// + public static implicit operator CsvDestination(string path) + { + return new CsvDestination(path); + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream writer. + private CsvDestination(StreamWriter streamWriter) + { + this.StreamWriter = streamWriter; + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + private CsvDestination(Stream stream) + { + this.StreamWriter = new StreamWriter(stream); + } + + /// + /// Initializes a new instance of the class. + /// + /// The full name. + public CsvDestination(string fullName) + { + FixCsvFileName(ref fullName); + this.StreamWriter = new StreamWriter(fullName); + } + + /// + /// Fixes the name of the CSV file. + /// + /// The full name. + private static void FixCsvFileName(ref string fullName) + { + fullName = Path.GetFullPath(fullName); + var path = Path.GetDirectoryName(fullName); + if (path != null && !Directory.Exists(path)) + Directory.CreateDirectory(path); + if (!String.Equals(Path.GetExtension(fullName), ".csv")) + fullName += ".csv"; + } + } +} diff --git a/Software/Visual_Studio/Tango.CSV/CsvFile.cs b/Software/Visual_Studio/Tango.CSV/CsvFile.cs new file mode 100644 index 000000000..ab5466da7 --- /dev/null +++ b/Software/Visual_Studio/Tango.CSV/CsvFile.cs @@ -0,0 +1,430 @@ +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 +{ + /// + /// Represents a component for reading and writing CSV files. + /// + /// + public class CsvFile : IDisposable + { + internal protected Stream BaseStream; + protected static DateTime DateTimeZero = new DateTime(); + + /// + /// Initializes the class. + /// + static CsvFile() + { + DefaultCsvDefinition = new CsvDefinition + { + EndOfLine = "\r\n", + FieldSeparator = ',', + TextQualifier = '"' + }; + UseLambdas = true; + UseTasks = true; + FastIndexOfAny = true; + } + + /// + /// Gets or sets the default CSV definition. + /// + public static CsvDefinition DefaultCsvDefinition { get; set; } + + /// + /// Gets or sets a value indicating whether [use lambdas]. + /// + public static bool UseLambdas { get; set; } + + /// + /// Gets or sets a value indicating whether [use tasks]. + /// + public static bool UseTasks { get; set; } + + /// + /// Gets or sets a value indicating whether [fast index of any]. + /// + public static bool FastIndexOfAny { get; set; } + + /// + /// Reads the specified CSV source. + /// + /// + /// The CSV source. + /// + public static IEnumerable Read(CsvSource csvSource) where T : new() + { + var csvFileReader = new CsvFileReader(csvSource); + return (IEnumerable)csvFileReader; + } + + /// + /// Gets the field separator. + /// + /// + /// The field separator. + /// + public char FieldSeparator { get; private set; } + + /// + /// Gets the text qualifier. + /// + /// + /// The text qualifier. + /// + public char TextQualifier { get; private set; } + + /// + /// Gets the columns. + /// + /// + /// The columns. + /// + public IEnumerable Columns { get; private set; } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + Dispose(true); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + // overriden in derived classes + } + } + + /// + /// Represents a component for reading and writing CSV files from and to a collection of objects. + /// + /// + /// + public class CsvFile : CsvFile + { + private readonly char fieldSeparator; + private readonly string fieldSeparatorAsString; + private readonly char[] invalidCharsInFields; + private readonly StreamWriter streamWriter; + private readonly char textQualifier; + private readonly String[] columns; + private Func[] getters; + readonly bool[] isInvalidCharInFields; + private int linesToWrite; + private readonly BlockingCollection csvLinesToWrite = new BlockingCollection(5000); + private readonly Thread writeCsvLinesTask; + private Task addAsyncTask; + + /// + /// Initializes a new instance of the class. + /// + /// The CSV destination. + public CsvFile(CsvDestination csvDestination) + : this(csvDestination, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + public CsvFile() + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The CSV destination. + /// The CSV definition. + public CsvFile(CsvDestination csvDestination, CsvDefinition csvDefinition) + { + if (csvDefinition == null) + csvDefinition = DefaultCsvDefinition; + this.columns = (csvDefinition.Columns ?? InferColumns(typeof(T))).ToArray(); + this.fieldSeparator = csvDefinition.FieldSeparator; + this.fieldSeparatorAsString = this.fieldSeparator.ToString(CultureInfo.InvariantCulture); + this.textQualifier = csvDefinition.TextQualifier; + this.streamWriter = csvDestination.StreamWriter; + + this.invalidCharsInFields = new[] { '\r', '\n', this.textQualifier, this.fieldSeparator }; + this.isInvalidCharInFields = new bool[256]; + + foreach (var c in this.invalidCharsInFields) + { + this.isInvalidCharInFields[c] = true; + } + this.WriteHeader(); + + this.CreateGetters(); + if (CsvFile.UseTasks) + { + writeCsvLinesTask = new Thread((o) => this.WriteCsvLines()); + writeCsvLinesTask.Start(); + } + this.addAsyncTask = Task.Factory.StartNew(() => { }); + + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + if (disposing) + { + // free managed resources + addAsyncTask.Wait(); + if (csvLinesToWrite != null) + { + csvLinesToWrite.CompleteAdding(); + } + if (writeCsvLinesTask != null) + writeCsvLinesTask.Join(); + this.streamWriter.Close(); + } + } + + /// + /// Infers the columns. + /// + /// Type of the record. + /// + protected static IEnumerable InferColumns(Type recordType) + { + var columns = recordType + .GetProperties(BindingFlags.Public | BindingFlags.Instance) + .Where(pi => pi.GetIndexParameters().Length == 0 + && pi.GetSetMethod() != null + && !Attribute.IsDefined(pi, typeof(CsvIgnoreAttribute))) + .Select(pi => pi.Name) + .Concat(recordType + .GetFields(BindingFlags.Public | BindingFlags.Instance) + .Where(fi => !Attribute.IsDefined(fi, typeof(CsvIgnoreAttribute))) + .Select(fi => fi.Name)) + .ToList(); + return columns; + } + + /// + /// Writes the CSV lines. + /// + private void WriteCsvLines() + { + int written = 0; + foreach (var csvLine in csvLinesToWrite.GetConsumingEnumerable()) + { + this.streamWriter.WriteLine(csvLine); + written++; + } + Interlocked.Add(ref this.linesToWrite, -written); + } + + /// + /// Appends the specified record. + /// + /// The record. + public void Append(T record) + { + + if (CsvFile.UseTasks) + { + + var linesWaiting = Interlocked.Increment(ref this.linesToWrite); + Action addRecord = (t) => + { + var csvLine = this.ToCsv(record); + this.csvLinesToWrite.Add(csvLine); + }; + + if (linesWaiting < 10000) + this.addAsyncTask = this.addAsyncTask.ContinueWith(addRecord); + else + addRecord(null); + } + else + { + var csvLine = this.ToCsv(record); + this.streamWriter.WriteLine(csvLine); + } + } + + /// + /// Finds the getter. + /// + /// The c. + /// if set to true [static member]. + /// + private static Func FindGetter(string c, int index, bool staticMember) + { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | (staticMember ? BindingFlags.Static : BindingFlags.Instance); + Func func = null; + + PropertyInfo pi = null; + pi = typeof(T).GetProperty(c, flags); + + if (pi == null) //Then try get by column index, + { + pi = typeof(T).GetProperties(flags)[index]; + } + + if (CsvFile.UseLambdas) + { + Expression expr = null; + ParameterExpression parameter = Expression.Parameter(typeof(T), "r"); + Type type = null; + + if (pi != null) + { + type = pi.PropertyType; + expr = Expression.Property(parameter, pi.Name); + } + if (expr != null) + { + Expression> lambda; + if (type.IsValueType) + { + lambda = Expression.Lambda>(Expression.TypeAs(expr, typeof(object)), parameter); + } + else + { + lambda = Expression.Lambda>(expr, parameter); + } + func = lambda.Compile(); + } + } + else + { + if (pi != null) + func = o => pi.GetValue(o, null); + } + return func; + } + + /// + /// Creates the getters. + /// + private void CreateGetters() + { + var list = new List>(); + + for (int i = 0; i < columns.Length; i++) + { + Func func = null; + var propertyName = (columns[i].IndexOf(' ') < 0 ? columns[i] : columns[i].Replace(" ", "")); + func = FindGetter(columns[i], i, false) ?? FindGetter(columns[i], i, true); + + list.Add(func); + } + this.getters = list.ToArray(); + } + + /// + /// To the CSV. + /// + /// The record. + /// + /// Cannot be null;record + private string ToCsv(T record) + { + if (record == null) + throw new ArgumentException("Cannot be null", "record"); + + string[] csvStrings = new string[getters.Length]; + + for (int i = 0; i < getters.Length; i++) + { + var getter = getters[i]; + object fieldValue = getter == null ? null : getter(record); + csvStrings[i] = this.ToCsvString(fieldValue); + } + return string.Join(this.fieldSeparatorAsString, csvStrings); + + } + + /// + /// To the CSV string. + /// + /// The o. + /// + private string ToCsvString(object o) + { + if (o != null) + { + string valueString = o as string ?? Convert.ToString(o, CultureInfo.CurrentUICulture); + if (RequireQuotes(valueString)) + { + var csvLine = new StringBuilder(); + csvLine.Append(this.textQualifier); + foreach (char c in valueString) + { + if (c == this.textQualifier) + csvLine.Append(c); // double the double quotes + csvLine.Append(c); + } + csvLine.Append(this.textQualifier); + return csvLine.ToString(); + } + else + return valueString; + } + return string.Empty; + } + + /// + /// Requires the quotes. + /// + /// The value string. + /// + private bool RequireQuotes(string valueString) + { + if (CsvFile.FastIndexOfAny) + { + var len = valueString.Length; + for (int i = 0; i < len; i++) + { + char c = valueString[i]; + if (c <= 255 && this.isInvalidCharInFields[c]) + return true; + } + return false; + } + else + { + return valueString.IndexOfAny(this.invalidCharsInFields) >= 0; + } + } + + /// + /// Writes the header. + /// + private void WriteHeader() + { + var csvLine = new StringBuilder(); + for (int i = 0; i < this.columns.Length; i++) + { + if (i > 0) + csvLine.Append(this.fieldSeparator); + csvLine.Append(this.ToCsvString(this.columns[i])); + } + this.streamWriter.WriteLine(csvLine.ToString()); + } + } +} diff --git a/Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs b/Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs new file mode 100644 index 000000000..8f99b2014 --- /dev/null +++ b/Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs @@ -0,0 +1,46 @@ +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; +using Tango.CSV; + + +public static class CsvFileLinqExtensions +{ + /// + /// Export the collection to CSV file. + /// + /// + /// The source. + /// The CSV destination. + public static void ToCsv(this IEnumerable source, CsvDestination csvDestination) + { + source.ToCsv(csvDestination, null); + } + + /// + /// Export the collection to CSV file. + /// + /// + /// The source. + /// The CSV destination. + /// The CSV definition. + public static void ToCsv(this IEnumerable source, CsvDestination csvDestination, CsvDefinition csvDefinition) + { + using (var csvFile = new CsvFile(csvDestination, csvDefinition)) + { + foreach (var record in source) + { + csvFile.Append(record); + } + } + } +} 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 +{ + /// + /// Represents a CSV file reader. + /// + /// + /// + /// + /// + internal class CsvFileReader : CsvFile, IEnumerable, IEnumerator + where T : new() + { + private readonly Dictionary>> allSetters = new Dictionary>>(); + 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(); + + /// + /// Initializes a new instance of the class. + /// + /// The CSV source. + public CsvFileReader(CsvSource csvSource) + : this(csvSource, null) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The CSV source. + /// The CSV definition. + 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); + + } + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + public T Current + { + get { return this.record; } + } + + /// + /// Gets a value indicating whether this is EOF. + /// + public bool Eof + { + get { return this.line == null; } + } + + /// + /// Gets the element in the collection at the current position of the enumerator. + /// + object IEnumerator.Current + { + get { return this.Current; } + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected override void Dispose(bool disposing) + { + if (disposing) + { + // free managed resources + this.textReader.Dispose(); + } + } + + /// + /// Returns an enumerator that iterates through a collection. + /// + /// + /// An object that can be used to iterate through the collection. + /// + IEnumerator IEnumerable.GetEnumerator() + { + return this.GetEnumerator(); + } + + /// + /// Returns an enumerator that iterates through the collection. + /// + /// + /// An enumerator that can be used to iterate through the collection. + /// + public IEnumerator GetEnumerator() + { + return this; + } + + /// + /// Advances the enumerator to the next element of the collection. + /// + /// + /// true if the enumerator was successfully advanced to the next element; false if the enumerator has passed the end of the collection. + /// + 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> 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); + } + + /// + /// Sets the enumerator to its initial position, which is before the first element in the collection. + /// + /// Cannot reset CsvFileReader enumeration. + public void Reset() + { + throw new NotImplementedException("Cannot reset CsvFileReader enumeration."); + } + + /// + /// Emits the set value action. + /// + /// The mi. + /// The function. + /// + /// + private static Action EmitSetValueAction(MemberInfo mi, Func 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>( + 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> + (assignExp, paramExpT, paramExpObj).Compile(); + + return (o, s) => setter(o, func(s)); + } + return ((o, v) => fi.SetValue(o, func(v))); + } + } + throw new NotImplementedException(); + } + + /// + /// Finds the setter. + /// + /// The c. + /// if set to true [static member]. + /// + private static Action FindSetter(string c, bool staticMember) + { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | (staticMember ? BindingFlags.Static : BindingFlags.Instance); + Action 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; + } + + /// + /// Strings to object. + /// + /// Type of the property. + /// + /// + private static Func 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(); + } + + /// + /// Creates the setters. + /// + /// + private List> CreateSetters() + { + var list = new List>(); + for (int i = 0; i < this.columns.Length; i++) + { + string columnName = this.columns[i]; + Action action = null; + if (columnName.IndexOf(' ') >= 0) + columnName = columnName.Replace(" ", ""); + action = FindSetter(columnName, false) ?? FindSetter(columnName, true); + + list.Add(action); + } + return list; + } + + /// + /// Next character. + /// + private void NextChar() + { + if (this.pos < this.len) + { + this.pos++; + this.curChar = this.pos < this.len ? this.line[this.pos] : '\0'; + } + } + + /// + /// Parses the end of line. + /// + /// + private void ParseEndOfLine() + { + throw new NotImplementedException(); + } + + /// + /// Parses the field. + /// + /// + 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(); + } + + /// + /// Reads the header. + /// + /// The header. + 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 columnName; + while ((columnName = this.ParseField()) != null) + { + readColumns.Add(columnName); + if (this.curChar == this.fieldSeparator) + this.NextChar(); + else + break; + } + this.columns = readColumns.ToArray(); + } + + /// + /// Reads the next line. + /// + 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(); + } + } + } +} diff --git a/Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs b/Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs new file mode 100644 index 000000000..87ae77516 --- /dev/null +++ b/Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs @@ -0,0 +1,27 @@ +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 +{ + /// + /// Represents an attribute for decorating properties to exclude from the CSV file. + /// + /// + public class CsvIgnoreAttribute : Attribute + { + public override string ToString() + { + return "Ignore Property"; + } + } +} diff --git a/Software/Visual_Studio/Tango.CSV/CsvSource.cs b/Software/Visual_Studio/Tango.CSV/CsvSource.cs new file mode 100644 index 000000000..ba7e458e6 --- /dev/null +++ b/Software/Visual_Studio/Tango.CSV/CsvSource.cs @@ -0,0 +1,95 @@ +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 +{ + /// + /// Represents a source. + /// + public class CsvSource + { + public readonly TextReader TextReader; + + /// + /// Performs an implicit conversion from to . + /// + /// The CSV file. + /// + /// The result of the conversion. + /// + public static implicit operator CsvSource(CsvFile csvFile) + { + return new CsvSource(csvFile); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The path. + /// + /// The result of the conversion. + /// + public static implicit operator CsvSource(string path) + { + return new CsvSource(path); + } + + /// + /// Performs an implicit conversion from to . + /// + /// The text reader. + /// + /// The result of the conversion. + /// + public static implicit operator CsvSource(TextReader textReader) + { + return new CsvSource(textReader); + } + + /// + /// Initializes a new instance of the class. + /// + /// The text reader. + public CsvSource(TextReader textReader) + { + this.TextReader = textReader; + } + + /// + /// Initializes a new instance of the class. + /// + /// The stream. + public CsvSource(Stream stream) + { + this.TextReader = new StreamReader(stream); + } + + /// + /// Initializes a new instance of the class. + /// + /// The path. + public CsvSource(string path) + { + this.TextReader = new StreamReader(path); + } + + /// + /// Initializes a new instance of the class. + /// + /// The CSV file. + public CsvSource(CsvFile csvFile) + { + this.TextReader = new StreamReader(csvFile.BaseStream); + } + } +} diff --git a/Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs b/Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..9696745ee --- /dev/null +++ b/Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs @@ -0,0 +1,9 @@ +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +[assembly: AssemblyTitle("Tango - CSV Library")] +[assembly: AssemblyVersion("2.0.17.1657")] +[assembly: ComVisible(false)] \ No newline at end of file diff --git a/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj b/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj new file mode 100644 index 000000000..48bda8bdb --- /dev/null +++ b/Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj @@ -0,0 +1,99 @@ + + + + + Debug + AnyCPU + {58E8825F-0C96-449C-B320-1E82B0AA876B} + Library + Properties + Tango.CSV + Tango.CSV + v4.6 + 512 + + + + true + full + false + ..\Build\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\Build\Release\ + TRACE + prompt + 4 + + + true + bin\x64\Debug\ + DEBUG;TRACE + full + x64 + prompt + MinimumRecommendedRules.ruleset + + + bin\x64\Release\ + TRACE + true + pdbonly + x64 + prompt + MinimumRecommendedRules.ruleset + + + true + bin\x86\Debug\ + DEBUG;TRACE + full + x86 + prompt + MinimumRecommendedRules.ruleset + + + bin\x86\Release\ + TRACE + true + pdbonly + x86 + prompt + MinimumRecommendedRules.ruleset + + + + + + + + + + + + + GlobalVersionInfo.cs + + + + + + + + + + + + + \ No newline at end of file -- cgit v1.3.1