Глобальный рефакторинг пространств имён
This commit is contained in:
@@ -0,0 +1,240 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Pf2e.Scrapper.SchemaBuilder.Types;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Элемент схемы данных.
|
||||
/// </summary>
|
||||
/// <example>
|
||||
/// Пример использования:
|
||||
/// <code>
|
||||
/// SchemaNode schema = new (); // Создание схемы данных
|
||||
/// data = JObject.Parse(reader.ReadToEnd()); // Загрузка данных
|
||||
/// schema.AddValue(data); // Добавление данных
|
||||
/// </code>
|
||||
/// </example>
|
||||
internal class SchemaNode
|
||||
{
|
||||
/// <summary>
|
||||
/// Отображаемое имя элемента.
|
||||
/// </summary>
|
||||
public readonly string Name = "";
|
||||
|
||||
/// <summary>
|
||||
/// Путь к элементу.
|
||||
/// </summary>
|
||||
public string Path = "";
|
||||
|
||||
/// <summary>
|
||||
/// Родительский элемент.
|
||||
/// </summary>
|
||||
public readonly SchemaNode? Parent;
|
||||
|
||||
/// <summary>
|
||||
/// Указание, является ли элемент корневым.
|
||||
/// </summary>
|
||||
public readonly bool Root;
|
||||
|
||||
/// <summary>
|
||||
/// Корневой элемент схемы данных.
|
||||
/// </summary>
|
||||
public SchemaNode RootNode => Root ? this : Parent!.RootNode;
|
||||
|
||||
/// <summary>
|
||||
/// Типы данных, использующиеся в элементе.
|
||||
/// </summary>
|
||||
public readonly List<AbstractSchemaType> Types = [];
|
||||
|
||||
/// <summary>
|
||||
/// Указание, может ли этот элемент отсутсвовать в структуре данных.
|
||||
/// </summary>
|
||||
public bool IsNullable;
|
||||
|
||||
/// <summary>
|
||||
/// Объект для определения типа поля в схеме данных.
|
||||
/// </summary>
|
||||
public TypeResolver Resolver = new();
|
||||
|
||||
/// <summary>
|
||||
/// Количество значений, добавленных в тип.
|
||||
/// </summary>
|
||||
public int Values { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Указание, является ли тип элемента простым.
|
||||
/// </summary>
|
||||
public bool IsSimple => Types.Count == 1;
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор корневого элемента.
|
||||
/// </summary>
|
||||
public SchemaNode()
|
||||
{
|
||||
Root = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Конструктор элемента схемы данных.
|
||||
/// </summary>
|
||||
/// <param name="parent">Родительский элемент.</param>
|
||||
/// <param name="name">Имя элемента.</param>
|
||||
public SchemaNode(SchemaNode parent, string name)
|
||||
{
|
||||
Parent = parent;
|
||||
Name = name;
|
||||
Path = parent.Path + "." + name;
|
||||
Resolver = parent.Resolver;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавление нового возможного значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Добавляемое значение</param>
|
||||
public void AddValue(JToken? value)
|
||||
{
|
||||
if (value is null || Resolver.TokenType(value) == JTokenType.Null)
|
||||
{
|
||||
IsNullable = true;
|
||||
return;
|
||||
}
|
||||
|
||||
Values++;
|
||||
|
||||
foreach (AbstractSchemaType type in Types.Where(type => type.IsCompatibleValue(value)))
|
||||
{
|
||||
type.AddValue(value);
|
||||
return;
|
||||
}
|
||||
|
||||
AbstractSchemaType newType = Resolver.Resolve(this, value);
|
||||
|
||||
if (newType.IsCompatibleValue(value))
|
||||
{
|
||||
Types.Add(newType);
|
||||
newType.AddValue(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
AbstractSchemaType fallbackType = Resolver.Resolve(this, value, true);
|
||||
Types.Add(fallbackType);
|
||||
fallbackType.AddValue(value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение строкового представоения типа элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представоение типа элемента</returns>
|
||||
public string GetTypeName()
|
||||
{
|
||||
if (IsSimple)
|
||||
{
|
||||
return Types[0].GetTypeName();
|
||||
}
|
||||
|
||||
List<string> typeNames = [.. Types.Select(t => t.GetTypeName())];
|
||||
|
||||
return string.Join(" | ", typeNames);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Полное строковое представление элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представление элемента.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
Dictionary<string, string> result = new()
|
||||
{
|
||||
["type"] = GetTypeName(),
|
||||
["path"] = Path,
|
||||
["is_null"] = IsNullable ? "true" : "false",
|
||||
["values"] = Values.ToString(),
|
||||
};
|
||||
|
||||
if (IsSimple)
|
||||
{
|
||||
return FormatYaml(result, Types[0].GetYamlData());
|
||||
}
|
||||
|
||||
if (Types.Count == 0)
|
||||
{
|
||||
return FormatYaml(result, new() { ["is_empty"] = "true" });
|
||||
}
|
||||
|
||||
List<string> types = [];
|
||||
|
||||
foreach (AbstractSchemaType type in Types)
|
||||
{
|
||||
types.Add(Indent(type.ToString()));
|
||||
}
|
||||
|
||||
return FormatYaml(result, new() { ["types"] = "- " + string.Join("\r\n- ", types) });
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Подготовка элемента к красивому представлению.
|
||||
/// </summary>
|
||||
public void Beautify()
|
||||
{
|
||||
foreach (AbstractSchemaType type in Types)
|
||||
{
|
||||
type.Beautify();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Отступ для строкового представления.
|
||||
/// </summary>
|
||||
public const string Tab = " ";
|
||||
|
||||
/// <summary>
|
||||
/// Предел количество отображаемых элементов.
|
||||
/// </summary>
|
||||
public const int ElementLimit = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Увеличение отступа строки.
|
||||
/// </summary>
|
||||
/// <param name="text">Изменяемая строка.</param>
|
||||
/// <returns>Строка с добавленными отступами.</returns>
|
||||
public static string Indent(string text)
|
||||
{
|
||||
return text.Replace("\r\n", "\r\n" + Tab);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Форматирование данных в YAML.
|
||||
/// </summary>
|
||||
/// <param name="input">Словарь данных.</param>
|
||||
/// <returns>Строка форматированных данных</returns>
|
||||
public static string FormatYaml(params List<Dictionary<string, string>> input)
|
||||
{
|
||||
string result = "";
|
||||
|
||||
foreach (KeyValuePair<string, string> kvp in MergeDictionaries(input))
|
||||
{
|
||||
string val = kvp.Value.Contains("\r\n") ? $"\r\n{Tab}{Indent(kvp.Value)}" : kvp.Value;
|
||||
|
||||
result += $"{kvp.Key}: {val}\r\n";
|
||||
}
|
||||
|
||||
return result.Trim();
|
||||
}
|
||||
|
||||
public static Dictionary<string, string> MergeDictionaries(
|
||||
params List<Dictionary<string, string>> input
|
||||
)
|
||||
{
|
||||
Dictionary<string, string> result = [];
|
||||
|
||||
foreach (KeyValuePair<string, string> kvp in input.SelectMany(item => item))
|
||||
{
|
||||
result[kvp.Key] = kvp.Value;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user