Files
Pf2eDataScrapper/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs
T

188 lines
6.5 KiB
C#

using System.Reflection;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors;
namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder;
/// <summary>
/// Класс отвечает за управление обработчиками, отмеченных атрибутом <see cref="AonprdProcessorAttribute"/>.
/// </summary>
internal class AonprdProcessorCollection
{
/// <summary>
/// Элемент коллекции обработчика.
/// </summary>
/// <param name="attribute">Атрибут, связанный с обаботчиком.</param>
/// <param name="constructor">Конструктор обработчика.</param>
public class ProcessorItem(AonprdProcessorAttribute attribute, ConstructorInfo constructor)
{
/// <summary>
/// Атрибут, связанный с обаботчиком.
/// </summary>
public readonly AonprdProcessorAttribute Attribute = attribute;
/// <summary>
/// Конструктор обработчика.
/// </summary>
public readonly ConstructorInfo Constructor = constructor;
/// <summary>
/// Приоритет выполнения обработчика.
/// </summary>
public int Priority = -1;
}
/// <summary>
/// Список всех объявленных обработчиков.
/// </summary>
private readonly List<ProcessorItem> _allItems;
/// <summary>
/// Список используемых обработчиков.
/// </summary>
public List<ProcessorItem> Items { get; private set; } = [];
/// <summary>
/// Конструктор класса <see cref="AonprdProcessorCollection"/>, загружающий все обработчики.
/// </summary>
public AonprdProcessorCollection()
{
foreach (Type type in Assembly.GetExecutingAssembly().GetTypes())
{
if (type.GetCustomAttributes(typeof(AonprdProcessorAttribute), true).Length <= 0)
{
continue;
}
if (!type.IsSubclassOf(typeof(AbstractProcessor)))
{
throw new(
$"'AonprdProcessor' attribute should only be applied to children of 'AbstractProcessor' class. '{type.FullName}' is not."
);
}
AonprdProcessorAttribute attribute =
type.GetCustomAttribute<AonprdProcessorAttribute>()!;
ConstructorInfo constructor = type.GetConstructor([typeof(AonprdProcessorInput)])!;
ProcessorItem? collision = Items.FirstOrDefault(i =>
i.Attribute.Name == attribute.Name
);
if (collision is not null)
{
throw new(
$"Multiply attributes 'AonprdProcessor' have same name '{attribute.Name}'."
+ $" They are added to classes: '{type.FullName}' and '{collision.Constructor.DeclaringType!.FullName}'"
);
}
Items.Add(new(attribute, constructor));
}
_allItems = Items.ToList();
}
/// <summary>
/// Проверка, что все зависимости всех обработчиков могут быть удовлетворены.
/// </summary>
private void CheckDependencies()
{
foreach (ProcessorItem item in Items)
{
foreach (string dependency in item.Attribute.Dependencies)
{
if (Items.All(i => i.Attribute.Name != dependency))
{
throw new(
$"Dependency '{dependency}' for processor '{item.Attribute.Name}' not found."
);
}
}
}
}
/// <summary>
/// Сортировать обработчики в соответсвии со всеми зависимостями.
/// </summary>
private void SortItems()
{
CheckDependencies();
Dictionary<string, int> priorities = [];
while (priorities.Count < Items.Count)
{
foreach (ProcessorItem item in Items)
{
if (item.Priority > 0)
{
continue;
}
IEnumerable<int> dependencyPriorities = item.Attribute.Dependencies.Select(d =>
priorities.GetValueOrDefault(d, -1)
);
if (dependencyPriorities.Any(p => p == -1))
{
continue;
}
item.Priority = dependencyPriorities.Any() ? dependencyPriorities.Max() + 1 : 1;
priorities.Add(item.Attribute.Name, item.Priority);
}
}
Items.Sort((a, b) => a.Priority - b.Priority);
}
/// <summary>
/// Итерация через все используемые обработчики.
/// </summary>
/// <returns>Итератор.</returns>
public IEnumerable<ProcessorItem> Iterate()
{
SortItems();
foreach (ProcessorItem item in Items)
{
yield return item;
}
}
/// <summary>
/// Ограничить список используемых обработчиков по названию.
/// Автоматически включает все обработчики, от которых зависят указанные в аргументе обработчики.
/// </summary>
/// <param name="processorNames">Коллекция названиий требуемых обработчиков.</param>
public void UseOnlyNames(ICollection<string> processorNames)
{
if (processorNames.Count == 0)
{
Items = _allItems;
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()
)
{
ProcessorItem item = _allItems.First(item => item.Attribute.Name == pair.Key);
foreach (string dependency in item.Attribute.Dependencies)
{
nameFlags.TryAdd(dependency, false);
}
nameFlags[pair.Key] = true;
}
}
Items = _allItems.Where(item => nameFlags.ContainsKey(item.Attribute.Name)).ToList();
}
}