Глобальный рефакторинг пространств имён
This commit is contained in:
@@ -0,0 +1,121 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Pf2e.Scrapper.Helpers;
|
||||
using Pf2e.Scrapper.Importers;
|
||||
using Pf2e.Scrapper.Processors.Definitions;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder;
|
||||
|
||||
internal abstract class AbstractSchemaBuildProcessor : IProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Связаный компендиум
|
||||
/// </summary>
|
||||
protected abstract AbstractCompendium Compendium { get; }
|
||||
|
||||
/// <inheritdoc cref="ProcessProgress"/>
|
||||
protected abstract ProcessProgress Progress { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Определитель типов данных.
|
||||
/// </summary>
|
||||
protected abstract TypeResolver Resolver { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Путь к каталогу для сохранения вывода.
|
||||
/// </summary>
|
||||
protected abstract string SavePath { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Набор схем данных.
|
||||
/// </summary>
|
||||
protected Dictionary<string, SchemaNode> Schemas { get; } = [];
|
||||
|
||||
/// <summary>
|
||||
/// Получить список названий элементов структуры данных на основе вводимого элемента.
|
||||
/// </summary>
|
||||
/// <param name="item">Вводимый элемент</param>
|
||||
/// <returns>Коллекция названий элементов структуры данных/</returns>
|
||||
protected abstract ICollection<string> GetSchemaNodes(AbstractCompendiumItem item);
|
||||
|
||||
/// <summary>
|
||||
/// Построение схемы данных.
|
||||
/// </summary>
|
||||
public void Process()
|
||||
{
|
||||
Progress.Local.Reset(Compendium.Count());
|
||||
|
||||
foreach ((AbstractCompendiumItem reference, JObject json) in Compendium.Iterate())
|
||||
{
|
||||
Progress.Local.Start(reference.ToString());
|
||||
|
||||
foreach (string nodeName in GetSchemaNodes(reference))
|
||||
{
|
||||
if (!Schemas.TryGetValue(nodeName, out SchemaNode? node))
|
||||
{
|
||||
node = new() { Path = nodeName, Resolver = Resolver };
|
||||
Schemas.Add(nodeName, node);
|
||||
}
|
||||
|
||||
node.AddValue(json);
|
||||
}
|
||||
|
||||
Progress.Local.End();
|
||||
}
|
||||
|
||||
Progress.Local.Complete();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сохранение постренных схем данных.
|
||||
/// </summary>
|
||||
public void Save()
|
||||
{
|
||||
string targetDir = SavePath;
|
||||
|
||||
foreach (KeyValuePair<string, SchemaNode> kvp in Schemas)
|
||||
{
|
||||
SaveSchema(targetDir, kvp.Key, kvp.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Сохранение отдельной схемы данных.
|
||||
/// </summary>
|
||||
/// <param name="dir">Целевой каталог.</param>
|
||||
/// <param name="name">Название схемы.</param>
|
||||
/// <param name="schema">Сохраняемая схема.</param>
|
||||
protected void SaveSchema(string dir, string name, SchemaNode schema)
|
||||
{
|
||||
char[] invalids = Path.GetInvalidFileNameChars();
|
||||
string fileName = string.Join(
|
||||
"_",
|
||||
schema.Path.Split(invalids, StringSplitOptions.RemoveEmptyEntries)
|
||||
)
|
||||
.TrimEnd('.');
|
||||
string path = Path.Combine(dir, fileName + ".yaml");
|
||||
|
||||
string? dirPath = Path.GetDirectoryName(path);
|
||||
if (dirPath is not null && !Directory.Exists(dirPath))
|
||||
{
|
||||
Directory.CreateDirectory(dirPath);
|
||||
}
|
||||
|
||||
using StreamWriter writer = new(path);
|
||||
|
||||
schema.Beautify();
|
||||
|
||||
writer.WriteLine(
|
||||
SchemaNode.FormatYaml(
|
||||
new Dictionary<string, string>()
|
||||
{
|
||||
["name"] = name,
|
||||
["values"] = schema.Values.ToString(),
|
||||
},
|
||||
schema.Types[0].GetYamlData()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using Pf2e.Scrapper.SchemaBuilder.Types;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder;
|
||||
|
||||
/// <summary>
|
||||
/// Класс для определения типа поля в схеме данных.
|
||||
/// </summary>
|
||||
internal class TypeResolver
|
||||
{
|
||||
/// <summary>
|
||||
/// Проверка соответствия пути.
|
||||
/// </summary>
|
||||
/// <param name="node">Элемент, путь которого проверяется.</param>
|
||||
/// <param name="path">Проверяемый путь. Допускается использование `*` в местах, где допускается любое значение.</param>
|
||||
/// <returns>`true`, если `node.Path` соответсвует указанному в `path` значению.</returns>
|
||||
public bool CheckPath(SchemaNode node, string path)
|
||||
{
|
||||
if (!path.Contains("*"))
|
||||
{
|
||||
return path == node.Path;
|
||||
}
|
||||
|
||||
string[] pathParts = path.Split('.');
|
||||
string[] nodeParts = node.Path.Split('.');
|
||||
|
||||
return pathParts.Length <= nodeParts.Length
|
||||
&& !pathParts.Where((t, i) => t != "*" && t != nodeParts[i]).Any();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Проверка соответствия пути.
|
||||
/// </summary>
|
||||
/// <param name="node">Элемент, путь которого проверяется.</param>
|
||||
/// <param name="paths">Проверяемые пути. Допускается использование `*` в местах, где допускается любое значение.</param>
|
||||
/// <returns>`true`, если `node.Path` соответсвует хотя бы одному указанному в `paths` значению.</returns>
|
||||
public bool CheckPath(SchemaNode node, ICollection<string> paths)
|
||||
{
|
||||
return paths.Any(path => CheckPath(node, path));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение типа значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Значение.</param>
|
||||
/// <returns>Типа значения.</returns>
|
||||
public JTokenType TokenType(JToken? value)
|
||||
{
|
||||
return
|
||||
value is null
|
||||
|| value.Type is JTokenType.Null or JTokenType.Undefined or JTokenType.None
|
||||
? JTokenType.Null
|
||||
: value.Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Частное определение типа поля.
|
||||
/// </summary>
|
||||
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
||||
/// <param name="value">Добавляемое значение.</param>
|
||||
/// <returns>Новый тип для схемы данных. `null`, что бы использовать значение по умолчанию</returns>
|
||||
protected virtual AbstractSchemaType? CustomResolve(SchemaNode node, JToken? value)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Определение типа поля.
|
||||
/// </summary>
|
||||
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
|
||||
/// <param name="value">Добавляемое значение.</param>
|
||||
/// <param name="disableResolve">Добавляемое значение.</param>
|
||||
/// <returns>Новый тип для схемы данных</returns>
|
||||
/// <exception cref="ArgumentNullException">
|
||||
/// Возникает, если у `value` значение идентично `null`
|
||||
/// (такие значения должны обрабаотываться в `SchemaNode`).
|
||||
/// </exception>
|
||||
public AbstractSchemaType Resolve(SchemaNode node, JToken? value, bool disableResolve = false)
|
||||
{
|
||||
if (!disableResolve)
|
||||
{
|
||||
AbstractSchemaType? customType = this.CustomResolve(node, value);
|
||||
|
||||
if (customType is not null)
|
||||
{
|
||||
return customType;
|
||||
}
|
||||
}
|
||||
|
||||
JTokenType type = TokenType(value);
|
||||
|
||||
return type == JTokenType.Null
|
||||
? throw new ArgumentNullException(nameof(value))
|
||||
: type switch
|
||||
{
|
||||
JTokenType.Array => new ArraySchemaType(node),
|
||||
JTokenType.Object => new ObjectSchemaType(node),
|
||||
_ => new StaticSchemaType(node, type),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Абстрактный тип представоления данных в схеме.
|
||||
/// </summary>
|
||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||
internal abstract class AbstractSchemaType(SchemaNode parent)
|
||||
{
|
||||
/// <summary>
|
||||
/// Элемент схемы данных, к которому относится тип.
|
||||
/// </summary>
|
||||
public readonly SchemaNode Parent = parent;
|
||||
|
||||
/// <summary>
|
||||
/// Корневой элемент схемы данных.
|
||||
/// </summary>
|
||||
public SchemaNode Root => Parent.RootNode;
|
||||
|
||||
/// <summary>
|
||||
/// Объект для определения типа поля в схеме данных.
|
||||
/// </summary>
|
||||
public TypeResolver Resolver => Root.Resolver;
|
||||
|
||||
/// <summary>
|
||||
/// Проверка, что значение совместимо с текущим типом.
|
||||
/// </summary>
|
||||
/// <param name="value">Проверяемое значение.</param>
|
||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||
public abstract bool IsCompatibleValue(JToken value);
|
||||
|
||||
/// <summary>
|
||||
/// Добавление нового значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Новое значение.</param>
|
||||
public abstract void AddValue(JToken value);
|
||||
|
||||
/// <summary>
|
||||
/// Подготовка типа к красивому представлению.
|
||||
/// </summary>
|
||||
public abstract void Beautify();
|
||||
|
||||
/// <summary>
|
||||
/// Получение строкового представоения типа элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представоение типа элемента</returns>
|
||||
public abstract string GetTypeName();
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных для форматирования в YAML.
|
||||
/// </summary>
|
||||
/// <returns>Словарь данных</returns>
|
||||
public abstract Dictionary<string, string> GetYamlData();
|
||||
|
||||
/// <summary>
|
||||
/// Полное строковое представление элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представление элемента.</returns>
|
||||
public override string ToString()
|
||||
{
|
||||
return SchemaNode.FormatYaml(GetYamlData());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Тип представоления массива в схеме.
|
||||
/// </summary>
|
||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||
internal class ArraySchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
||||
{
|
||||
/// <summary>
|
||||
/// Минимальное количество элементов в массиве.
|
||||
/// </summary>
|
||||
public int MinCount = int.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Максимальное количество элементов в массиве.
|
||||
/// </summary>
|
||||
public int MaxCount = int.MinValue;
|
||||
|
||||
/// <summary>
|
||||
/// Схема элемента массива.
|
||||
/// </summary>
|
||||
protected readonly SchemaNode ElementSchema = new(parent, "[]");
|
||||
|
||||
/// <summary>
|
||||
/// Проверка, что значение совместимо с текущим типом.
|
||||
/// </summary>
|
||||
/// <param name="value">Проверяемое значение.</param>
|
||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||
public override bool IsCompatibleValue(JToken value)
|
||||
{
|
||||
return Resolver.TokenType(value) == JTokenType.Array;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавление нового значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Новое значение.</param>
|
||||
public override void AddValue(JToken value)
|
||||
{
|
||||
if (value.Type != JTokenType.Array)
|
||||
{
|
||||
throw new("Value is not an array, did you forget to call IsCompatibleValue?");
|
||||
}
|
||||
|
||||
JArray array = (JArray)value;
|
||||
|
||||
MinCount = Math.Min(MinCount, array.Count);
|
||||
MaxCount = Math.Max(MaxCount, array.Count);
|
||||
|
||||
foreach (JToken item in array)
|
||||
{
|
||||
ElementSchema.AddValue(item);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Подготовка типа к красивому представлению.
|
||||
/// </summary>
|
||||
public override void Beautify()
|
||||
{
|
||||
ElementSchema.Beautify();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение строкового представоения типа элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представоение типа элемента</returns>
|
||||
public override string GetTypeName()
|
||||
{
|
||||
return $"array<{ElementSchema.GetTypeName()}>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных для форматирования в YAML.
|
||||
/// </summary>
|
||||
/// <returns>Словарь данных</returns>
|
||||
public override Dictionary<string, string> GetYamlData()
|
||||
{
|
||||
return new()
|
||||
{
|
||||
["min_count"] = MinCount.ToString(),
|
||||
["max_count"] = MaxCount.ToString(),
|
||||
["value_type"] = ElementSchema.ToString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Тип представоления словаря в схеме.
|
||||
/// </summary>
|
||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||
internal class DictionarySchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
||||
{
|
||||
/// <summary>
|
||||
/// Схема ключей словаря.
|
||||
/// </summary>
|
||||
public readonly SchemaNode KeyNode = new(parent, "[key]");
|
||||
|
||||
/// <summary>
|
||||
/// Схема значений словаря.
|
||||
/// </summary>
|
||||
public readonly SchemaNode ItemNode = new(parent, "[value]");
|
||||
|
||||
/// <summary>
|
||||
/// Проверка, что значение совместимо с текущим типом.
|
||||
/// </summary>
|
||||
/// <param name="value">Проверяемое значение.</param>
|
||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||
public override bool IsCompatibleValue(JToken value)
|
||||
{
|
||||
return Resolver.TokenType(value) == JTokenType.Object;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавление нового значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Новое значение.</param>
|
||||
public override void AddValue(JToken value)
|
||||
{
|
||||
JObject obj = (JObject)value;
|
||||
|
||||
foreach (KeyValuePair<string, JToken?> pair in obj)
|
||||
{
|
||||
KeyNode.AddValue(pair.Key);
|
||||
ItemNode.AddValue(pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Подготовка типа к красивому представлению.
|
||||
/// </summary>
|
||||
public override void Beautify()
|
||||
{
|
||||
KeyNode.Beautify();
|
||||
ItemNode.Beautify();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение строкового представоения типа элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представоение типа элемента</returns>
|
||||
public override string GetTypeName()
|
||||
{
|
||||
return $"dict<{KeyNode.GetTypeName()}, {ItemNode.GetTypeName()}>";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных для форматирования в YAML.
|
||||
/// </summary>
|
||||
/// <returns>Словарь данных</returns>
|
||||
public override Dictionary<string, string> GetYamlData()
|
||||
{
|
||||
return new Dictionary<string, string>()
|
||||
{
|
||||
["_key"] = KeyNode.ToString(),
|
||||
["_value"] = ItemNode.ToString(),
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using System.Collections.Generic;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Тип, указывающий, что данные игнорируются.
|
||||
/// </summary>
|
||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||
internal class IgnoreSchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
||||
{
|
||||
/// <summary>
|
||||
/// Проверка, что значение совместимо с текущим типом.
|
||||
/// </summary>
|
||||
/// <param name="value">Проверяемое значение.</param>
|
||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||
public override bool IsCompatibleValue(JToken value)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавление нового значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Новое значение.</param>
|
||||
public override void AddValue(JToken value) { }
|
||||
|
||||
/// <summary>
|
||||
/// Подготовка типа к красивому представлению.
|
||||
/// </summary>
|
||||
public override void Beautify() { }
|
||||
|
||||
/// <summary>
|
||||
/// Получение строкового представоения типа элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представоение типа элемента</returns>
|
||||
public override string GetTypeName()
|
||||
{
|
||||
return "ignore";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных для форматирования в YAML.
|
||||
/// </summary>
|
||||
/// <returns>Словарь данных</returns>
|
||||
public override Dictionary<string, string> GetYamlData()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Тип представоления объектов в схеме.
|
||||
/// </summary>
|
||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||
internal class ObjectSchemaType(SchemaNode parent) : AbstractSchemaType(parent)
|
||||
{
|
||||
/// <summary>
|
||||
/// Элемнты полей объекта.
|
||||
/// </summary>
|
||||
public readonly List<SchemaNode> Fields = [];
|
||||
|
||||
/// <summary>
|
||||
/// Проверка, что значение совместимо с текущим типом.
|
||||
/// </summary>
|
||||
/// <param name="value">Проверяемое значение.</param>
|
||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||
public override bool IsCompatibleValue(JToken value)
|
||||
{
|
||||
return Resolver.TokenType(value) == JTokenType.Object;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавление нового значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Новое значение.</param>
|
||||
public override void AddValue(JToken value)
|
||||
{
|
||||
JObject obj = (JObject)value;
|
||||
|
||||
foreach (SchemaNode field in Fields.Where(field => !obj.ContainsKey(field.Name)))
|
||||
{
|
||||
field.IsNullable = true;
|
||||
}
|
||||
|
||||
foreach (KeyValuePair<string, JToken?> pair in obj)
|
||||
{
|
||||
SchemaNode? field = Fields.FirstOrDefault(f => f.Name == pair.Key);
|
||||
|
||||
if (field is null)
|
||||
{
|
||||
field = new(Parent, pair.Key);
|
||||
Fields.Add(field);
|
||||
}
|
||||
|
||||
field.AddValue(pair.Value);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Подготовка типа к красивому представлению.
|
||||
/// </summary>
|
||||
public override void Beautify()
|
||||
{
|
||||
Fields.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
||||
|
||||
foreach (SchemaNode field in Fields)
|
||||
{
|
||||
field.Beautify();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение строкового представоения типа элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представоение типа элемента</returns>
|
||||
public override string GetTypeName()
|
||||
{
|
||||
return "object";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных для форматирования структуры в YAML.
|
||||
/// </summary>
|
||||
/// <returns>Словарь данных</returns>
|
||||
public Dictionary<string, string> GetSchemaYaml()
|
||||
{
|
||||
Dictionary<string, string> result = new();
|
||||
|
||||
foreach (SchemaNode field in Fields)
|
||||
{
|
||||
string s = field.Name;
|
||||
s = field.IsNullable ? $"[{s}]" : $"'{s}'";
|
||||
result.Add(s, field.ToString());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных для форматирования в YAML.
|
||||
/// </summary>
|
||||
/// <returns>Словарь данных</returns>
|
||||
public override Dictionary<string, string> GetYamlData()
|
||||
{
|
||||
return new() { ["schema"] = SchemaNode.FormatYaml(GetSchemaYaml()) };
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Pf2e.Scrapper.SchemaBuilder.Types;
|
||||
|
||||
/// <summary>
|
||||
/// Тип представоления простых значений в схеме.
|
||||
/// </summary>
|
||||
/// <param name="parent">Элемент, к которому добавляется тип.</param>
|
||||
/// <param name="type">Тип значения.</param>
|
||||
internal class StaticSchemaType(SchemaNode parent, JTokenType type) : AbstractSchemaType(parent)
|
||||
{
|
||||
/// <summary>
|
||||
/// Тип значения.
|
||||
/// </summary>
|
||||
protected readonly JTokenType Type = type;
|
||||
|
||||
/// <summary>
|
||||
/// Набор возможных значений и их количества.
|
||||
/// </summary>
|
||||
protected Dictionary<JToken, int> Values = [];
|
||||
|
||||
/// <summary>
|
||||
/// Проверка, что значение совместимо с текущим типом.
|
||||
/// </summary>
|
||||
/// <param name="value">Проверяемое значение.</param>
|
||||
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
|
||||
public override bool IsCompatibleValue(JToken value)
|
||||
{
|
||||
return value.Type == Type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Добавление нового значения.
|
||||
/// </summary>
|
||||
/// <param name="value">Новое значение.</param>
|
||||
public override void AddValue(JToken value)
|
||||
{
|
||||
Values.TryAdd(value, 0);
|
||||
|
||||
Values[value]++;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Подготовка типа к красивому представлению.
|
||||
/// </summary>
|
||||
public override void Beautify()
|
||||
{
|
||||
if (Type == JTokenType.String)
|
||||
{
|
||||
Dictionary<JToken, int> newValues = new();
|
||||
|
||||
foreach (KeyValuePair<JToken, int> pair in Values)
|
||||
{
|
||||
string newValue = "\"" + pair.Key.ToString().Trim().Replace("\"", "\\\"") + "\"";
|
||||
newValues.TryAdd(newValue, 0);
|
||||
newValues[newValue] += pair.Value;
|
||||
}
|
||||
|
||||
Values = newValues;
|
||||
}
|
||||
|
||||
Values = Values
|
||||
.OrderByDescending(pair => pair.Value)
|
||||
.ToDictionary(pair => pair.Key, pair => pair.Value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение строкового представоения типа элемента.
|
||||
/// </summary>
|
||||
/// <returns>Строковое представоение типа элемента</returns>
|
||||
public override string GetTypeName()
|
||||
{
|
||||
return Type.ToString().ToLower();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Получение данных для форматирования в YAML.
|
||||
/// </summary>
|
||||
/// <returns>Словарь данных</returns>
|
||||
public override Dictionary<string, string> GetYamlData()
|
||||
{
|
||||
List<string> values = [];
|
||||
int count = Values.Count;
|
||||
|
||||
string prefix = (count == 1) ? "" : "- ";
|
||||
|
||||
if (
|
||||
count is <= SchemaNode.ElementLimit and > 0
|
||||
&& (Type != JTokenType.String || !Values.Any(s => s.ToString().Contains('\n')))
|
||||
)
|
||||
{
|
||||
foreach (KeyValuePair<JToken, int> kvp in Values)
|
||||
{
|
||||
values.Add($"{prefix}{kvp.Key} (x{kvp.Value})");
|
||||
}
|
||||
}
|
||||
|
||||
return new()
|
||||
{
|
||||
["different_values"] =
|
||||
count.ToString()
|
||||
+ ((count == Root.Values) ? " (all different)" : "")
|
||||
+ ((count == 1) ? " (all same)" : ""),
|
||||
["variants"] = (values.Count > 0) ? string.Join("\r\n", values) : "[omitted]",
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user