188 lines
6.5 KiB
C#
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();
|
|
}
|
|
}
|