using System; using System.ComponentModel; using System.Linq; using System.Reflection; using Fasterflect; namespace LeafWeb.Core.Utility { public static class ParsedObjectFactory 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()).ToArray(); } /// /// Create an object type T filling properties from the given title values /// /// Colon separated title: values 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().IsTitleMatch(title)); } if (property == null) { // handles case for row number, i.e. "Value" value = row.Trim(); property = properties .FirstOrDefault(p => p.Attribute().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[] 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().IsTitleMatch(title)) ?? properties.FirstOrDefault(p => p.Attribute().IsPositionMatch(position)); } private static PropertyInfo MatchPropertyExact(PropertyInfo[] properties, string title, int position) { return properties .FirstOrDefault(p => { var attribute = p.Attribute(); 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; } } }