<commit_before>﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
using EfCore.Shaman.Reflection;
using EfCore.Shaman.Services;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;

namespace EfCore.Shaman.ModelScanner
{
    public class ModelInfo
    {
        public ModelInfo(Type dbContextType, ShamanOptions options = null)
        {
            _dbContextType = dbContextType;
            options = options ?? ShamanOptions.CreateShamanOptions(dbContextType);
            UsedShamanOptions = options;
            _logger = options.Logger ?? EmptyShamanLogger.Instance;
            Prepare();
        }

        public static ModelInfo Make<T>(ShamanOptions options = null)
        {
            return new ModelInfo(typeof(T), options);
        }

        public static bool NotNullFromPropertyType(Type type)
        {
            if (type == typeof(string)) return false;
            var typeInfo = type.GetTypeInfo();
            if (typeInfo.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                return false;
            if (typeInfo.IsEnum || typeInfo.IsValueType) return true;
            return false;
        }

        private static string GetTableName(Type entityType, string propertyName,
            IReadOnlyDictionary<Type, string> tableNames)
        {
            if (tableNames != null)
            {
                string name;
                if (tableNames.TryGetValue(entityType, out name))
                    return name;
            }
            var a = entityType.GetTypeInfo().GetCustomAttribute<TableAttribute>();
            return string.IsNullOrEmpty(a?.Name) ? propertyName : a.Name;
        }

        private static IReadOnlyDictionary<Type, string> GetTableNamesFromModel(EfModelWrapper model,
            IShamanLogger logger)
        {
            if (model == null) return null;
            var result = new Dictionary<Type, string>();
            foreach (var entityType in model.EntityTypes)
            {
                result[entityType.ClrType] = entityType.TableName;
                logger.Log(typeof(ModelInfo), nameof(GetTableNamesFromModel),
                    $"Table name for {entityType.ClrType.Name}: {entityType.TableName}");
            }
            return result;
        }

        public DbSetInfo DbSet<T>()
            => _dbSets.Values.SingleOrDefault(a => a.EntityType == typeof(T));

        public DbSetInfo GetByTableName(FullTableName tableName)
        {
            _dbSets.TryGetValue(tableName, out var entity);
            return entity;
        }


        private DbSetInfo CreateDbSetWrapper(Type entityType, string propertyName,
            IReadOnlyDictionary<Type, string> tableNames)
        {
            var dbSetInfoUpdateServices = UsedShamanOptions.Services?.OfType<IDbSetInfoUpdateService>().ToArray();
            var dbSetInfo = new DbSetInfo(entityType, GetTableName(entityType, propertyName, tableNames), DefaultSchema);
            {
                if (dbSetInfoUpdateServices != null)
                    foreach (var i in dbSetInfoUpdateServices)
                        i.UpdateDbSetInfo(dbSetInfo, entityType, _dbContextType, _logger);
            }
            var columnInfoUpdateServices = UsedShamanOptions.Services?.OfType<IColumnInfoUpdateService>().ToArray();
            var useDirectSaverForType = entityType.GetTypeInfo().GetCustomAttribute<NoDirectSaverAttribute>() == null;
            foreach (var propertyInfo in entityType.GetProperties())
            {
                if (propertyInfo.NotMappedByEntityFramework())
                    continue;
                var columnInfo = new ColumnInfo(dbSetInfo.Properites.Count, propertyInfo.Name)
                {
                    NotNull = NotNullFromPropertyType(propertyInfo.PropertyType)
                };
                if (useDirectSaverForType)
                {
                    var readerWriter = new SimplePropertyReaderWriter(entityType, propertyInfo, _logger);
                    columnInfo.ValueReader = readerWriter;
                    columnInfo.ValueWriter = readerWriter;
                }
                if (columnInfoUpdateServices != null)
                    foreach (var service in columnInfoUpdateServices)
                        service.UpdateColumnInfo(columnInfo, propertyInfo, _logger);
                dbSetInfo.Properites.Add(columnInfo);
            }
            return dbSetInfo;
        }

        private void Log(string methodName, string message)<commit_msg>Skip processing static properties<commit_after>﻿using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
using EfCore.Shaman.Reflection;
using EfCore.Shaman.Services;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;

namespace EfCore.Shaman.ModelScanner
{
    public class ModelInfo
    {
        public ModelInfo(Type dbContextType, ShamanOptions options = null)
        {
            _dbContextType = dbContextType;
            options = options ?? ShamanOptions.CreateShamanOptions(dbContextType);
            UsedShamanOptions = options;
            _logger = options.Logger ?? EmptyShamanLogger.Instance;
            Prepare();
        }

        public static ModelInfo Make<T>(ShamanOptions options = null)
        {
            return new ModelInfo(typeof(T), options);
        }

        public static bool NotNullFromPropertyType(Type type)
        {
            if (type == typeof(string)) return false;
            var typeInfo = type.GetTypeInfo();
            if (typeInfo.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>))
                return false;
            if (typeInfo.IsEnum || typeInfo.IsValueType) return true;
            return false;
        }

        private static string GetTableName(Type entityType, string propertyName,
            IReadOnlyDictionary<Type, string> tableNames)
        {
            if (tableNames != null)
            {
                string name;
                if (tableNames.TryGetValue(entityType, out name))
                    return name;
            }
            var a = entityType.GetTypeInfo().GetCustomAttribute<TableAttribute>();
            return string.IsNullOrEmpty(a?.Name) ? propertyName : a.Name;
        }

        private static IReadOnlyDictionary<Type, string> GetTableNamesFromModel(EfModelWrapper model,
            IShamanLogger logger)
        {
            if (model == null) return null;
            var result = new Dictionary<Type, string>();
            foreach (var entityType in model.EntityTypes)
            {
                result[entityType.ClrType] = entityType.TableName;
                logger.Log(typeof(ModelInfo), nameof(GetTableNamesFromModel),
                    $"Table name for {entityType.ClrType.Name}: {entityType.TableName}");
            }
            return result;
        }

        public DbSetInfo DbSet<T>()
            => _dbSets.Values.SingleOrDefault(a => a.EntityType == typeof(T));

        public DbSetInfo GetByTableName(FullTableName tableName)
        {
            _dbSets.TryGetValue(tableName, out var entity);
            return entity;
        }


        private DbSetInfo CreateDbSetWrapper(Type entityType, string propertyName,
            IReadOnlyDictionary<Type, string> tableNames)
        {
            var dbSetInfoUpdateServices = UsedShamanOptions.Services?.OfType<IDbSetInfoUpdateService>().ToArray();
            var dbSetInfo = new DbSetInfo(entityType, GetTableName(entityType, propertyName, tableNames), DefaultSchema);
            {
                if (dbSetInfoUpdateServices != null)
                    foreach (var i in dbSetInfoUpdateServices)
                        i.UpdateDbSetInfo(dbSetInfo, entityType, _dbContextType, _logger);
            }
            var columnInfoUpdateServices = UsedShamanOptions.Services?.OfType<IColumnInfoUpdateService>().ToArray();
            var useDirectSaverForType = entityType.GetTypeInfo().GetCustomAttribute<NoDirectSaverAttribute>() == null;
            foreach (var propertyInfo in entityType.GetProperties(BindingFlags.Instance | BindingFlags.Public))
            {
                if (propertyInfo.NotMappedByEntityFramework())
                    continue;
                var columnInfo = new ColumnInfo(dbSetInfo.Properites.Count, propertyInfo.Name)
                {
                    NotNull = NotNullFromPropertyType(propertyInfo.PropertyType)
                };
                if (useDirectSaverForType)
                {
                    var readerWriter = new SimplePropertyReaderWriter(entityType, propertyInfo, _logger);
                    columnInfo.ValueReader = readerWriter;
                    columnInfo.ValueWriter = readerWriter;
                }
                if (columnInfoUpdateServices != null)
                    foreach (var service in columnInfoUpdateServices)
                        service.UpdateColumnInfo(columnInfo, propertyInfo, _logger);
                dbSetInfo.Properites.Add(columnInfo);
            }
            return dbSetInfo;
        }

        private void Log(string methodName, string message)
{'fraction_non_alphanumeric': 0.07891053934761547, 'fraction_numerical': 0.0, 'mean_word_length': 2.113270777479893, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 29, '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': '1550618', 'n_tokens_mistral': 2301, 'n_tokens_neox': 2234, 'n_words': 483}