195 lines
5.2 KiB
C#
195 lines
5.2 KiB
C#
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;
|
|
}
|
|
}
|
|
} |