diff options
Diffstat (limited to 'Software/Visual_Studio/Tango.CSV')
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/CsvDefinition.cs | 59 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/CsvDestination.cs | 77 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/CsvFile.cs | 430 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/CsvFileLinqExtensions.cs | 46 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/CsvFileReader.cs | 425 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/CsvIgnoreAttribute.cs | 27 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/CsvSource.cs | 95 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/Properties/AssemblyInfo.cs | 9 | ||||
| -rw-r--r-- | Software/Visual_Studio/Tango.CSV/Tango.CSV.csproj | 99 |
9 files changed, 1267 insertions, 0 deletions
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 +{ + /// <summary> + /// Represents a <see cref="CsvFile"/> configuration. + /// </summary> + public class CsvDefinition + { + /// <summary> + /// Gets or sets the header. + /// </summary> + public string Header { get; set; } + + /// <summary> + /// Gets or sets the field separator. + /// </summary> + public char FieldSeparator { get; set; } + + /// <summary> + /// Gets or sets the text qualifier. + /// </summary> + public char TextQualifier { get; set; } + + /// <summary> + /// Gets or sets the columns. + /// </summary> + public IEnumerable<String> Columns { get; set; } + + /// <summary> + /// Gets or sets the end of line. + /// </summary> + public string EndOfLine { get; set; } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvDefinition"/> class. + /// </summary> + 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 +{ + /// <summary> + /// Represents a <see cref="CsvFile"/> destination. + /// </summary> + public class CsvDestination + { + public StreamWriter StreamWriter; + + /// <summary> + /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="CsvDestination"/>. + /// </summary> + /// <param name="path">The path.</param> + /// <returns> + /// The result of the conversion. + /// </returns> + public static implicit operator CsvDestination(string path) + { + return new CsvDestination(path); + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvDestination"/> class. + /// </summary> + /// <param name="streamWriter">The stream writer.</param> + private CsvDestination(StreamWriter streamWriter) + { + this.StreamWriter = streamWriter; + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvDestination"/> class. + /// </summary> + /// <param name="stream">The stream.</param> + private CsvDestination(Stream stream) + { + this.StreamWriter = new StreamWriter(stream); + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvDestination"/> class. + /// </summary> + /// <param name="fullName">The full name.</param> + public CsvDestination(string fullName) + { + FixCsvFileName(ref fullName); + this.StreamWriter = new StreamWriter(fullName); + } + + /// <summary> + /// Fixes the name of the CSV file. + /// </summary> + /// <param name="fullName">The full name.</param> + 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 +{ + /// <summary> + /// Represents a component for reading and writing CSV files. + /// </summary> + /// <seealso cref="System.IDisposable" /> + public class CsvFile : IDisposable + { + internal protected Stream BaseStream; + protected static DateTime DateTimeZero = new DateTime(); + + /// <summary> + /// Initializes the <see cref="CsvFile"/> class. + /// </summary> + static CsvFile() + { + DefaultCsvDefinition = new CsvDefinition + { + EndOfLine = "\r\n", + FieldSeparator = ',', + TextQualifier = '"' + }; + UseLambdas = true; + UseTasks = true; + FastIndexOfAny = true; + } + + /// <summary> + /// Gets or sets the default CSV definition. + /// </summary> + public static CsvDefinition DefaultCsvDefinition { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [use lambdas]. + /// </summary> + public static bool UseLambdas { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [use tasks]. + /// </summary> + public static bool UseTasks { get; set; } + + /// <summary> + /// Gets or sets a value indicating whether [fast index of any]. + /// </summary> + public static bool FastIndexOfAny { get; set; } + + /// <summary> + /// Reads the specified CSV source. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="csvSource">The CSV source.</param> + /// <returns></returns> + public static IEnumerable<T> Read<T>(CsvSource csvSource) where T : new() + { + var csvFileReader = new CsvFileReader<T>(csvSource); + return (IEnumerable<T>)csvFileReader; + } + + /// <summary> + /// Gets the field separator. + /// </summary> + /// <value> + /// The field separator. + /// </value> + public char FieldSeparator { get; private set; } + + /// <summary> + /// Gets the text qualifier. + /// </summary> + /// <value> + /// The text qualifier. + /// </value> + public char TextQualifier { get; private set; } + + /// <summary> + /// Gets the columns. + /// </summary> + /// <value> + /// The columns. + /// </value> + public IEnumerable<String> Columns { get; private set; } + + /// <summary> + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// </summary> + public void Dispose() + { + Dispose(true); + } + + /// <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 virtual void Dispose(bool disposing) + { + // overriden in derived classes + } + } + + /// <summary> + /// Represents a component for reading and writing CSV files from and to a collection of objects. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <seealso cref="System.IDisposable" /> + public class CsvFile<T> : 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<T, object>[] getters; + readonly bool[] isInvalidCharInFields; + private int linesToWrite; + private readonly BlockingCollection<string> csvLinesToWrite = new BlockingCollection<string>(5000); + private readonly Thread writeCsvLinesTask; + private Task addAsyncTask; + + /// <summary> + /// Initializes a new instance of the <see cref="CsvFile{T}"/> class. + /// </summary> + /// <param name="csvDestination">The CSV destination.</param> + public CsvFile(CsvDestination csvDestination) + : this(csvDestination, null) + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvFile{T}"/> class. + /// </summary> + public CsvFile() + { + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvFile{T}"/> class. + /// </summary> + /// <param name="csvDestination">The CSV destination.</param> + /// <param name="csvDefinition">The CSV definition.</param> + 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(() => { }); + + } + + /// <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 + addAsyncTask.Wait(); + if (csvLinesToWrite != null) + { + csvLinesToWrite.CompleteAdding(); + } + if (writeCsvLinesTask != null) + writeCsvLinesTask.Join(); + this.streamWriter.Close(); + } + } + + /// <summary> + /// Infers the columns. + /// </summary> + /// <param name="recordType">Type of the record.</param> + /// <returns></returns> + protected static IEnumerable<string> 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; + } + + /// <summary> + /// Writes the CSV lines. + /// </summary> + private void WriteCsvLines() + { + int written = 0; + foreach (var csvLine in csvLinesToWrite.GetConsumingEnumerable()) + { + this.streamWriter.WriteLine(csvLine); + written++; + } + Interlocked.Add(ref this.linesToWrite, -written); + } + + /// <summary> + /// Appends the specified record. + /// </summary> + /// <param name="record">The record.</param> + public void Append(T record) + { + + if (CsvFile.UseTasks) + { + + var linesWaiting = Interlocked.Increment(ref this.linesToWrite); + Action<Task> 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); + } + } + + /// <summary> + /// Finds the getter. + /// </summary> + /// <param name="c">The c.</param> + /// <param name="staticMember">if set to <c>true</c> [static member].</param> + /// <returns></returns> + private static Func<T, object> FindGetter(string c, int index, bool staticMember) + { + var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.IgnoreCase | (staticMember ? BindingFlags.Static : BindingFlags.Instance); + Func<T, object> 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<Func<T, object>> lambda; + if (type.IsValueType) + { + lambda = Expression.Lambda<Func<T, object>>(Expression.TypeAs(expr, typeof(object)), parameter); + } + else + { + lambda = Expression.Lambda<Func<T, object>>(expr, parameter); + } + func = lambda.Compile(); + } + } + else + { + if (pi != null) + func = o => pi.GetValue(o, null); + } + return func; + } + + /// <summary> + /// Creates the getters. + /// </summary> + private void CreateGetters() + { + var list = new List<Func<T, object>>(); + + for (int i = 0; i < columns.Length; i++) + { + Func<T, Object> 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(); + } + + /// <summary> + /// To the CSV. + /// </summary> + /// <param name="record">The record.</param> + /// <returns></returns> + /// <exception cref="System.ArgumentException">Cannot be null;record</exception> + 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); + + } + + /// <summary> + /// To the CSV string. + /// </summary> + /// <param name="o">The o.</param> + /// <returns></returns> + 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; + } + + /// <summary> + /// Requires the quotes. + /// </summary> + /// <param name="valueString">The value string.</param> + /// <returns></returns> + 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; + } + } + + /// <summary> + /// Writes the header. + /// </summary> + 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 +{ + /// <summary> + /// Export the collection to CSV file. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="source">The source.</param> + /// <param name="csvDestination">The CSV destination.</param> + public static void ToCsv<T>(this IEnumerable<T> source, CsvDestination csvDestination) + { + source.ToCsv(csvDestination, null); + } + + /// <summary> + /// Export the collection to CSV file. + /// </summary> + /// <typeparam name="T"></typeparam> + /// <param name="source">The source.</param> + /// <param name="csvDestination">The CSV destination.</param> + /// <param name="csvDefinition">The CSV definition.</param> + public static void ToCsv<T>(this IEnumerable<T> source, CsvDestination csvDestination, CsvDefinition csvDefinition) + { + using (var csvFile = new CsvFile<T>(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 +{ + /// <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(); + } + } + } +} 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 +{ + /// <summary> + /// Represents an attribute for decorating properties to exclude from the CSV file. + /// </summary> + /// <seealso cref="System.Attribute" /> + 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 +{ + /// <summary> + /// Represents a <see cref="CsvFile"/> source. + /// </summary> + public class CsvSource + { + public readonly TextReader TextReader; + + /// <summary> + /// Performs an implicit conversion from <see cref="CsvFile"/> to <see cref="CsvSource"/>. + /// </summary> + /// <param name="csvFile">The CSV file.</param> + /// <returns> + /// The result of the conversion. + /// </returns> + public static implicit operator CsvSource(CsvFile csvFile) + { + return new CsvSource(csvFile); + } + + /// <summary> + /// Performs an implicit conversion from <see cref="System.String"/> to <see cref="CsvSource"/>. + /// </summary> + /// <param name="path">The path.</param> + /// <returns> + /// The result of the conversion. + /// </returns> + public static implicit operator CsvSource(string path) + { + return new CsvSource(path); + } + + /// <summary> + /// Performs an implicit conversion from <see cref="TextReader"/> to <see cref="CsvSource"/>. + /// </summary> + /// <param name="textReader">The text reader.</param> + /// <returns> + /// The result of the conversion. + /// </returns> + public static implicit operator CsvSource(TextReader textReader) + { + return new CsvSource(textReader); + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvSource"/> class. + /// </summary> + /// <param name="textReader">The text reader.</param> + public CsvSource(TextReader textReader) + { + this.TextReader = textReader; + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvSource"/> class. + /// </summary> + /// <param name="stream">The stream.</param> + public CsvSource(Stream stream) + { + this.TextReader = new StreamReader(stream); + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvSource"/> class. + /// </summary> + /// <param name="path">The path.</param> + public CsvSource(string path) + { + this.TextReader = new StreamReader(path); + } + + /// <summary> + /// Initializes a new instance of the <see cref="CsvSource"/> class. + /// </summary> + /// <param name="csvFile">The CSV file.</param> + 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 @@ +<?xml version="1.0" encoding="utf-8"?> +<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> + <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" /> + <PropertyGroup> + <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration> + <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform> + <ProjectGuid>{58E8825F-0C96-449C-B320-1E82B0AA876B}</ProjectGuid> + <OutputType>Library</OutputType> + <AppDesignerFolder>Properties</AppDesignerFolder> + <RootNamespace>Tango.CSV</RootNamespace> + <AssemblyName>Tango.CSV</AssemblyName> + <TargetFrameworkVersion>v4.6</TargetFrameworkVersion> + <FileAlignment>512</FileAlignment> + <TargetFrameworkProfile /> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' "> + <DebugSymbols>true</DebugSymbols> + <DebugType>full</DebugType> + <Optimize>false</Optimize> + <OutputPath>..\Build\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' "> + <DebugType>pdbonly</DebugType> + <Optimize>true</Optimize> + <OutputPath>..\Build\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <ErrorReport>prompt</ErrorReport> + <WarningLevel>4</WarningLevel> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x64\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>x64</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'"> + <OutputPath>bin\x64\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x64</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'"> + <DebugSymbols>true</DebugSymbols> + <OutputPath>bin\x86\Debug\</OutputPath> + <DefineConstants>DEBUG;TRACE</DefineConstants> + <DebugType>full</DebugType> + <PlatformTarget>x86</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> + <OutputPath>bin\x86\Release\</OutputPath> + <DefineConstants>TRACE</DefineConstants> + <Optimize>true</Optimize> + <DebugType>pdbonly</DebugType> + <PlatformTarget>x86</PlatformTarget> + <ErrorReport>prompt</ErrorReport> + <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> + </PropertyGroup> + <ItemGroup> + <Reference Include="System" /> + <Reference Include="System.Core" /> + <Reference Include="System.Xml.Linq" /> + <Reference Include="System.Data.DataSetExtensions" /> + <Reference Include="Microsoft.CSharp" /> + <Reference Include="System.Data" /> + <Reference Include="System.Xml" /> + </ItemGroup> + <ItemGroup> + <Compile Include="..\Versioning\GlobalVersionInfo.cs"> + <Link>GlobalVersionInfo.cs</Link> + </Compile> + <Compile Include="CsvDefinition.cs" /> + <Compile Include="CsvDestination.cs" /> + <Compile Include="CsvFile.cs" /> + <Compile Include="CsvFileLinqExtensions.cs" /> + <Compile Include="CsvFileReader.cs" /> + <Compile Include="CsvIgnoreAttribute.cs" /> + <Compile Include="CsvSource.cs" /> + <Compile Include="Properties\AssemblyInfo.cs" /> + </ItemGroup> + <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> + <!-- To modify your build process, add your task inside one of the targets below and uncomment it. + Other similar extension points exist, see Microsoft.Common.targets. + <Target Name="BeforeBuild"> + </Target> + <Target Name="AfterBuild"> + </Target> + --> +</Project>
\ No newline at end of file |
