Files
Pf2eDataScrapper/Pf2eDataScrapper/Processors/ProcessorExecutor.cs
T

163 lines
5.3 KiB
C#

using System.ComponentModel;
using Microsoft.Extensions.DependencyInjection;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.App;
using Pf2eDataScrapper.Helpers;
using Pf2eDataScrapper.Importers;
using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium;
using Pf2eDataScrapper.Importers.Implementations.FoundryEn.Compendium;
namespace Pf2eDataScrapper.Processors;
internal sealed class ProcessorExecutor(
IServiceProvider serviceProvider,
ProcessorCollection collection,
ProcessProgress progress,
AonprdCompendium aonprdCompendium,
FoundryEnCompendium foundryEnCompendium,
ApplicationSettings settings
)
{
public IServiceProvider ServiceProvider { get; } = serviceProvider;
public ProcessorCollection Collection { get; } = collection;
public ProcessProgress Progress { get; } = progress;
public AonprdCompendium AonprdCompendium { get; } = aonprdCompendium;
public FoundryEnCompendium FoundryEnCompendium { get; } = foundryEnCompendium;
public ApplicationSettings Settings { get; } = settings;
public void Start()
{
Collection.SortItems();
Progress.Processors.Reset(Collection.Items.Count);
StartItem();
}
private int _currentIndex;
private void StartItem()
{
ProcessorCollectionItem item = Collection.Items[_currentIndex];
Progress.Processors.Start(item.Description);
Progress.Progress(Progress.Processors.ToString());
BackgroundWorker worker = new();
worker.DoWork += (_, _) =>
{
Parallel.ForEach(
IterateProcessors(item),
new() { MaxDegreeOfParallelism = item.Parallel ? Settings.MaxThreads : 1 },
RunProcessor
);
};
worker.RunWorkerCompleted += (_, _) => Continue();
worker.RunWorkerAsync();
}
private void RunProcessor(IProcessor processor, ParallelLoopState state, long arg3)
{
try
{
processor.Process();
processor.Save();
}
catch (SkipItemException exception)
{
Progress.Information(exception.Message);
}
catch (SilentSkipItemException)
{
//
}
}
private void Continue()
{
Progress.Processors.End();
_currentIndex++;
if (_currentIndex < Collection.Items.Count)
{
StartItem();
}
else
{
Progress.Processors.Complete();
}
}
private IEnumerable<IProcessor> IterateProcessors(ProcessorCollectionItem item)
{
if (!item.IsCollection)
{
Progress.Local.Reset(1);
Progress.Local.Start(item.Description);
yield return (IProcessor)ActivatorUtilities.CreateInstance(ServiceProvider, item.Type);
Progress.Local.Complete();
yield break;
}
if (item.Group == ProcessorGroupEnum.General)
{
throw new(
$"Обработчик {item.Description} помечен как общий (поле Group == General) и коллекционный (поле InnerType), что запрещено."
);
}
AbstractCompendium compendium = item.Compendium switch
{
ProcessorCompendiumEnum.Aonprd => AonprdCompendium,
ProcessorCompendiumEnum.FoundryEn => FoundryEnCompendium,
_ => throw new($"Не могу выбрать импортёр для группы {item.Group}"),
};
ICollection<string> types = item.InnerType is null
? Collection.GetTypesOfCompendium(item.Compendium ?? throw new("impossible"))
: [item.InnerType];
Progress.Local.Reset(compendium.Count(types));
foreach ((AbstractCompendiumItem compendiumItem, JObject json) in compendium.Iterate(types))
{
Progress.Local.Start(compendiumItem.Id);
ProcessorInput processorInput = ActivatorUtilities.CreateInstance<ProcessorInput>(
ServiceProvider,
compendiumItem.Id,
json
);
yield return (IProcessor)
ActivatorUtilities.CreateInstance(ServiceProvider, item.Type, processorInput);
Progress.Local.End();
}
Progress.Local.Complete();
}
}
/// <summary>
/// Исключение, показывающее, что обработка сущности пропускается как устаревшая.
/// </summary>
/// <param name="name">Название сущности.</param>
public class LegacyItemException(string name) : SkipItemException(name, "Устаревший элемент") { }
/// <summary>
/// Исключение, показывающее, что обработка сущности пропускается.
/// </summary>
/// <param name="name">Название сущности.</param>
/// <param name="reason">Причина пропуска.</param>
public class SkipItemException(string name, string reason)
: Exception($"Пропускаю элемент '{name}', указана причина: {reason}.") { }
/// <summary>
/// Исключение, показывающее, что обработка сущности пропускается без вывода сообщения.
/// </summary>
public class SilentSkipItemException : Exception { }