Files
Pf2eDataScrapper/Pf2e/Scrapper/Importers/Implementations/Aonprd/Helpers/RuleToc.cs
T

342 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Pf2e.Database.Model.Base.Source;
namespace Pf2e.Scrapper.Importers.Implementations.Aonprd.Helpers;
/// <summary>
/// Класс коллекции книг, содержащих правила.
/// Реализует шаблон <c>Singleton</c>.
/// </summary>
public class RuleTocCollection
{
/// <summary>
/// Список книг, содержащих правила.
/// </summary>
public List<RuleTocBook> Books { get; } = [];
/// <summary>
/// Поиск книги по числовому идентификатору.
/// </summary>
public RuleTocBook this[int id] =>
Books.FirstOrDefault(i => i.SourceId == id) ?? throw new($"Book with id {id} not found");
/// <summary>
/// Поиск книги по CLR-представлению источника.
/// </summary>
public RuleTocBook this[Source source]
{
get
{
RuleTocBook? result = Books.FirstOrDefault(i => i.SourceId == source.Id);
if (result is not null)
{
return result;
}
result = new(source);
Books.Add(result);
return result;
}
}
/// <summary>
/// Поиск книги по имени.
/// </summary>
public RuleTocBook? FindName(string name)
{
foreach (RuleTocBook book in Books)
{
if (book.IsNameExists(name))
{
return book;
}
}
return null;
}
/// <summary>
/// Поиск правила по идентификатору.
/// </summary>
public RuleTocItem? FindEntityId(int id)
{
foreach (RuleTocBook book in Books)
{
RuleTocItem? result = book.GetByEntityId(id);
if (result is not null)
{
return result;
}
}
return null;
}
/// <summary>
/// Возвращает строковое представление коллекции в файле YAML.
/// </summary>
public override string ToString()
{
return string.Join("\n", Books.Select(book => book.ToString()).ToList());
}
/// <summary>
/// Сохраняет коллекцию в файле YAML.
/// </summary>
public void SaveFile()
{
string fileName =
$"rules-toc.{new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds()}.debug.yaml";
using FileStream fileStream = File.Create(fileName);
using StreamWriter writer = new(fileStream);
writer.Write(ToString());
}
/// <summary>
/// Экземпляр класса <see cref="RuleTocCollection"/>.
/// </summary>
public static RuleTocCollection Instance { get; } = new();
}
/// <summary>
/// Класс представляет собой книгу, содержащую правила.
/// </summary>
internal class RuleTocBook(Source source)
{
/// <summary>
/// Идентификатор источника.
/// </summary>
public int SourceId { get; } = source.Id;
/// <summary>
/// Названия источника.
/// </summary>
public string SourceName { get; } = source.Name;
/// <summary>
/// Список дочерних правил.
/// </summary>
/// <summary>
/// Список дочерних элементов.
/// </summary>
public List<RuleTocItem> Children { get; } = [];
/// <summary>
/// Находит элемент по хлебным крошкам.
/// </summary>
/// <param name="breadcrumbs">Массив хлебных крошек, указывающих на путь в дереве.</param>
/// <returns><see cref="RuleTocItem"/> или <c>null</c> если элемент не найден.</returns>
public RuleTocItem Get(string[] breadcrumbs)
{
RuleTocItem? node = null;
while (true)
{
List<RuleTocItem> children = node?.Children ?? Children;
if (breadcrumbs.Length == 0)
{
return node!;
}
string root = breadcrumbs[0];
RuleTocItem? item = children.FirstOrDefault(i => i.Name == root);
if (item is null)
{
item = new(root, node);
children.Add(item);
}
breadcrumbs = breadcrumbs[1..];
node = item;
}
}
/// <summary>
/// Проверяет, существует ли имя в текущей книге или дочерних элементах.
/// </summary>
/// <param name="name">Имя для проверки.</param>
/// <param name="node">Начальный элемент для поиска.</param>
/// <returns>true если имя.</returns>
public bool IsNameExists(string name, RuleTocItem? node = null)
{
foreach (RuleTocItem item in node?.Children ?? Children)
{
if (item.Name == name || IsNameExists(name, item))
{
return true;
}
}
return false;
}
/// <summary>
/// Находит элемент по идентификатору сущности.
/// </summary>
/// <param name="id">Идентификатор сущности.</param>
/// <param name="node">Начальный узел для поиска.</param>
/// <returns><see cref="RuleTocItem"/> или <c>null</c> если элемент не найден.</returns>
public RuleTocItem? GetByEntityId(int id, RuleTocItem? node = null)
{
if (node?.RuleId == id)
{
return node;
}
foreach (RuleTocItem item in node?.Children ?? Children)
{
RuleTocItem? result = GetByEntityId(id, item);
if (result is not null)
{
return result;
}
}
return null;
}
/// <summary>
/// Возвращает строковое представление книги в файле YAML.
/// </summary>
/// <returns>Строковое представление книги.</returns>
public override string ToString()
{
return $"\"{SourceName}\":\n"
+ string.Join(
"\n",
Children.Select(child => RuleTocItem.Indent(child.ToString())).ToList()
);
}
}
/// <summary>
/// Класс представляет собой элемент дерева правил.
/// </summary>
internal class RuleTocItem(string name, RuleTocItem? parent)
{
public string? Id { get; set; } = null;
/// <summary>
/// Имя элемента.
/// </summary>
public string Name { get; } = name;
/// <summary>
/// Идентификатор правила.
/// </summary>
public int? RuleId { get; set; } = null;
/// <summary>
/// Указывает, что элемент обрабатывается.
/// </summary>
public bool IsHandled { get; set; } = false;
/// <summary>
/// Родительский элемент дерева.
/// </summary>
public RuleTocItem? Parent { get; } = parent;
/// <summary>
/// Порядок дочерних элементов.
/// </summary>
public List<string>? ChildrenOrder { get; set; } = null;
/// <summary>
/// Коллекция дочерних элементов.
/// </summary>
public List<RuleTocItem> Children { get; } = [];
/// <summary>
/// Предыдущий идентификатор.
/// </summary>
public string? PreviousId { get; set; }
/// <summary>
/// Следующий идентификатор.
/// </summary>
public string? NextId { get; set; }
/// <summary>
/// Идентификатор первого дочернего элемента.
/// </summary>
public string? FirstChildId { get; set; }
/// <summary>
/// Идентификатор последнего дочернего элемента.
/// </summary>
public string? LastChildId { get; set; }
/// <summary>
/// Возвращает строковое представление элемента содержания в файле YAML.
/// </summary>
/// <returns>Строковое представление элемента содержания.</returns>
public override string ToString()
{
List<string> rows =
[
$"{Name}:",
$"{Tab}aonprd: {Id ?? "null"}",
$"{Tab}entity: {RuleId?.ToString() ?? "null"}",
];
if (Children.Count == 0)
{
// rows.Add($"{TAB}children: None");
return string.Join("\n", rows);
}
rows.Add($"{Tab}children:");
foreach (RuleTocItem child in Children)
{
rows.Add(child.Indent(2));
}
return string.Join("\n", rows);
}
/// <summary>
/// Увеличение отступа строкового представления элемента содержания в файле YAML.
/// </summary>
/// <param name="amount">Количество отступов.</param>
/// <returns>Строковое представление элемента содержания с отступом.</returns>
/// <seealso cref="Tab"/>
public string Indent(int amount = 1)
{
string result = ToString();
for (int i = 0; i < amount; i++)
{
result = Indent(result);
}
return result;
}
/// <summary>
/// Увеличение отступа строки.
/// </summary>
/// <param name="str">Входная строка.</param>
/// <returns>Строка с отступом.</returns>
/// <seealso cref="Tab"/>
public static string Indent(string str)
{
return Tab + str.Replace("\n", $"\n{Tab}");
}
/// <summary>
/// Строка отступа строки.
/// </summary>
/// <seealso cref="Indent(int)"/>
/// <seealso cref="Indent(string)"/>
public const string Tab = " ";
}