FlatLoopValueInjection with support for collections (Listis)

May 13, 2013 at 11:20 PM
How could I flatten this structure?
//--- THIS ---
    public class Product
    {
        public IList<ProductTranslation> ProductTranslations { get; set; }
    }

    public class ProductTranslation
    {
        public string Name { get; set; }

        public Language Language { get; set; }
    }

    public class Language
    {
        public string Name { get; set; }
    }
    //--- TO THIS ---
    public class ProductFlat
    {
        public string ProductTranslations_Name { get; set; }
        public string ProductTranslations_Language_Name { get; set; }
    }
    //-----------------
so basically, when instantiated with a data it would be:
var product = new Product()
                               {
                                   ProductTranslations = new List<ProductTranslation>()
                                                          {
                                                              new ProductTranslation() { Name = "One", Language = new Language(){ Name = "English" }},
                                                              new ProductTranslation() { Name = "Two", Language = new Language(){ Name = "English" }},
                                                              new ProductTranslation() { Name = "Eins", Language = new Language(){ Name = "Deutschland" }},
                                                          }
                               };

            var flet = new List<ProductFlat>();

            flet.InjectFrom<FlatLoopValueInjection>(product);
Basically, this should be output:
flet = new List<ProductFlat>()
                           {
                               new ProductFlat(){ ProductTranslations_Name = "One", ProductTranslations_Language_Name = "English"},
                               new ProductFlat(){ ProductTranslations_Name = "Two", ProductTranslations_Language_Name = "English"},
                               new ProductFlat(){ ProductTranslations_Name = "Eins", ProductTranslations_Language_Name = "Deutschland"},
                           };
What implementation of FlatLoopValueInjection should I use? Thanks.
May 19, 2013 at 10:55 PM
In case that somebody needs solution to this, I'm attaching source:
public class FlatLoopWithListValueInjection : LoopValueInjectionBase
    {
        protected override void Inject(object source, object target)
        {
            foreach (PropertyDescriptor t in target.GetProps())
            {
                var t1 = t;
                var es = UberFlatter.Flat(t.Name, source, type => TypesMatch(type, t1.PropertyType));

                if (es.Count() == 0) continue;
                var endpoint = es.First();
                if (endpoint == null) continue;
                var val = endpoint.Property.GetValue(endpoint.Component);

                if (AllowSetValue(val))
                    t.SetValue(target, SetValue(val));
            }
        }

        protected virtual bool TypesMatch(Type sourceType, Type targetType)
        {
            return targetType == sourceType;
        }

        protected virtual object SetValue(object sourcePropertyValue)
        {
            return sourcePropertyValue;
        }
    }
 public static class TrailFinder
    {
        public static IEnumerable<IList<string>> GetTrails(string upn, IEnumerable<PropertyInfo> all, Func<Type, bool> f, StringComparison comparison)
        {
            return all.SelectMany(p => GetTrails(upn, p, f, new List<string>(), comparison));
        }

        public static IEnumerable<IList<string>> GetTrails(
            string upn, PropertyInfo prop, Func<Type, bool> f, IList<string> root, StringComparison comparison)
        {
            if (string.Equals(upn, prop.Name, comparison) && f(prop.PropertyType))
            {
                var l = new List<string> { prop.Name };
                yield return l;
                yield break;
            }

            if (upn.StartsWith(prop.Name + "_", comparison))
            {
                root.Add(prop.Name);

                if (prop.PropertyType.IsGenericType)
                {
                    foreach (PropertyInfo pro in prop.PropertyType.GetGenericArguments().First().GetProperties())
                    {
                        foreach (var trail in
                            GetTrails(upn.RemovePrefix(prop.Name + "_", comparison), pro, f, root, comparison))
                        {
                            var r = new List<string> { prop.Name };
                            r.AddRange(trail);
                            yield return r;
                        }
                    }
                }
                else
                {
                    foreach (PropertyInfo pro in prop.PropertyType.GetInfos())
                    {
                        foreach (var trail in
                            GetTrails(upn.RemovePrefix(prop.Name + "_", comparison), pro, f, root, comparison))
                        {
                            var r = new List<string> { prop.Name };
                            r.AddRange(trail);
                            yield return r;
                        }
                    }
                }
            }
        }
    }
public static class Tunnelier
    {
        public static PropertyWithComponent Digg(IList<string> trail, object o)
        {
            if (trail.Count == 1)
            {
                var prop = o.GetProps().GetByName(trail[0]);
                return new PropertyWithComponent { Component = o, Property = prop };
            }
            else
            {
                var prop = o.GetProps().GetByName(trail[0]);

                if (prop.GetValue(o) == null)
                {
                    prop.SetValue(o, Activator.CreateInstance(prop.PropertyType));
                }

                var val = prop.GetValue(o);

                trail.RemoveAt(0);
                return Digg(trail, val);
            }
        }

        public static PropertyWithComponent GetValue(IList<string> trail, object o)
        {
            if (trail.Count == 1)
            {
                var prop = o.GetProps().GetByName(trail[0]);
                return new PropertyWithComponent { Component = o, Property = prop };
            }

            var propx = o.GetProps().GetByName(trail[0]);

            object val = null;
            if (propx.PropertyType.IsGenericType)
            {
                var items = (IEnumerable)propx.GetValue(o);
                if (items != null)
                {
                    val = items.Cast<object>().FirstOrDefault();
                }
            }
            else
            {
                val = propx.GetValue(o);
            }
            if (val == null)
            {
                return null;
            }
            trail.RemoveAt(0);
            return GetValue(trail, val);
        }
    }
public static class UberFlatter
    {
        public static IEnumerable<PropertyWithComponent> Unflat(string flatPropertyName, object target, Func<Type, bool> f, StringComparison comparison)
        {
            var trails = TrailFinder.GetTrails(flatPropertyName, target.GetType().GetInfos(), f, comparison);

            return trails.Select(trail => Tunnelier.Digg(trail, target));
        }

        public static IEnumerable<PropertyWithComponent> Unflat(string flatPropertyName, object target, Func<Type, bool> f)
        {
            return Unflat(flatPropertyName, target, f, StringComparison.Ordinal);
        }

        public static IEnumerable<PropertyWithComponent> Unflat(string flatPropertyName, object target)
        {
            return Unflat(flatPropertyName, target, o => true);
        }

        public static IEnumerable<PropertyWithComponent> Flat(string flatPropertyName, object source, Func<Type, bool> f)
        {
            return Flat(flatPropertyName, source, f, StringComparison.Ordinal);
        }

        public static IEnumerable<PropertyWithComponent> Flat(string flatPropertyName, object source, Func<Type, bool> f, StringComparison comparison)
        {
            var trails = TrailFinder.GetTrails(flatPropertyName, source.GetType().GetInfos(), f, comparison);

            return trails.Select(trail => Tunnelier.GetValue(trail, source));
        }

        public static IEnumerable<PropertyWithComponent> Flat(string flatPropertyName, object source)
        {
            return Flat(flatPropertyName, source, o => true);
        }
    }
public static class ValueInjecterHelpers
    {
        public static ICollection<TTo> InjectFromFlatLoopWithListValueInjection<TTo, TFrom>(this ICollection<TTo> to, IEnumerable<TFrom> from)
            where TTo : new()
        {
            foreach (var source in from)
            {
                var target = new TTo();
                target.InjectFrom<FlatLoopWithListValueInjection>(source);
                to.Add(target);
            }
            return to;
        }
    }