From 80e6fe33c7dc76495d55c40f64fda87cb4aaadef Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Thu, 26 Jul 2018 11:48:22 -0400 Subject: [PATCH 01/12] perf: Massive optimization to enum utils --- ICD.Common.Utils/EnumUtils.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ICD.Common.Utils/EnumUtils.cs b/ICD.Common.Utils/EnumUtils.cs index b0a7d70..24dc0ac 100644 --- a/ICD.Common.Utils/EnumUtils.cs +++ b/ICD.Common.Utils/EnumUtils.cs @@ -35,11 +35,11 @@ namespace ICD.Common.Utils if (type == null) throw new ArgumentNullException("type"); - return type.IsAssignableTo(typeof(Enum)) || type + return type #if !SIMPLSHARP - .GetTypeInfo() + .GetTypeInfo() #endif - .IsEnum; + .IsEnum || type.IsAssignableTo(typeof(Enum)); } /// From 2ff70733858e31470a58a3ea4cdfe780b7641a65 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Tue, 28 Aug 2018 10:52:12 -0400 Subject: [PATCH 02/12] feat: add CSV Writer --- ICD.Common.Utils/Csv/CsvWriter.cs | 135 ++++++++++++++++++ ICD.Common.Utils/Csv/CsvWriterSettings.cs | 46 ++++++ .../ICD.Common.Utils_SimplSharp.csproj | 3 + ICD.Common.Utils/IO/IcdFile.cs | 6 + ICD.Common.Utils/IO/IcdStreamWriter.cs | 22 +++ 5 files changed, 212 insertions(+) create mode 100644 ICD.Common.Utils/Csv/CsvWriter.cs create mode 100644 ICD.Common.Utils/Csv/CsvWriterSettings.cs create mode 100644 ICD.Common.Utils/IO/IcdStreamWriter.cs diff --git a/ICD.Common.Utils/Csv/CsvWriter.cs b/ICD.Common.Utils/Csv/CsvWriter.cs new file mode 100644 index 0000000..0911129 --- /dev/null +++ b/ICD.Common.Utils/Csv/CsvWriter.cs @@ -0,0 +1,135 @@ +using System; +using System.Linq; +using ICD.Common.Properties; +using ICD.Common.Utils.IO; + +namespace ICD.Common.Utils.Csv +{ + public sealed class CsvWriter : IDisposable + { + private const string QUOTATION_MARK = "\""; + private const string DOUBLE_QUOTE_MARK = "\"\""; + + private readonly IcdTextWriter m_Writer; + + private readonly string m_Seperator; + private readonly string m_LineTerminator; + private readonly bool m_AlwaysEscape; + + private bool m_NewLine; + + /// + /// Constructor. + /// + public CsvWriter(IcdTextWriter writer, bool spaceAfterComma, bool alwaysEscape, string newline, params string[] header) + { + m_NewLine = true; + m_Writer = writer; + m_Seperator = spaceAfterComma ? ", " : ","; + m_AlwaysEscape = alwaysEscape; + m_LineTerminator = newline; + + if(header.Any()) + AppendRow(header); + } + + ~CsvWriter() + { + Dispose(); + } + + /// + /// Calls ToString() for each item and adds the row to the builder. + /// + /// + [PublicAPI] + public void AppendRow(params object[] row) + { + foreach (object value in row) + AppendValue(value); + AppendNewline(); + } + + /// + /// Adds the row to the builder. + /// + /// + [PublicAPI] + public void AppendRow(params string[] row) + { + foreach (string value in row) + AppendValue(value); + AppendNewline(); + } + + /// + /// Calls ToString() on the item and adds it to the builder. + /// + /// + [PublicAPI] + public void AppendValue(object value) + { + AppendValue(string.Format("{0}", value)); + } + + /// + /// Adds a value to the builder. + /// + /// + [PublicAPI] + public void AppendValue(string value) + { + if (!m_NewLine) + m_Writer.WrappedTextWriter.Write(m_Seperator); + + if (m_AlwaysEscape || value.Contains(",")) + { + value = value.Replace(QUOTATION_MARK, DOUBLE_QUOTE_MARK); + + // Append the value, surrounded by quotes + m_Writer.WrappedTextWriter.Write(QUOTATION_MARK); + m_Writer.WrappedTextWriter.Write(value); + m_Writer.WrappedTextWriter.Write(QUOTATION_MARK); + } + else + { + m_Writer.WrappedTextWriter.Write(value); + } + + m_NewLine = false; + } + + /// + /// Adds a New Line To the Builder + /// + [PublicAPI] + public void AppendNewline() + { + m_Writer.WrappedTextWriter.Write(m_LineTerminator); + + m_NewLine = true; + } + + public void Dispose() + { + m_Writer.Dispose(); + } + + /// + /// Instantiates a new CsvWriter with the properties given in the CsvWriterSettings. + /// + /// + /// + /// + /// + [PublicAPI] + public static CsvWriter Create(IcdTextWriter writer, CsvWriterSettings settings, params string[] header) + { + return new CsvWriter(writer, + settings.InsertSpaceAfterComma, + settings.AlwaysEscapeEveryValue, + settings.NewLineSequence, + header); + } + } +} \ No newline at end of file diff --git a/ICD.Common.Utils/Csv/CsvWriterSettings.cs b/ICD.Common.Utils/Csv/CsvWriterSettings.cs new file mode 100644 index 0000000..249abdf --- /dev/null +++ b/ICD.Common.Utils/Csv/CsvWriterSettings.cs @@ -0,0 +1,46 @@ +using ICD.Common.Properties; + +namespace ICD.Common.Utils.Csv +{ + public sealed class CsvWriterSettings + { + private bool m_InsertSpaceAfterComma = true; + private bool m_AlwaysEscapeEveryValue = true; + private string m_NewLineSequence = IcdEnvironment.NewLine; + + /// + /// Gets/Sets whether to insert a space between elements, after the comma + /// Defaults to true. + /// + [PublicAPI] + public bool InsertSpaceAfterComma + { + get { return m_InsertSpaceAfterComma; } + set { m_InsertSpaceAfterComma = value; } + } + + /// + /// Gets/Sets whether to always escape the values. + /// If true, values are recorded surrounded by quotes, regardless of if they contain a comma or not. Quotes are escaped. + /// If false, values are recorded as the value without quotes, unless escaping is required. + /// Defaults to true. + /// + [PublicAPI] + public bool AlwaysEscapeEveryValue + { + get { return m_AlwaysEscapeEveryValue; } + set { m_AlwaysEscapeEveryValue = value; } + } + + /// + /// Gets/Sets the newline character or characters to deliniate records. + /// Defaults to System.NewLine. + /// + [PublicAPI] + public string NewLineSequence + { + get { return m_NewLineSequence; } + set { m_NewLineSequence = value; } + } + } +} \ No newline at end of file diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index b663b60..811faf7 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -79,6 +79,8 @@ + + @@ -92,6 +94,7 @@ + diff --git a/ICD.Common.Utils/IO/IcdFile.cs b/ICD.Common.Utils/IO/IcdFile.cs index 0b90670..0dcd728 100644 --- a/ICD.Common.Utils/IO/IcdFile.cs +++ b/ICD.Common.Utils/IO/IcdFile.cs @@ -89,5 +89,11 @@ namespace ICD.Common.Utils.IO { return new IcdFileStream(File.Create(path)); } + + [PublicAPI] + public static IcdStreamWriter AppendText(string path) + { + return new IcdStreamWriter(File.AppendText(path)); + } } } diff --git a/ICD.Common.Utils/IO/IcdStreamWriter.cs b/ICD.Common.Utils/IO/IcdStreamWriter.cs new file mode 100644 index 0000000..4d5604a --- /dev/null +++ b/ICD.Common.Utils/IO/IcdStreamWriter.cs @@ -0,0 +1,22 @@ +using System; +#if SIMPLSHARP +using Crestron.SimplSharp.CrestronIO; +#elif STANDARD +using System.IO; +#endif + +namespace ICD.Common.Utils.IO +{ + public sealed class IcdStreamWriter : IcdTextWriter + { + public StreamWriter WrappedStreamWriter { get { return WrappedTextWriter as StreamWriter; } } + + /// + /// Constructor. + /// + /// + public IcdStreamWriter(StreamWriter baseStreamWriter) : base(baseStreamWriter) + { + } + } +} \ No newline at end of file From 2ae8fa9c2d3b4d1b35c083bc6fefad5226ffd339 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Sun, 9 Sep 2018 14:21:25 -0400 Subject: [PATCH 03/12] feat: Begin implementing XmlConverters --- .../Xml/AbstractGenericXmlConverter.cs | 155 ++++++++++++++++++ ICD.Common.Utils/Xml/AbstractXmlConverter.cs | 25 +++ ICD.Common.Utils/Xml/DefaultXmlConverter.cs | 32 ++++ ICD.Common.Utils/Xml/IXmlConverter.cs | 22 +++ ICD.Common.Utils/Xml/IcdXmlConvert.cs | 54 ++++++ ICD.Common.Utils/Xml/XmlConverterAttribute.cs | 91 ++++++++++ 6 files changed, 379 insertions(+) create mode 100644 ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs create mode 100644 ICD.Common.Utils/Xml/AbstractXmlConverter.cs create mode 100644 ICD.Common.Utils/Xml/DefaultXmlConverter.cs create mode 100644 ICD.Common.Utils/Xml/IXmlConverter.cs create mode 100644 ICD.Common.Utils/Xml/XmlConverterAttribute.cs diff --git a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs new file mode 100644 index 0000000..eb0e436 --- /dev/null +++ b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs @@ -0,0 +1,155 @@ +using System; +using System.Xml; +using ICD.Common.Properties; + +namespace ICD.Common.Utils.Xml +{ + public abstract class AbstractGenericXmlConverter : AbstractXmlConverter + { + /// + /// Creates a new instance of T. + /// + /// + protected virtual T Instantiate() + { + return ReflectionUtils.CreateInstance(); + } + + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + public sealed override void WriteXml(IcdXmlTextWriter writer, string elementName, object value) + { + if (writer == null) + throw new ArgumentNullException("writer"); + + if (value == null) + { + writer.WriteElementString(elementName, null); + return; + } + + WriteXml(writer, elementName, (T)value); + } + + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + [PublicAPI] + public void WriteXml(IcdXmlTextWriter writer, string elementName, T value) + { + if (writer == null) + throw new ArgumentNullException("writer"); + + if (value == null) + { + writer.WriteElementString(elementName, null); + return; + } + + writer.WriteStartElement(elementName); + { + WriteAttributes(writer, value); + WriteElements(writer, value); + } + writer.WriteEndElement(); + } + + /// + /// Override to write attributes to the root element. + /// + /// + /// + protected virtual void WriteAttributes(IcdXmlTextWriter writer, T value) + { + } + + /// + /// Override to write elements to the writer. + /// + /// + /// + protected virtual void WriteElements(IcdXmlTextWriter writer, T value) + { + } + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + public sealed override object ReadXml(IcdXmlReader reader) + { + return ReadXmlTyped(reader); + } + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + [PublicAPI] + public virtual T ReadXmlTyped(IcdXmlReader reader) + { + T output = default(T); + bool instantiated = false; + + while (reader.Read()) + { + if (reader.NodeType == XmlNodeType.Element && reader.IsEmptyElement) + break; + + if (!instantiated) + { + instantiated = true; + output = Instantiate(); + } + + switch (reader.NodeType) + { + case XmlNodeType.Attribute: + ReadAttribute(reader, output); + break; + + case XmlNodeType.Element: + ReadElement(reader, output); + break; + } + } + + return output; + } + + /// + /// Override to handle the current attribute. + /// + /// + /// + protected virtual void ReadAttribute(IcdXmlReader reader, T instance) + { + // Skip the attribute + reader.Read(); + } + + /// + /// Override to handle the current element. + /// + /// + /// + protected virtual void ReadElement(IcdXmlReader reader, T instance) + { + // Skip the element + reader.ReadOuterXml(); + } + } +} diff --git a/ICD.Common.Utils/Xml/AbstractXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractXmlConverter.cs new file mode 100644 index 0000000..23a818c --- /dev/null +++ b/ICD.Common.Utils/Xml/AbstractXmlConverter.cs @@ -0,0 +1,25 @@ +using ICD.Common.Properties; + +namespace ICD.Common.Utils.Xml +{ + public abstract class AbstractXmlConverter : IXmlConverter + { + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + public abstract void WriteXml(IcdXmlTextWriter writer, string elementName, object value); + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + [PublicAPI] + public abstract object ReadXml(IcdXmlReader reader); + } +} diff --git a/ICD.Common.Utils/Xml/DefaultXmlConverter.cs b/ICD.Common.Utils/Xml/DefaultXmlConverter.cs new file mode 100644 index 0000000..da72eea --- /dev/null +++ b/ICD.Common.Utils/Xml/DefaultXmlConverter.cs @@ -0,0 +1,32 @@ +using System; + +namespace ICD.Common.Utils.Xml +{ + public sealed class DefaultXmlConverter : AbstractXmlConverter + { + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + public override void WriteXml(IcdXmlTextWriter writer, string elementName, object value) + { + string elementString = IcdXmlConvert.ToString(value); + + writer.WriteElementString(elementName, elementString); + } + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + public override object ReadXml(IcdXmlReader reader) + { + throw new NotSupportedException(); + } + } +} diff --git a/ICD.Common.Utils/Xml/IXmlConverter.cs b/ICD.Common.Utils/Xml/IXmlConverter.cs new file mode 100644 index 0000000..e695040 --- /dev/null +++ b/ICD.Common.Utils/Xml/IXmlConverter.cs @@ -0,0 +1,22 @@ +namespace ICD.Common.Utils.Xml +{ + public interface IXmlConverter + { + /// + /// Writes the XML representation of the object. + /// + /// + /// + /// The value. + void WriteXml(IcdXmlTextWriter writer, string elementName, object value); + + /// + /// Reads the XML representation of the object. + /// + /// The XmlReader to read from. + /// + /// The object value. + /// + object ReadXml(IcdXmlReader reader); + } +} diff --git a/ICD.Common.Utils/Xml/IcdXmlConvert.cs b/ICD.Common.Utils/Xml/IcdXmlConvert.cs index e9ed1a5..08eed47 100644 --- a/ICD.Common.Utils/Xml/IcdXmlConvert.cs +++ b/ICD.Common.Utils/Xml/IcdXmlConvert.cs @@ -1,4 +1,6 @@ using System; +using System.Text; +using ICD.Common.Utils.IO; #if SIMPLSHARP using Crestron.SimplSharp.CrestronXml; #else @@ -9,6 +11,58 @@ namespace ICD.Common.Utils.Xml { public static class IcdXmlConvert { + /// + /// Serializes the given instance to an xml string. + /// + /// + /// + /// + public static string SerializeObject(string elementName, object value) + { + if (value == null) + return ToString(null); + + IXmlConverter converter = XmlConverterAttribute.GetConverterForInstance(value); + + StringBuilder builder = new StringBuilder(); + + using (IcdStringWriter stringWriter = new IcdStringWriter(builder)) + { + using (IcdXmlTextWriter writer = new IcdXmlTextWriter(stringWriter)) + converter.WriteXml(writer, elementName, value); + } + + return builder.ToString(); + } + + /// + /// Deserializes the given xml to an instance of the given type. + /// + /// + /// + /// + public static object DeserializeObject(string xml) + { + return (T)DeserializeObject(typeof(T), xml); + } + + /// + /// Deserializes the given xml to an instance of the given type. + /// + /// + /// + /// + private static object DeserializeObject(Type type, string xml) + { + if (type == null) + throw new ArgumentNullException("type"); + + IXmlConverter converter = XmlConverterAttribute.GetConverterForInstance(type); + + using (IcdXmlReader reader = new IcdXmlReader(xml)) + return converter.ReadXml(reader); + } + public static string ToString(int value) { return XmlConvert.ToString(value); diff --git a/ICD.Common.Utils/Xml/XmlConverterAttribute.cs b/ICD.Common.Utils/Xml/XmlConverterAttribute.cs new file mode 100644 index 0000000..af24969 --- /dev/null +++ b/ICD.Common.Utils/Xml/XmlConverterAttribute.cs @@ -0,0 +1,91 @@ +using System; +using System.Collections.Generic; +using ICD.Common.Utils.Attributes; + +namespace ICD.Common.Utils.Xml +{ + [AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)] + public sealed class XmlConverterAttribute : AbstractIcdAttribute + { + private static readonly Dictionary s_InstanceTypeToConverter; + private static readonly Dictionary s_ConverterTypeToConverter; + + private readonly Type m_ConverterType; + + /// + /// Gets the converter type. + /// + public Type ConverterType { get { return m_ConverterType; } } + + /// + /// Static constructor. + /// + static XmlConverterAttribute() + { + s_InstanceTypeToConverter = new Dictionary(); + s_ConverterTypeToConverter = new Dictionary(); + } + + /// + /// Constructor. + /// + /// + public XmlConverterAttribute(Type converterType) + { + m_ConverterType = converterType; + } + + /// + /// Gets the XML converter for the given instance. + /// + /// + /// + public static IXmlConverter GetConverterForInstance(object value) + { + return value == null ? LazyLoadConverter(typeof(DefaultXmlConverter)) : GetConverterForType(value.GetType()); + } + + /// + /// Gets the XML converter for the given type. + /// + /// + /// + public static IXmlConverter GetConverterForType(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + IXmlConverter converter; + if (!s_InstanceTypeToConverter.TryGetValue(type, out converter)) + { + XmlConverterAttribute attribute = AttributeUtils.GetClassAttribute(type); + Type converterType = attribute == null ? typeof(DefaultXmlConverter) : attribute.ConverterType; + + converter = LazyLoadConverter(converterType); + s_InstanceTypeToConverter[type] = converter; + } + + return converter; + } + + /// + /// Lazy-loads the converter of the given type. + /// + /// + /// + private static IXmlConverter LazyLoadConverter(Type converterType) + { + if (converterType == null) + throw new ArgumentNullException("converterType"); + + IXmlConverter converter; + if (!s_ConverterTypeToConverter.TryGetValue(converterType, out converter)) + { + converter = ReflectionUtils.CreateInstance(converterType) as IXmlConverter; + s_ConverterTypeToConverter[converterType] = converter; + } + + return converter; + } + } +} From 12ee533cbbefee348910dee3a5d41f07eb8a8683 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Sun, 9 Sep 2018 20:33:56 -0400 Subject: [PATCH 04/12] feat: Shims for deserializing XML to object instance --- ICD.Common.Utils/Xml/IcdXmlConvert.cs | 39 ++++++++++++++++++++++++--- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/ICD.Common.Utils/Xml/IcdXmlConvert.cs b/ICD.Common.Utils/Xml/IcdXmlConvert.cs index 08eed47..eda85b7 100644 --- a/ICD.Common.Utils/Xml/IcdXmlConvert.cs +++ b/ICD.Common.Utils/Xml/IcdXmlConvert.cs @@ -41,7 +41,7 @@ namespace ICD.Common.Utils.Xml /// /// /// - public static object DeserializeObject(string xml) + public static T DeserializeObject(string xml) { return (T)DeserializeObject(typeof(T), xml); } @@ -57,10 +57,41 @@ namespace ICD.Common.Utils.Xml if (type == null) throw new ArgumentNullException("type"); - IXmlConverter converter = XmlConverterAttribute.GetConverterForInstance(type); - using (IcdXmlReader reader = new IcdXmlReader(xml)) - return converter.ReadXml(reader); + return DeserializeObject(type, reader); + } + + /// + /// Deserializes the current node to an instance of the given type. + /// + /// + /// + /// + public static T DeserializeObject(IcdXmlReader reader) + { + if (reader == null) + throw new ArgumentNullException("reader"); + + return (T)DeserializeObject(typeof(T), reader); + } + + /// + /// Deserializes the current node to an instance of the given type. + /// + /// + /// + /// + public static object DeserializeObject(Type type, IcdXmlReader reader) + { + if (type == null) + throw new ArgumentNullException("type"); + + if (reader == null) + throw new ArgumentNullException("reader"); + + IXmlConverter converter = XmlConverterAttribute.GetConverterForType(type); + + return converter.ReadXml(reader); } public static string ToString(int value) From a3e548290faf067144bd454118a4872475b50384 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Sun, 9 Sep 2018 21:24:39 -0400 Subject: [PATCH 05/12] fix: Potential fixes for over-reading XML --- .../Xml/AbstractGenericXmlConverter.cs | 41 +++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs index eb0e436..7325c41 100644 --- a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs +++ b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs @@ -101,18 +101,37 @@ namespace ICD.Common.Utils.Xml [PublicAPI] public virtual T ReadXmlTyped(IcdXmlReader reader) { + // Read into the first node + if (!reader.Read()) + throw new FormatException(); + T output = default(T); bool instantiated = false; - while (reader.Read()) + while (reader.NodeType != XmlNodeType.EndElement) { - if (reader.NodeType == XmlNodeType.Element && reader.IsEmptyElement) - break; - if (!instantiated) { - instantiated = true; - output = Instantiate(); + switch (reader.NodeType) + { + case XmlNodeType.Element: + if (reader.IsEmptyElement) + return default(T); + + output = Instantiate(); + instantiated = true; + + // Read out of the root element + if (!reader.Read()) + throw new FormatException(); + continue; + + default: + // Keep reading until we reach the root element. + if (!reader.Read()) + throw new FormatException(); + continue; + } } switch (reader.NodeType) @@ -124,6 +143,14 @@ namespace ICD.Common.Utils.Xml case XmlNodeType.Element: ReadElement(reader, output); break; + + case XmlNodeType.EndElement: + return output; + + default: + if (!reader.Read()) + return output; + break; } } @@ -149,7 +176,7 @@ namespace ICD.Common.Utils.Xml protected virtual void ReadElement(IcdXmlReader reader, T instance) { // Skip the element - reader.ReadOuterXml(); + reader.Skip(); } } } From deff0d540818f666350fcc2835e26c83734bfd63 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Mon, 10 Sep 2018 11:17:37 -0400 Subject: [PATCH 06/12] chore: commit 2008 csproj changes --- ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj index 41a8175..ba191bc 100644 --- a/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj +++ b/ICD.Common.Utils/ICD.Common.Utils_SimplSharp.csproj @@ -198,12 +198,17 @@ + + + + + From 2c87d8e98867d2da14b3def4ab9eedb855d8d8b5 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Mon, 10 Sep 2018 11:17:51 -0400 Subject: [PATCH 07/12] fix: add preprocessor for 2008/crestron support --- ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs index 7325c41..49b3a9f 100644 --- a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs +++ b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs @@ -1,5 +1,9 @@ using System; +#if SIMPLSHARP +using Crestron.SimplSharp.CrestronXml; +#else using System.Xml; +#endif using ICD.Common.Properties; namespace ICD.Common.Utils.Xml From e1693bc738a58ef1f0dcfd3886e35afba11541c2 Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Mon, 10 Sep 2018 21:09:18 -0400 Subject: [PATCH 08/12] fix: Xml converter over-reading fixes --- .../Xml/AbstractGenericXmlConverter.cs | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs index 7325c41..04cbe0d 100644 --- a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs +++ b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs @@ -102,13 +102,13 @@ namespace ICD.Common.Utils.Xml public virtual T ReadXmlTyped(IcdXmlReader reader) { // Read into the first node - if (!reader.Read()) + if (reader.NodeType != XmlNodeType.Element && !reader.ReadToNextElement()) throw new FormatException(); T output = default(T); bool instantiated = false; - while (reader.NodeType != XmlNodeType.EndElement) + while (true) { if (!instantiated) { @@ -121,6 +121,10 @@ namespace ICD.Common.Utils.Xml output = Instantiate(); instantiated = true; + // Read the root attributes + while (reader.MoveToNextAttribute()) + ReadAttribute(reader, output); + // Read out of the root element if (!reader.Read()) throw new FormatException(); @@ -136,15 +140,13 @@ namespace ICD.Common.Utils.Xml switch (reader.NodeType) { - case XmlNodeType.Attribute: - ReadAttribute(reader, output); - break; - case XmlNodeType.Element: ReadElement(reader, output); - break; + continue; case XmlNodeType.EndElement: + // Read out of the end element + reader.Read(); return output; default: @@ -153,8 +155,6 @@ namespace ICD.Common.Utils.Xml break; } } - - return output; } /// @@ -164,8 +164,6 @@ namespace ICD.Common.Utils.Xml /// protected virtual void ReadAttribute(IcdXmlReader reader, T instance) { - // Skip the attribute - reader.Read(); } /// From d41aa6d111688e539a96ad50b5e01ee7a1b50adb Mon Sep 17 00:00:00 2001 From: Chris Cameron Date: Wed, 12 Sep 2018 20:18:56 -0400 Subject: [PATCH 09/12] feat: Xml recursion methods allow cancelling recursion into child nodes --- ICD.Common.Utils/Xml/XmlReaderExtensions.cs | 2 +- ICD.Common.Utils/Xml/XmlUtils.cs | 13 +++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/ICD.Common.Utils/Xml/XmlReaderExtensions.cs b/ICD.Common.Utils/Xml/XmlReaderExtensions.cs index 613a9c6..e7bd60f 100644 --- a/ICD.Common.Utils/Xml/XmlReaderExtensions.cs +++ b/ICD.Common.Utils/Xml/XmlReaderExtensions.cs @@ -115,7 +115,7 @@ namespace ICD.Common.Utils.Xml /// /// [PublicAPI] - public static void Recurse(this IcdXmlReader extends, Action callback) + public static void Recurse(this IcdXmlReader extends, Func callback) { if (extends == null) throw new ArgumentNullException("extends"); diff --git a/ICD.Common.Utils/Xml/XmlUtils.cs b/ICD.Common.Utils/Xml/XmlUtils.cs index 226bc1a..0f559f4 100644 --- a/ICD.Common.Utils/Xml/XmlUtils.cs +++ b/ICD.Common.Utils/Xml/XmlUtils.cs @@ -138,7 +138,7 @@ namespace ICD.Common.Utils.Xml /// /// [PublicAPI] - public static void Recurse(string xml, Action callback) + public static void Recurse(string xml, Func callback) { if (callback == null) throw new ArgumentNullException("callback"); @@ -152,7 +152,7 @@ namespace ICD.Common.Utils.Xml /// /// /// - private static void Recurse(string xml, Stack path, Action callback) + private static void Recurse(string xml, Stack path, Func callback) { if (path == null) throw new ArgumentNullException("path"); @@ -168,10 +168,11 @@ namespace ICD.Common.Utils.Xml path.Push(childReader.Name); string[] pathOutput = path.Reverse().ToArray(path.Count); - callback(new XmlRecursionEventArgs(xml, pathOutput)); - - foreach (string child in childReader.GetChildElementsAsString()) - Recurse(child, path, callback); + if (callback(new XmlRecursionEventArgs(xml, pathOutput))) + { + foreach (string child in childReader.GetChildElementsAsString()) + Recurse(child, path, callback); + } path.Pop(); } From 09603b0537f4dc4f9c5feae91341f2fcf85aa360 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Thu, 13 Sep 2018 14:21:59 -0400 Subject: [PATCH 10/12] refactor: make instantiaton abstract --- ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs index 8e0a6a4..e8fafcf 100644 --- a/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs +++ b/ICD.Common.Utils/Xml/AbstractGenericXmlConverter.cs @@ -14,10 +14,7 @@ namespace ICD.Common.Utils.Xml /// Creates a new instance of T. /// /// - protected virtual T Instantiate() - { - return ReflectionUtils.CreateInstance(); - } + protected abstract T Instantiate(); /// /// Writes the XML representation of the object. From 667c1cdd2156387daa16bddb2d59bb41b42cb6b7 Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Mon, 17 Sep 2018 17:12:00 -0400 Subject: [PATCH 11/12] fix: fix test --- ICD.Common.Utils.Tests/Xml/XmlUtilsTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/ICD.Common.Utils.Tests/Xml/XmlUtilsTest.cs b/ICD.Common.Utils.Tests/Xml/XmlUtilsTest.cs index 027455d..9b0b95a 100644 --- a/ICD.Common.Utils.Tests/Xml/XmlUtilsTest.cs +++ b/ICD.Common.Utils.Tests/Xml/XmlUtilsTest.cs @@ -90,6 +90,7 @@ namespace ICD.Common.Utils.Tests.Xml { paths.Add(args.Path); nodes.Add(args.Outer); + return true; } ); From 32aae27a7f95d8498539fba437c73b6901f589cc Mon Sep 17 00:00:00 2001 From: Jack Kanarish Date: Wed, 19 Sep 2018 11:39:26 -0400 Subject: [PATCH 12/12] chore: update changelog --- CHANGELOG.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index e235e6f..4d34d70 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,13 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Added + - CsvWriter for creating CSV files + Settings + - AppendText method for IcdFile + - IcdStreamWriter, a wrapper for a StreamWriter + - New XML conversion framework for performance improvements +### Changed + - XmlUtils is now using the improved XML conversion framework ## [5.0.0] - 2018-09-14 ### Added