Merge branch 'MetLife_v5.3' of Common/Utils into dev

This commit is contained in:
Chris Cameron
2018-09-19 17:54:14 +00:00
committed by Gogs
15 changed files with 669 additions and 7 deletions

View File

@@ -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

View File

@@ -90,6 +90,7 @@ namespace ICD.Common.Utils.Tests.Xml
{
paths.Add(args.Path);
nodes.Add(args.Outer);
return true;
}
);

View File

@@ -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;
/// <summary>
/// Constructor.
/// </summary>
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();
}
/// <summary>
/// Calls ToString() for each item and adds the row to the builder.
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AppendRow(params object[] row)
{
foreach (object value in row)
AppendValue(value);
AppendNewline();
}
/// <summary>
/// Adds the row to the builder.
/// </summary>
/// <param name="row"></param>
[PublicAPI]
public void AppendRow(params string[] row)
{
foreach (string value in row)
AppendValue(value);
AppendNewline();
}
/// <summary>
/// Calls ToString() on the item and adds it to the builder.
/// </summary>
/// <param name="value"></param>
[PublicAPI]
public void AppendValue(object value)
{
AppendValue(string.Format("{0}", value));
}
/// <summary>
/// Adds a value to the builder.
/// </summary>
/// <param name="value"></param>
[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;
}
/// <summary>
/// Adds a New Line To the Builder
/// </summary>
[PublicAPI]
public void AppendNewline()
{
m_Writer.WrappedTextWriter.Write(m_LineTerminator);
m_NewLine = true;
}
public void Dispose()
{
m_Writer.Dispose();
}
/// <summary>
/// Instantiates a new CsvWriter with the properties given in the CsvWriterSettings.
/// </summary>
/// <param name="writer"></param>
/// <param name="settings"></param>
/// <param name="header"></param>
/// <returns></returns>
[PublicAPI]
public static CsvWriter Create(IcdTextWriter writer, CsvWriterSettings settings, params string[] header)
{
return new CsvWriter(writer,
settings.InsertSpaceAfterComma,
settings.AlwaysEscapeEveryValue,
settings.NewLineSequence,
header);
}
}
}

View File

@@ -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;
/// <summary>
/// Gets/Sets whether to insert a space between elements, after the comma
/// Defaults to true.
/// </summary>
[PublicAPI]
public bool InsertSpaceAfterComma
{
get { return m_InsertSpaceAfterComma; }
set { m_InsertSpaceAfterComma = value; }
}
/// <summary>
/// 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.
/// </summary>
[PublicAPI]
public bool AlwaysEscapeEveryValue
{
get { return m_AlwaysEscapeEveryValue; }
set { m_AlwaysEscapeEveryValue = value; }
}
/// <summary>
/// Gets/Sets the newline character or characters to deliniate records.
/// Defaults to System.NewLine.
/// </summary>
[PublicAPI]
public string NewLineSequence
{
get { return m_NewLineSequence; }
set { m_NewLineSequence = value; }
}
}
}

View File

@@ -85,6 +85,8 @@
<Compile Include="Comparers\SequenceComparer.cs" />
<Compile Include="ConsoleColor.cs" />
<Compile Include="EncodingUtils.cs" />
<Compile Include="Csv\CsvWriter.cs" />
<Compile Include="Csv\CsvWriterSettings.cs" />
<Compile Include="EventArguments\BoolEventArgs.cs" />
<Compile Include="EventArguments\CharEventArgs.cs" />
<Compile Include="EventArguments\DateTimeEventArgs.cs" />
@@ -109,6 +111,7 @@
<Compile Include="IO\Compression\IcdZipEntry.cs" />
<Compile Include="IO\IcdBinaryReader.cs" />
<Compile Include="IO\eSeekOrigin.cs" />
<Compile Include="IO\IcdStreamWriter.cs" />
<Compile Include="ProcessorUtils.SimplSharp.cs" />
<Compile Include="ProcessorUtils.Standard.cs" />
<Compile Include="ProgramUtils.SimplSharp.cs" />
@@ -195,12 +198,17 @@
<Compile Include="Timers\SafeTimer.cs" />
<Compile Include="TryUtils.cs" />
<Compile Include="UriUtils.cs" />
<Compile Include="Xml\AbstractGenericXmlConverter.cs" />
<Compile Include="Xml\AbstractXmlConverter.cs" />
<Compile Include="Xml\DefaultXmlConverter.cs" />
<Compile Include="Xml\IcdXmlConvert.cs" />
<Compile Include="Xml\IcdXmlDocument.cs" />
<Compile Include="Xml\IcdXmlException.cs" />
<Compile Include="Xml\IcdXmlReader.cs" />
<Compile Include="Xml\IcdXmlTextWriter.cs" />
<Compile Include="Xml\IcdXmlAttribute.cs" />
<Compile Include="Xml\IXmlConverter.cs" />
<Compile Include="Xml\XmlConverterAttribute.cs" />
<Compile Include="Xml\XmlReaderExtensions.cs" />
<Compile Include="Xml\XmlUtils.cs" />
<None Include="Properties\ControlSystem.cfg" />

View File

@@ -94,5 +94,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));
}
}
}

View File

@@ -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; } }
/// <summary>
/// Constructor.
/// </summary>
/// <param name="baseStreamWriter"></param>
public IcdStreamWriter(StreamWriter baseStreamWriter) : base(baseStreamWriter)
{
}
}
}

View File

@@ -0,0 +1,181 @@
using System;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronXml;
#else
using System.Xml;
#endif
using ICD.Common.Properties;
namespace ICD.Common.Utils.Xml
{
public abstract class AbstractGenericXmlConverter<T> : AbstractXmlConverter
{
/// <summary>
/// Creates a new instance of T.
/// </summary>
/// <returns></returns>
protected abstract T Instantiate();
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
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);
}
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
[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();
}
/// <summary>
/// Override to write attributes to the root element.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected virtual void WriteAttributes(IcdXmlTextWriter writer, T value)
{
}
/// <summary>
/// Override to write elements to the writer.
/// </summary>
/// <param name="writer"></param>
/// <param name="value"></param>
protected virtual void WriteElements(IcdXmlTextWriter writer, T value)
{
}
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
public sealed override object ReadXml(IcdXmlReader reader)
{
return ReadXmlTyped(reader);
}
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
[PublicAPI]
public virtual T ReadXmlTyped(IcdXmlReader reader)
{
// Read into the first node
if (reader.NodeType != XmlNodeType.Element && !reader.ReadToNextElement())
throw new FormatException();
T output = default(T);
bool instantiated = false;
while (true)
{
if (!instantiated)
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
if (reader.IsEmptyElement)
return default(T);
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();
continue;
default:
// Keep reading until we reach the root element.
if (!reader.Read())
throw new FormatException();
continue;
}
}
switch (reader.NodeType)
{
case XmlNodeType.Element:
ReadElement(reader, output);
continue;
case XmlNodeType.EndElement:
// Read out of the end element
reader.Read();
return output;
default:
if (!reader.Read())
return output;
break;
}
}
}
/// <summary>
/// Override to handle the current attribute.
/// </summary>
/// <param name="reader"></param>
/// <param name="instance"></param>
protected virtual void ReadAttribute(IcdXmlReader reader, T instance)
{
}
/// <summary>
/// Override to handle the current element.
/// </summary>
/// <param name="reader"></param>
/// <param name="instance"></param>
protected virtual void ReadElement(IcdXmlReader reader, T instance)
{
// Skip the element
reader.Skip();
}
}
}

View File

@@ -0,0 +1,25 @@
using ICD.Common.Properties;
namespace ICD.Common.Utils.Xml
{
public abstract class AbstractXmlConverter : IXmlConverter
{
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
public abstract void WriteXml(IcdXmlTextWriter writer, string elementName, object value);
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
[PublicAPI]
public abstract object ReadXml(IcdXmlReader reader);
}
}

View File

@@ -0,0 +1,32 @@
using System;
namespace ICD.Common.Utils.Xml
{
public sealed class DefaultXmlConverter : AbstractXmlConverter
{
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
public override void WriteXml(IcdXmlTextWriter writer, string elementName, object value)
{
string elementString = IcdXmlConvert.ToString(value);
writer.WriteElementString(elementName, elementString);
}
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
public override object ReadXml(IcdXmlReader reader)
{
throw new NotSupportedException();
}
}
}

View File

@@ -0,0 +1,22 @@
namespace ICD.Common.Utils.Xml
{
public interface IXmlConverter
{
/// <summary>
/// Writes the XML representation of the object.
/// </summary>
/// <param name="writer"></param>
/// <param name="elementName"></param>
/// <param name="value">The value.</param>
void WriteXml(IcdXmlTextWriter writer, string elementName, object value);
/// <summary>
/// Reads the XML representation of the object.
/// </summary>
/// <param name="reader">The XmlReader to read from.</param>
/// <returns>
/// The object value.
/// </returns>
object ReadXml(IcdXmlReader reader);
}
}

View File

@@ -1,4 +1,6 @@
using System;
using System.Text;
using ICD.Common.Utils.IO;
#if SIMPLSHARP
using Crestron.SimplSharp.CrestronXml;
#else
@@ -9,6 +11,89 @@ namespace ICD.Common.Utils.Xml
{
public static class IcdXmlConvert
{
/// <summary>
/// Serializes the given instance to an xml string.
/// </summary>
/// <param name="elementName"></param>
/// <param name="value"></param>
/// <returns></returns>
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();
}
/// <summary>
/// Deserializes the given xml to an instance of the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="xml"></param>
/// <returns></returns>
public static T DeserializeObject<T>(string xml)
{
return (T)DeserializeObject(typeof(T), xml);
}
/// <summary>
/// Deserializes the given xml to an instance of the given type.
/// </summary>
/// <param name="type"></param>
/// <param name="xml"></param>
/// <returns></returns>
private static object DeserializeObject(Type type, string xml)
{
if (type == null)
throw new ArgumentNullException("type");
using (IcdXmlReader reader = new IcdXmlReader(xml))
return DeserializeObject(type, reader);
}
/// <summary>
/// Deserializes the current node to an instance of the given type.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="reader"></param>
/// <returns></returns>
public static T DeserializeObject<T>(IcdXmlReader reader)
{
if (reader == null)
throw new ArgumentNullException("reader");
return (T)DeserializeObject(typeof(T), reader);
}
/// <summary>
/// Deserializes the current node to an instance of the given type.
/// </summary>
/// <param name="type"></param>
/// <param name="reader"></param>
/// <returns></returns>
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)
{
return XmlConvert.ToString(value);

View File

@@ -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<Type, IXmlConverter> s_InstanceTypeToConverter;
private static readonly Dictionary<Type, IXmlConverter> s_ConverterTypeToConverter;
private readonly Type m_ConverterType;
/// <summary>
/// Gets the converter type.
/// </summary>
public Type ConverterType { get { return m_ConverterType; } }
/// <summary>
/// Static constructor.
/// </summary>
static XmlConverterAttribute()
{
s_InstanceTypeToConverter = new Dictionary<Type, IXmlConverter>();
s_ConverterTypeToConverter = new Dictionary<Type, IXmlConverter>();
}
/// <summary>
/// Constructor.
/// </summary>
/// <param name="converterType"></param>
public XmlConverterAttribute(Type converterType)
{
m_ConverterType = converterType;
}
/// <summary>
/// Gets the XML converter for the given instance.
/// </summary>
/// <param name="value"></param>
/// <returns></returns>
public static IXmlConverter GetConverterForInstance(object value)
{
return value == null ? LazyLoadConverter(typeof(DefaultXmlConverter)) : GetConverterForType(value.GetType());
}
/// <summary>
/// Gets the XML converter for the given type.
/// </summary>
/// <param name="type"></param>
/// <returns></returns>
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<XmlConverterAttribute>(type);
Type converterType = attribute == null ? typeof(DefaultXmlConverter) : attribute.ConverterType;
converter = LazyLoadConverter(converterType);
s_InstanceTypeToConverter[type] = converter;
}
return converter;
}
/// <summary>
/// Lazy-loads the converter of the given type.
/// </summary>
/// <param name="converterType"></param>
/// <returns></returns>
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;
}
}
}

View File

@@ -115,7 +115,7 @@ namespace ICD.Common.Utils.Xml
/// <param name="extends"></param>
/// <param name="callback"></param>
[PublicAPI]
public static void Recurse(this IcdXmlReader extends, Action<XmlRecursionEventArgs> callback)
public static void Recurse(this IcdXmlReader extends, Func<XmlRecursionEventArgs, bool> callback)
{
if (extends == null)
throw new ArgumentNullException("extends");

View File

@@ -138,7 +138,7 @@ namespace ICD.Common.Utils.Xml
/// <param name="xml"></param>
/// <param name="callback"></param>
[PublicAPI]
public static void Recurse(string xml, Action<XmlRecursionEventArgs> callback)
public static void Recurse(string xml, Func<XmlRecursionEventArgs, bool> callback)
{
if (callback == null)
throw new ArgumentNullException("callback");
@@ -152,7 +152,7 @@ namespace ICD.Common.Utils.Xml
/// <param name="xml"></param>
/// <param name="path"></param>
/// <param name="callback"></param>
private static void Recurse(string xml, Stack<string> path, Action<XmlRecursionEventArgs> callback)
private static void Recurse(string xml, Stack<string> path, Func<XmlRecursionEventArgs, bool> 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();
}