Глобальный рефакторинг пространств имён
This commit is contained in:
@@ -0,0 +1,296 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using LinqToDB;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Pf2e.Scrapper.Processors.Attributes;
|
||||
using Pf2e.Scrapper.Processors.Definitions;
|
||||
using Pf2e.Scrapper.Processors.Factory;
|
||||
|
||||
namespace Pf2e.Scrapper.Processors.Collection;
|
||||
|
||||
/// <summary>
|
||||
/// Класс с коллекцией обработчиков.
|
||||
/// </summary>
|
||||
internal class ProcessorCollection
|
||||
{
|
||||
public IServiceProvider ServiceProvider { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Коллекция всех доступных обработчиков.
|
||||
/// </summary>
|
||||
public ICollection<ProcessorRecord> AllItems { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Коллекция обработчиков, которые будут обрабатываться.
|
||||
/// Для изменения необходимо использовать <see cref="FilterProcessors"/>.
|
||||
/// </summary>
|
||||
public List<ProcessorRecord> Items { get; private set; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Обеспечение доступа к элементам обработчиков.
|
||||
/// </summary>
|
||||
/// <param name="name"></param>
|
||||
/// <returns></returns>
|
||||
public ProcessorRecord this[string name] => AllItems.First(item => item.Name == name);
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор класса <see cref="ProcessorCollection"/>.
|
||||
/// </summary>
|
||||
public ProcessorCollection(IServiceProvider serviceProvider)
|
||||
{
|
||||
ServiceProvider = serviceProvider;
|
||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
||||
|
||||
foreach (Type type in assembly.GetTypes())
|
||||
{
|
||||
ProcessType(type, type.GetCustomAttribute<AbstractProcessorAttribute>());
|
||||
}
|
||||
|
||||
CheckDependencies();
|
||||
FilterProcessors([]);
|
||||
}
|
||||
|
||||
private void ProcessType(Type type, AbstractProcessorAttribute? attribute)
|
||||
{
|
||||
switch (attribute)
|
||||
{
|
||||
case null:
|
||||
break;
|
||||
|
||||
case ProcessorAttribute processorAttribute:
|
||||
ProcessType(type, processorAttribute);
|
||||
break;
|
||||
|
||||
case ProcessorCollectionAttribute processorCollectionAttribute:
|
||||
ProcessType(type, processorCollectionAttribute);
|
||||
break;
|
||||
|
||||
case ProcessorFactoryAttribute processorFactoryAttribute:
|
||||
ProcessType(type, processorFactoryAttribute);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new(
|
||||
$"Неизвестная реализация класса {typeof(AbstractProcessorAttribute)} - {attribute.GetType()}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private void ProcessType(Type type, ProcessorAttribute? attribute)
|
||||
{
|
||||
if (attribute is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!type.IsAssignableTo(typeof(IProcessor)))
|
||||
{
|
||||
throw new(
|
||||
$"Процессор {type}, которому установлен аттрибут {attribute.GetType()}, не представляет интерфейс {typeof(IProcessor)}"
|
||||
);
|
||||
}
|
||||
|
||||
ProcessorDependenciesAttribute? dependenciesAttribute =
|
||||
type.GetCustomAttribute<ProcessorDependenciesAttribute>();
|
||||
|
||||
ProcessorRecord item = new()
|
||||
{
|
||||
Name = attribute.Name,
|
||||
Type = type,
|
||||
Description = attribute.Description,
|
||||
Group = attribute.Group,
|
||||
Dependencies = dependenciesAttribute?.Dependencies ?? [],
|
||||
Active = attribute.Active,
|
||||
Parallel = attribute.Parallel,
|
||||
};
|
||||
|
||||
item.Factory = ActivatorUtilities.CreateInstance<SimpleProcessorFactory>(
|
||||
ServiceProvider,
|
||||
item
|
||||
);
|
||||
|
||||
AllItems.Add(item);
|
||||
}
|
||||
|
||||
private void ProcessType(Type type, ProcessorCollectionAttribute? attribute)
|
||||
{
|
||||
if (attribute is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!type.IsAssignableTo(typeof(AbstractInputProcessor)))
|
||||
{
|
||||
throw new(
|
||||
$"Процессор {type}, которому установлен аттрибут {attribute.GetType()}, не реализует класс {typeof(AbstractInputProcessor)}."
|
||||
);
|
||||
}
|
||||
|
||||
ProcessorDependenciesAttribute? dependenciesAttribute =
|
||||
type.GetCustomAttribute<ProcessorDependenciesAttribute>();
|
||||
|
||||
ProcessorRecord item = new()
|
||||
{
|
||||
Name = attribute.Name,
|
||||
Type = type,
|
||||
Description = attribute.Description,
|
||||
Group = attribute.Group,
|
||||
Dependencies = dependenciesAttribute?.Dependencies ?? [],
|
||||
Active = attribute.Active,
|
||||
Parallel = attribute.Parallel,
|
||||
};
|
||||
|
||||
CompendiumProcessorFactory factory =
|
||||
ActivatorUtilities.CreateInstance<CompendiumProcessorFactory>(ServiceProvider, item);
|
||||
|
||||
factory.CompendiumType = attribute.CompendiumType;
|
||||
factory.InnerType = attribute.InnerType;
|
||||
|
||||
item.Factory = factory;
|
||||
|
||||
AllItems.Add(item);
|
||||
}
|
||||
|
||||
private void ProcessType(Type type, ProcessorFactoryAttribute? attribute)
|
||||
{
|
||||
if (attribute is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!type.IsAssignableTo(typeof(AbstractProcessorFactory)))
|
||||
{
|
||||
throw new(
|
||||
$"Процессор {type}, которому установлен аттрибут {attribute.GetType()}, не реализует класс {typeof(AbstractProcessorFactory)}."
|
||||
);
|
||||
}
|
||||
|
||||
ProcessorDependenciesAttribute? dependenciesAttribute =
|
||||
type.GetCustomAttribute<ProcessorDependenciesAttribute>();
|
||||
|
||||
ProcessorRecord item = new()
|
||||
{
|
||||
Name = attribute.Name,
|
||||
Type = attribute.ProcessorType,
|
||||
Description = attribute.Description,
|
||||
Group = attribute.Group,
|
||||
Dependencies = dependenciesAttribute?.Dependencies ?? [],
|
||||
Active = attribute.Active,
|
||||
Parallel = attribute.Parallel,
|
||||
Factory = (AbstractProcessorFactory)
|
||||
ActivatorUtilities.CreateInstance(ServiceProvider, type),
|
||||
};
|
||||
|
||||
AllItems.Add(item);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверка, что все зависимости всех обработчиков могут быть удовлетворены.
|
||||
/// </summary>
|
||||
private void CheckDependencies()
|
||||
{
|
||||
foreach (ProcessorRecord item in Items)
|
||||
{
|
||||
foreach (string dependency in item.Dependencies)
|
||||
{
|
||||
if (Items.All(i => i.Name != dependency))
|
||||
{
|
||||
throw new($"Dependency '{dependency}' for processor '{item.Name}' not found.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сортировать обработчики в соответсвии со всеми зависимостями.
|
||||
/// </summary>
|
||||
public void SortItems()
|
||||
{
|
||||
Items.ForEach(item => item.Priority = -1);
|
||||
|
||||
Dictionary<string, int> priorities = [];
|
||||
|
||||
while (priorities.Count < Items.Count)
|
||||
{
|
||||
foreach (ProcessorRecord item in Items)
|
||||
{
|
||||
if (item.Priority >= 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
IEnumerable<int> dependencyPriorities = item.Dependencies.Select(d =>
|
||||
priorities.GetValueOrDefault(d, -1)
|
||||
);
|
||||
|
||||
if (dependencyPriorities.Any(p => p == -1))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
item.Priority = Math.Max(
|
||||
dependencyPriorities.Any() ? dependencyPriorities.Max() + 1 : 1,
|
||||
(int)item.Group * 100
|
||||
);
|
||||
priorities.Add(item.Name, item.Priority);
|
||||
}
|
||||
}
|
||||
|
||||
Items.Sort((a, b) => a.Priority - b.Priority);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ограничить список используемых обработчиков по названию.
|
||||
/// Автоматически включает все обработчики, от которых зависят указанные в аргументе обработчики.
|
||||
/// </summary>
|
||||
/// <param name="processorNames">Коллекция названиий требуемых обработчиков.</param>
|
||||
public void FilterProcessors(ICollection<string> processorNames)
|
||||
{
|
||||
if (processorNames.Count == 0)
|
||||
{
|
||||
Items = [.. AllItems];
|
||||
SortItems();
|
||||
return;
|
||||
}
|
||||
|
||||
Dictionary<string, bool> nameFlags = processorNames.ToDictionary(name => name, _ => false);
|
||||
|
||||
while (nameFlags.Values.Any(v => !v))
|
||||
{
|
||||
foreach (
|
||||
KeyValuePair<string, bool> pair in nameFlags.Where(pair => !pair.Value).ToList()
|
||||
)
|
||||
{
|
||||
ProcessorRecord item = AllItems.First(item => item.Name == pair.Key);
|
||||
|
||||
foreach (string dependency in item.Dependencies)
|
||||
{
|
||||
nameFlags.TryAdd(dependency, false);
|
||||
}
|
||||
|
||||
nameFlags[pair.Key] = true;
|
||||
}
|
||||
}
|
||||
|
||||
Items = AllItems.Where(item => nameFlags.ContainsKey(item.Name)).ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получить список типов, которые обрабатываются в компендиуме.
|
||||
/// </summary>
|
||||
/// <param name="compendium">Тип компендиума.</param>
|
||||
/// <returns>Набор типов.</returns>
|
||||
public ICollection<string> GetTypesOfCompendium(ProcessorCompendiumEnum compendium) =>
|
||||
Items
|
||||
.Where(item =>
|
||||
item.Factory.GetType().IsAssignableTo(typeof(CompendiumProcessorFactory))
|
||||
)
|
||||
.Where(item =>
|
||||
(item.Factory as CompendiumProcessorFactory)!.CompendiumType == compendium
|
||||
)
|
||||
.Select(item => (item.Factory as CompendiumProcessorFactory)!.InnerType)
|
||||
.OfType<string>()
|
||||
.ToList();
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Pf2e.Scrapper.Processors.Attributes;
|
||||
using Pf2e.Scrapper.Processors.Factory;
|
||||
|
||||
namespace Pf2e.Scrapper.Processors.Collection;
|
||||
|
||||
/// <summary>
|
||||
/// Элемент коллекции обработчиков.
|
||||
/// </summary>
|
||||
internal class ProcessorRecord
|
||||
{
|
||||
/// <summary>
|
||||
/// Название обработчика.
|
||||
/// </summary>
|
||||
public required string Name { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Тип класса обработчика.
|
||||
/// </summary>
|
||||
public required Type Type { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Описание обработчика.
|
||||
/// </summary>
|
||||
public string Description { get; init; } = "";
|
||||
|
||||
/// <summary>
|
||||
/// Фабрика обработчиков.
|
||||
/// </summary>
|
||||
public AbstractProcessorFactory Factory { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Группа обработчика.
|
||||
/// </summary>
|
||||
public required ProcessorGroupEnum Group { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Коллекция названий обработчиков, после которых должен выполняться обработчик.
|
||||
/// </summary>
|
||||
public ICollection<string> Dependencies { get; init; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Процессор включён по умолчанию.
|
||||
/// </summary>
|
||||
public bool Active { get; init; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// Приоритет выполнения обработчика.
|
||||
/// </summary>
|
||||
public int Priority { get; set; } = -1;
|
||||
|
||||
/// <summary>
|
||||
/// Разрешить выполнять процессов в параллельном режиме
|
||||
/// </summary>
|
||||
public bool Parallel { get; init; } = true;
|
||||
}
|
||||
Reference in New Issue
Block a user