Files
LeafWeb/Core/Utility/ParsedObjectFactory.cs
T

170 lines
4.7 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();
}
/// <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 = new ParseInfoPropertyMatcherWithCache<T>().ParseInfoProperties;
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 matcher = new ParseInfoPropertyMatcherWithCache<T>();
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 = matcher.MatchProperty(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 matcher = new ParseInfoPropertyMatcherWithCache<T>();
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 = matcher.MatchProperty(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 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;
value = HandleBoolEncodedProperty(property, value);
// convertedValue = Convert.ChangeType(value, t);
var converter = TypeDescriptor.GetConverter(t);
convertedValue = converter.ConvertTo(value, t);
}
catch (Exception)
{
convertedValue = null;
return false;
}
return true;
}
private static object HandleBoolEncodedProperty(PropertyInfo property, object value)
{
var boolEncodedPosition = property.Attribute<ParseInfoAttribute>().BoolEncodedPosition;
if (boolEncodedPosition == null) return value;
var v = (value as string)?.Substring(boolEncodedPosition.Value -1, 1);
switch (v)
{
// these values are False=1, True=2
case "1":
value = false;
break;
case "2":
value = true;
break;
default:
throw new ArgumentOutOfRangeException($"{property.Name} {boolEncodedPosition} {value} {v}");
}
return value;
}
}
}