CntrlComparison parsing

This commit is contained in:
2015-12-04 10:15:51 -05:00
parent 4d46f206ac
commit 685e8c8658
39 changed files with 820 additions and 491 deletions
+47
View File
@@ -0,0 +1,47 @@
using System;
using System.ComponentModel;
using System.Globalization;
namespace LeafWeb.Core.Utility
{
// http://stackoverflow.com/a/19022931/99492
public class BoolTypeConverter : TypeConverter
{
public static void Register()
{
TypeDescriptor.AddAttributes(typeof(Boolean),
new TypeConverterAttribute(typeof(BoolTypeConverter)));
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
{
if (destinationType == typeof(bool))
{
return true;
}
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
{
if (value is string)
{
var s = value as string;
if (string.IsNullOrEmpty(s))
return false;
switch (s.Trim().ToUpper())
{
case "TRUE":
case "YES":
case "1":
case "-1":
return true;
default:
return false;
}
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
}
+12
View File
@@ -0,0 +1,12 @@
using System;
namespace LeafWeb.Core.Utility
{
public class ParseException : Exception
{
public ParseException(string message)
: base(message)
{
}
}
}
+45
View File
@@ -0,0 +1,45 @@
using System;
using System.Linq;
using System.Runtime.CompilerServices;
namespace LeafWeb.Core.Utility
{
[AttributeUsage(AttributeTargets.Property)]
public class ParseInfoAttribute : Attribute
{
public int Position { get; private set; }
public string Units { get; private set; }
public string Title { get; private set; }
public string AlterateTitle { get; private set; }
public string ExampleValue { get; private set; }
public string[] FieldNames
{
get
{
var fieldNames = new[] {Title, Title.SplitCamelCase()};
if (!string.IsNullOrEmpty(AlterateTitle))
fieldNames = fieldNames.Concat(new[] { AlterateTitle }).ToArray();
return fieldNames;
}
}
public ParseInfoAttribute(int position, [CallerMemberName]string title = null, string units = null, string alternateTitle = null, string exampleValue = null)
{
AlterateTitle = alternateTitle;
ExampleValue = exampleValue;
Title = title;
Position = position;
Units = units;
}
public bool IsTitleMatch(string title)
{
return FieldNames.Any(t => string.Compare(title, t, StringComparison.InvariantCultureIgnoreCase) == 0);
}
public bool IsPositionMatch(int index)
{
return Position == index;
}
}
}
+195
View File
@@ -0,0 +1,195 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using Fasterflect;
namespace LeafWeb.Core.Utility
{
public static class ParsedObjectFactory<T> where T : new()
{
static ParsedObjectFactory()
{
// register this for usage by TypeDescriptor.GetConverter
BoolTypeConverter.Register();
}
private static PropertyInfo[] GetProperties()
{
var propertyInfos = typeof(T).Properties();
return propertyInfos.Where(p => p.HasAttribute<ParseInfoAttribute>()).ToArray();
}
/// <summary>
/// Create an object type T filling properties from the given title values
/// </summary>
/// <param name="titleValues">Colon separated title: values</param>
public static T Create(string[] titleValues)
{
var properties = GetProperties();
var obj = new T();
// take each of the
for (var index = 0; index < titleValues.Length; index++)
{
PropertyInfo property = null;
string value = null;
var lineNumber = index + 1;
var row = titleValues[index];
var split = row.Split(':');
if (split.Length > 1)
{
// handles case for "Title : Value"
var title = split[0].Trim();
value = split[1].Trim();
property =
properties
.FirstOrDefault(p => p.Attribute<ParseInfoAttribute>().IsTitleMatch(title));
}
if (property == null)
{
// handles case for row number, i.e. "Value"
value = row.Trim();
property =
properties
.FirstOrDefault(p => p.Attribute<ParseInfoAttribute>().IsPositionMatch(lineNumber));
}
if (property != null)
{
object convertedVal;
if (!TryConvertValue(property, value, out convertedVal))
throw new ParseException($"Cannot convert value '{value}' for {property.Name} at line number {lineNumber}");
property.Set(obj, convertedVal);
}
}
return obj;
}
public static T[] Create(string[] titles, string[][] valueArrays)
{
var properties = GetProperties();
var objs = new T[valueArrays.Length];
for (var vIndex = 0; vIndex < valueArrays.Length; vIndex++)
{
var obj = new T();
var values = valueArrays[vIndex];
for (var tIndex = 0; tIndex < titles.Length; tIndex++)
{
var title = titles[tIndex];
var value = values[tIndex];
var position = tIndex + 1;
if (IsMissingValue(value))
continue;
var property = MatchProperty(properties, title, position);
if (property != null)
{
object convertedVal;
if (!TryConvertValue(property, value, out convertedVal))
throw new ParseException($"Cannot convert value '{value}' for {property.Name} in position {position}");
property.Set(obj, convertedVal);
}
}
objs[vIndex] = obj;
}
return objs;
}
public static T Create(Tuple<string, string>[] titleValues)
{
var properties = GetProperties();
var obj = new T();
for (var index = 0; index < titleValues.Length; index++)
{
var item = titleValues[index];
var position = index + 1;
var title = item.Item1.Trim();
var value = item.Item2.Trim();
if (IsMissingValue(value))
continue;
var property = MatchProperty(properties, title, position);
if (property != null)
{
object convertedVal;
if (!TryConvertValue(property, value, out convertedVal))
throw new ParseException($"Cannot convert value '{value}' for {property.Name} in position {position}");
property.Set(obj, convertedVal);
}
}
return obj;
}
private static bool IsMissingValue(string value)
{
return string.IsNullOrEmpty(value) || value == "NA" || value == "-9999";
}
private static PropertyInfo MatchProperty(PropertyInfo[] properties, string title, int position)
{
return
properties.FirstOrDefault(p => p.Attribute<ParseInfoAttribute>().IsTitleMatch(title)) ??
properties.FirstOrDefault(p => p.Attribute<ParseInfoAttribute>().IsPositionMatch(position));
}
private static PropertyInfo MatchPropertyExact(PropertyInfo[] properties, string title, int position)
{
return
properties
.FirstOrDefault(p =>
{
var attribute = p.Attribute<ParseInfoAttribute>();
return attribute.IsTitleMatch(title) && attribute.IsPositionMatch(position);
});
}
public static bool IsPropertiesTitlesMatch(string[] titles)
{
var properties = GetProperties();
var propertyMatch = 0;
var propertyNoMatch = 0;
for (var i = 0; i < titles.Length; i++)
{
var title = titles[i];
var position = i + 1;
if (string.IsNullOrEmpty(title))
continue;
var property = MatchPropertyExact(properties, title, position);
if (property != null)
propertyMatch++;
else
propertyNoMatch++;
}
return propertyMatch / (double) propertyNoMatch > .9;
}
private static bool TryConvertValue(PropertyInfo property, object value, out object convertedValue)
{
try
{
// http://stackoverflow.com/questions/3531318/convert-changetype-fails-on-nullable-types
var t = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
// convertedValue = Convert.ChangeType(value, t);
var converter = TypeDescriptor.GetConverter(t);
convertedValue = converter.ConvertTo(value, t);
}
catch (Exception)
{
convertedValue = null;
return false;
}
return true;
}
}
}
+3 -4
View File
@@ -1,5 +1,4 @@
using System;
using System.Globalization;
using System.Globalization;
using System.Linq;
namespace LeafWeb.Core.Utility
@@ -9,9 +8,9 @@ namespace LeafWeb.Core.Utility
public static string SplitCamelCase(this string str)
{
return str.Aggregate(
String.Empty,
string.Empty,
(current, c) =>
current + (Char.IsUpper(c) && current.Length > 0 ? " " + c : c.ToString(CultureInfo.InvariantCulture)));
current + (char.IsUpper(c) && current.Length > 0 ? " " + c : c.ToString(CultureInfo.InvariantCulture)));
}
}
}