<commit_before>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Signum.Utilities;
using Signum.Entities.Reflection;
using Signum.Utilities.Reflection;
using System.Linq.Expressions;
using Signum.Utilities.ExpressionTrees;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;

namespace Signum.Entities
{
    [Serializable]
    public class PropertyRoute : IEquatable<PropertyRoute>, ISerializable
    {
        Type type;
        public PropertyRouteType PropertyRouteType { get; private set; } 
        public FieldInfo FieldInfo { get; private set;}
        public PropertyInfo PropertyInfo { get; private set; }
        public PropertyRoute Parent { get; private set;}

        public MemberInfo[] Members
        {
            get
            {
                return this.Follow(a => a.Parent).Select(a =>
                    a.PropertyRouteType == Entities.PropertyRouteType.Mixin ? a.type :
                    a.FieldInfo ?? (MemberInfo)a.PropertyInfo).Reverse().Skip(1).ToArray();
            }
        }

        public PropertyInfo[] Properties
        {
            get { return this.Follow(a => a.Parent).Select(a => a.PropertyInfo).Reverse().Skip(1).ToArray(); }
        }

        public static PropertyRoute Construct<T, S>(Expression<Func<T, S>> propertyRoute)
            where T : IRootEntity
        {
            PropertyRoute result = Root(typeof(T));

            foreach (var mi in Reflector.GetMemberList(propertyRoute))
            {
                if (mi is MethodInfo && ((MethodInfo)mi).IsInstantiationOf(MixinDeclarations.miMixin))
                    result = result.Add(((MethodInfo)mi).GetGenericArguments()[0]);
                else
                    result = result.Add(mi);
            }
            return result;
        }

     
        public PropertyRoute Add(string fieldOrProperty)
        {
            return Add(GetMember(fieldOrProperty));
        }


        MemberInfo GetMember(string fieldOrProperty)
        {
            MemberInfo mi = (MemberInfo)Type.GetProperty(fieldOrProperty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, null, this.Type.IsMList() ? new[] { typeof(int) } : new Type[0], null) ??
                            (MemberInfo)Type.GetField(fieldOrProperty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            if (mi == null && Type.IsIdentifiableEntity())
            {
                string name = ExtractMixin(fieldOrProperty);

                mi = MixinDeclarations.GetMixinDeclarations(Type).FirstOrDefault(t => t.Name == name);
            }

            if (mi == null)
                throw new InvalidOperationException("{0}.{1} does not exist".Formato(this, fieldOrProperty));

            return mi;
        }

        static string ExtractMixin(string fieldOrProperty)
        {
            Match match = Regex.Match(fieldOrProperty, @"^\[(?<type>.*)\]$");

            if (!match.Success)
                return null;

            return match.Groups["type"].Value;
        }

        public PropertyRoute Add(MemberInfo fieldOrProperty)
        {
            if (this.Type.IsIIdentifiable() && PropertyRouteType != PropertyRouteType.Root)
            {
                Implementations imp = GetImplementations();

                Type only;
                if (imp.IsByAll || (only = imp.Types.Only()) == null)
                    throw new InvalidOperationException("Attempt to make a PropertyRoute on a {0}. Cast first".Formato(imp));

                return new PropertyRoute(Root(only), fieldOrProperty);
            }

            return new PropertyRoute(this, fieldOrProperty);
        }

        PropertyRoute(PropertyRoute parent, MemberInfo fieldOrProperty)
        {
            SetParentAndProperty(parent, fieldOrProperty);
        }

        void SetParentAndProperty(PropertyRoute parent, MemberInfo fieldOrProperty)
        {
            if (fieldOrProperty == null)
                throw new ArgumentNullException("fieldOrProperty");

            if (parent == null)
                throw new ArgumentNullException("parent");

            this.Parent = parent;

            if (parent.Type.IsIIdentifiable() && parent.PropertyRouteType != PropertyRouteType.Root)
                throw new ArgumentException("Parent can not be a non-root Identifiable");

            if (fieldOrProperty is PropertyInfo && Reflector.IsMList(parent.Type))
            {
                if (fieldOrProperty.Name != "Item")
                    throw new NotSupportedException("PropertyInfo {0} is not supported".Formato(fieldOrProperty.Name));

                PropertyInfo = (PropertyInfo)fieldOrProperty;
                PropertyRouteType = PropertyRouteType.MListItems;
            }
            else if (fieldOrProperty is PropertyInfo && parent.Type.IsLite())
            {
                if (fieldOrProperty.Name != "Entity" && fieldOrProperty.Name != "EntityOrNull")
                    throw new NotSupportedException("PropertyInfo {0} is not supported".Formato(fieldOrProperty.Name));

                PropertyInfo = (PropertyInfo)fieldOrProperty;
                PropertyRouteType = PropertyRouteType.LiteEntity;
            }
            else if (typeof(IdentifiableEntity).IsAssignableFrom(parent.type) && fieldOrProperty is Type)
            {
                MixinDeclarations.AssertDefined(parent.type, (Type)fieldOrProperty);

                type = (Type)fieldOrProperty;
                PropertyRouteType = PropertyRouteType.Mixin;
            }
            else if (typeof(ModifiableEntity).IsAssignableFrom(parent.Type) || typeof(IRootEntity).IsAssignableFrom(parent.Type))
            {
                PropertyRouteType = PropertyRouteType.FieldOrProperty;
                if (fieldOrProperty is PropertyInfo)
                {
                    if (!parent.Type.Follow(a => a.BaseType).Contains(fieldOrProperty.DeclaringType))
                    {
                        var pi = (PropertyInfo)fieldOrProperty;

                        if (!parent.Type.GetInterfaces().Contains(fieldOrProperty.DeclaringType))
                            throw new ArgumentException("PropertyInfo {0} not found on {1}".Formato(pi.PropertyName(), parent.Type));

                        var otherProperty = parent.Type.Follow(a => a.BaseType)
                            .Select(a => a.GetProperty(fieldOrProperty.Name, BindingFlags.Public | BindingFlags.Instance, null, null, new Type[0], null)).NotNull().FirstEx();

                        if (otherProperty == null)
                            throw new ArgumentException("PropertyInfo {0} not found on {1}".Formato(pi.PropertyName(), parent.Type));

                        fieldOrProperty = otherProperty;
                    }

                    PropertyInfo = (PropertyInfo)fieldOrProperty;
                    FieldInfo = Reflector.TryFindFieldInfo(Parent.Type, PropertyInfo);
                }
                else
                {
                    FieldInfo = (FieldInfo)fieldOrProperty;<commit_msg>MixinDeclarations.AssertDefined -> AssertDeclared<commit_after>﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Signum.Utilities;
using Signum.Entities.Reflection;
using Signum.Utilities.Reflection;
using System.Linq.Expressions;
using Signum.Utilities.ExpressionTrees;
using System.Runtime.Serialization;
using System.Text.RegularExpressions;

namespace Signum.Entities
{
    [Serializable]
    public class PropertyRoute : IEquatable<PropertyRoute>, ISerializable
    {
        Type type;
        public PropertyRouteType PropertyRouteType { get; private set; } 
        public FieldInfo FieldInfo { get; private set;}
        public PropertyInfo PropertyInfo { get; private set; }
        public PropertyRoute Parent { get; private set;}

        public MemberInfo[] Members
        {
            get
            {
                return this.Follow(a => a.Parent).Select(a =>
                    a.PropertyRouteType == Entities.PropertyRouteType.Mixin ? a.type :
                    a.FieldInfo ?? (MemberInfo)a.PropertyInfo).Reverse().Skip(1).ToArray();
            }
        }

        public PropertyInfo[] Properties
        {
            get { return this.Follow(a => a.Parent).Select(a => a.PropertyInfo).Reverse().Skip(1).ToArray(); }
        }

        public static PropertyRoute Construct<T, S>(Expression<Func<T, S>> propertyRoute)
            where T : IRootEntity
        {
            PropertyRoute result = Root(typeof(T));

            foreach (var mi in Reflector.GetMemberList(propertyRoute))
            {
                if (mi is MethodInfo && ((MethodInfo)mi).IsInstantiationOf(MixinDeclarations.miMixin))
                    result = result.Add(((MethodInfo)mi).GetGenericArguments()[0]);
                else
                    result = result.Add(mi);
            }
            return result;
        }

     
        public PropertyRoute Add(string fieldOrProperty)
        {
            return Add(GetMember(fieldOrProperty));
        }


        MemberInfo GetMember(string fieldOrProperty)
        {
            MemberInfo mi = (MemberInfo)Type.GetProperty(fieldOrProperty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, null, this.Type.IsMList() ? new[] { typeof(int) } : new Type[0], null) ??
                            (MemberInfo)Type.GetField(fieldOrProperty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);

            if (mi == null && Type.IsIdentifiableEntity())
            {
                string name = ExtractMixin(fieldOrProperty);

                mi = MixinDeclarations.GetMixinDeclarations(Type).FirstOrDefault(t => t.Name == name);
            }

            if (mi == null)
                throw new InvalidOperationException("{0}.{1} does not exist".Formato(this, fieldOrProperty));

            return mi;
        }

        static string ExtractMixin(string fieldOrProperty)
        {
            Match match = Regex.Match(fieldOrProperty, @"^\[(?<type>.*)\]$");

            if (!match.Success)
                return null;

            return match.Groups["type"].Value;
        }

        public PropertyRoute Add(MemberInfo fieldOrProperty)
        {
            if (this.Type.IsIIdentifiable() && PropertyRouteType != PropertyRouteType.Root)
            {
                Implementations imp = GetImplementations();

                Type only;
                if (imp.IsByAll || (only = imp.Types.Only()) == null)
                    throw new InvalidOperationException("Attempt to make a PropertyRoute on a {0}. Cast first".Formato(imp));

                return new PropertyRoute(Root(only), fieldOrProperty);
            }

            return new PropertyRoute(this, fieldOrProperty);
        }

        PropertyRoute(PropertyRoute parent, MemberInfo fieldOrProperty)
        {
            SetParentAndProperty(parent, fieldOrProperty);
        }

        void SetParentAndProperty(PropertyRoute parent, MemberInfo fieldOrProperty)
        {
            if (fieldOrProperty == null)
                throw new ArgumentNullException("fieldOrProperty");

            if (parent == null)
                throw new ArgumentNullException("parent");

            this.Parent = parent;

            if (parent.Type.IsIIdentifiable() && parent.PropertyRouteType != PropertyRouteType.Root)
                throw new ArgumentException("Parent can not be a non-root Identifiable");

            if (fieldOrProperty is PropertyInfo && Reflector.IsMList(parent.Type))
            {
                if (fieldOrProperty.Name != "Item")
                    throw new NotSupportedException("PropertyInfo {0} is not supported".Formato(fieldOrProperty.Name));

                PropertyInfo = (PropertyInfo)fieldOrProperty;
                PropertyRouteType = PropertyRouteType.MListItems;
            }
            else if (fieldOrProperty is PropertyInfo && parent.Type.IsLite())
            {
                if (fieldOrProperty.Name != "Entity" && fieldOrProperty.Name != "EntityOrNull")
                    throw new NotSupportedException("PropertyInfo {0} is not supported".Formato(fieldOrProperty.Name));

                PropertyInfo = (PropertyInfo)fieldOrProperty;
                PropertyRouteType = PropertyRouteType.LiteEntity;
            }
            else if (typeof(IdentifiableEntity).IsAssignableFrom(parent.type) && fieldOrProperty is Type)
            {
                MixinDeclarations.AssertDeclared(parent.type, (Type)fieldOrProperty);

                type = (Type)fieldOrProperty;
                PropertyRouteType = PropertyRouteType.Mixin;
            }
            else if (typeof(ModifiableEntity).IsAssignableFrom(parent.Type) || typeof(IRootEntity).IsAssignableFrom(parent.Type))
            {
                PropertyRouteType = PropertyRouteType.FieldOrProperty;
                if (fieldOrProperty is PropertyInfo)
                {
                    if (!parent.Type.Follow(a => a.BaseType).Contains(fieldOrProperty.DeclaringType))
                    {
                        var pi = (PropertyInfo)fieldOrProperty;

                        if (!parent.Type.GetInterfaces().Contains(fieldOrProperty.DeclaringType))
                            throw new ArgumentException("PropertyInfo {0} not found on {1}".Formato(pi.PropertyName(), parent.Type));

                        var otherProperty = parent.Type.Follow(a => a.BaseType)
                            .Select(a => a.GetProperty(fieldOrProperty.Name, BindingFlags.Public | BindingFlags.Instance, null, null, new Type[0], null)).NotNull().FirstEx();

                        if (otherProperty == null)
                            throw new ArgumentException("PropertyInfo {0} not found on {1}".Formato(pi.PropertyName(), parent.Type));

                        fieldOrProperty = otherProperty;
                    }

                    PropertyInfo = (PropertyInfo)fieldOrProperty;
                    FieldInfo = Reflector.TryFindFieldInfo(Parent.Type, PropertyInfo);
                }
                else
                {
                    FieldInfo = (FieldInfo)fieldOrProperty;
{'fraction_non_alphanumeric': 0.09655704368557605, 'fraction_numerical': 0.0019633966762499125, 'mean_word_length': 2.037699680511182, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 30, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7297648', 'n_tokens_mistral': 3585, 'n_tokens_neox': 3423, 'n_words': 726}