Compare commits

...
2 Commits
18 changed files with 468 additions and 144 deletions
+20 -7
View File
@@ -1,26 +1,39 @@
using System; using dotenv.net;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using dotenv.net;
using dotenv.net.Utilities; using dotenv.net.Utilities;
namespace Pf2eDataScrapper namespace Pf2eDataScrapper
{ {
internal class EnvSettings /// <summary>
/// Класс настроек.
/// </summary>
internal static class EnvSettings
{ {
/// <summary>
/// Загрузка файла настроек `.env`.
/// </summary>
public static void Load() public static void Load()
{ {
DotEnv.Load(); DotEnv.Load();
} }
/// <summary>
/// Путь к папке с входными данными.
/// </summary>
public static string DataDirPath => EnvReader.GetStringValue("DATA_DIR"); public static string DataDirPath => EnvReader.GetStringValue("DATA_DIR");
/// <summary>
/// Папка в входными данными.
/// </summary>
public static DirectoryInfo DataDir => new(DataDirPath); public static DirectoryInfo DataDir => new(DataDirPath);
/// <summary>
/// Путь к папке для сохранения результатов.
/// </summary>
public static string OutputDirPath => EnvReader.GetStringValue("OUTPUT_DIR"); public static string OutputDirPath => EnvReader.GetStringValue("OUTPUT_DIR");
/// <summary>
/// Папка для сохранения результатов.
/// </summary>
public static string OutputDir => new(OutputDirPath); public static string OutputDir => new(OutputDirPath);
} }
+11 -7
View File
@@ -1,11 +1,8 @@
using System; namespace Pf2eDataScrapper.Importers
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers
{ {
/// <summary>
/// Абстрактный класс импортёра
/// </summary>
internal abstract class AbstractImporter internal abstract class AbstractImporter
{ {
/// <summary> /// <summary>
@@ -28,7 +25,14 @@ namespace Pf2eDataScrapper.Importers
/// </summary> /// </summary>
public string Dir => Path.Combine(EnvSettings.DataDirPath, Slug); public string Dir => Path.Combine(EnvSettings.DataDirPath, Slug);
/// <summary>
/// Метод подготовки данных для импорта
/// </summary>
public abstract void Prepare(); public abstract void Prepare();
/// <summary>
/// Импорт данных
/// </summary>
public abstract void Import(); public abstract void Import();
} }
+21 -11
View File
@@ -1,20 +1,30 @@
using System; using System.Diagnostics;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers namespace Pf2eDataScrapper.Importers
{ {
internal class GitLoader(string DataDirPath) /// <summary>
/// Загрузчик данных из git-репозиториев
/// </summary>
/// <param name="repositoryDirPath"></param>
internal class GitLoader(string repositoryDirPath)
{ {
public readonly string DataDirPath = DataDirPath; /// <summary>
public DirectoryInfo DataDir = new(DataDirPath); /// Путь к директории репозитория
/// </summary>
public readonly string RepositoryDirPath = repositoryDirPath;
/// <summary>
/// Директория репозитория
/// </summary>
public DirectoryInfo RepositoryDir = new(repositoryDirPath);
/// <summary>
/// Обновление импортёра
/// </summary>
/// <param name="importer"></param>
public void UpdateImporter(AbstractImporter importer) public void UpdateImporter(AbstractImporter importer)
{ {
string gitPath = Path.Combine(DataDirPath, importer.Slug); var gitPath = Path.Combine(RepositoryDirPath, importer.Slug);
if (Directory.Exists(gitPath)) if (Directory.Exists(gitPath))
{ {
@@ -38,7 +48,7 @@ namespace Pf2eDataScrapper.Importers
StartInfo = new ProcessStartInfo() StartInfo = new ProcessStartInfo()
{ {
FileName = "git", FileName = "git",
WorkingDirectory = DataDirPath, WorkingDirectory = RepositoryDirPath,
Arguments = $"clone {importer.GitUrl} {importer.Slug}", Arguments = $"clone {importer.GitUrl} {importer.Slug}",
} }
}; };
@@ -1,11 +1,8 @@
using System; namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
{ {
/// <summary>
/// Тип документов компендиума.
/// </summary>
internal enum CompendiumDocumentType internal enum CompendiumDocumentType
{ {
Actor, Actor,
@@ -1,32 +1,52 @@
using System; using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
{ {
/// <summary>
/// Папка компендиума.
/// </summary>
internal class CompendiumFolder internal class CompendiumFolder
{ {
/// <summary>
/// Идентификатор папки.
/// </summary>
public readonly string Id; public readonly string Id;
/// <summary>
/// Название папки.
/// </summary>
public readonly string Name; public readonly string Name;
/// <summary>
/// Путь к папке.
/// </summary>
public readonly string? Path; public readonly string? Path;
/// <summary>
/// Указатель, что папка содержится в других папках.
/// </summary>
public readonly bool IsContained; public readonly bool IsContained;
/// <summary>
/// Родительская папка.
/// </summary>
public CompendiumFolder? Parent; public CompendiumFolder? Parent;
/// <summary>
/// Тип документов в папке.
/// </summary>
public readonly CompendiumDocumentType Type; public readonly CompendiumDocumentType Type;
/// <summary>
/// Конструктор папки компендиума из JSON-объекта.
/// </summary>
/// <param name="json"></param>
public CompendiumFolder(JObject json) public CompendiumFolder(JObject json)
{ {
if (json.ContainsKey("_id")) if (json.TryGetValue("_id", out var id))
{ {
// Вложенная папка через _folders.json // Вложенная папка через _folders.json
Id = json["_id"]!.ToString(); Id = id!.ToString();
Name = json["name"]!.ToString(); Name = json["name"]!.ToString();
Path = null; Path = null;
IsContained = true; IsContained = true;
@@ -43,6 +63,11 @@ namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
Type = (CompendiumDocumentType)Enum.Parse(typeof(CompendiumDocumentType), json["type"]!.ToString()); Type = (CompendiumDocumentType)Enum.Parse(typeof(CompendiumDocumentType), json["type"]!.ToString());
} }
/// <summary>
/// Конструктор папки компендиума.
/// </summary>
/// <param name="id">Идентификатор папки.</param>
/// <param name="name">Название папки.</param>
public CompendiumFolder(string id, string name) public CompendiumFolder(string id, string name)
{ {
Id = id; Id = id;
@@ -51,6 +76,10 @@ namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
IsContained = false; IsContained = false;
} }
/// <summary>
/// Строковое представление данных о папки компендиума.
/// </summary>
/// <returns>Строковое представление данных о папки компендиума.</returns>
public override string ToString() public override string ToString()
{ {
return $"CompendiumFolder([Id={Id}][Type={Type}][Name={Name}][Path={Path}][Parent={Parent?.Name ?? "null"}])"; return $"CompendiumFolder([Id={Id}][Type={Type}][Name={Name}][Path={Path}][Parent={Parent?.Name ?? "null"}])";
@@ -1,14 +1,15 @@
using System; namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
{ {
/// <summary>
/// Коллекция папок компендиума
/// </summary>
internal class CompendiumFolderCollection : List<CompendiumFolder> internal class CompendiumFolderCollection : List<CompendiumFolder>
{ {
/// <summary>
/// Получение папки по идентификатору
/// </summary>
/// <param name="id">Искомый идентификатор</param>
/// <returns>Искомая папка</returns>
public CompendiumFolder? GetById(string id) public CompendiumFolder? GetById(string id)
{ {
return this.FirstOrDefault(x => x.Id == id); return this.FirstOrDefault(x => x.Id == id);
@@ -1,27 +1,46 @@
using Konsole; using Konsole;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.SchemaBuilder; using Pf2eDataScrapper.SchemaBuilder;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
{ {
/// <summary>
/// Класс построения схемы компендиумов.
/// </summary>
/// <param name="loader">Загрузчик данных.</param>
internal class CompendiumSchemaBuilder(CompendiumLoader loader) internal class CompendiumSchemaBuilder(CompendiumLoader loader)
{ {
/// <summary>
/// Загрузчик данных.
/// </summary>
public CompendiumLoader Loader = loader; public CompendiumLoader Loader = loader;
/// <summary>
/// Коллекция папок компендиума.
/// </summary>
public CompendiumFolderCollection Collection = loader.Collection; public CompendiumFolderCollection Collection = loader.Collection;
/// <summary>
/// Объект для определения типов полей в компендиумах PF2E.
/// </summary>
public CompendiumSchemaTypeResolver Resolver = new(); public CompendiumSchemaTypeResolver Resolver = new();
/// <summary>
/// Схемы данных по папкам.
/// </summary>
public Dictionary<CompendiumFolder, SchemaNode> PackSchemas = []; public Dictionary<CompendiumFolder, SchemaNode> PackSchemas = [];
/// <summary>
/// Схемы данных по типам.
/// </summary>
public Dictionary<string, SchemaNode> TypeSchemas = []; public Dictionary<string, SchemaNode> TypeSchemas = [];
/// <summary>
/// Получение набора схем, к которым относится проверяемый объект.
/// </summary>
/// <param name="folder">Папка компендиума, в которой находится объект.</param>
/// <param name="itemType">Указанный тип объекта.</param>
/// <returns></returns>
protected List<SchemaNode> GetItemSchemas(CompendiumFolder folder, string? itemType) protected List<SchemaNode> GetItemSchemas(CompendiumFolder folder, string? itemType)
{ {
if (!PackSchemas.ContainsKey(folder)) if (!PackSchemas.ContainsKey(folder))
@@ -47,6 +66,9 @@ namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
return [PackSchemas[folder], TypeSchemas[key]]; return [PackSchemas[folder], TypeSchemas[key]];
} }
/// <summary>
/// Построение схем данных.
/// </summary>
public void BuildSchemas() public void BuildSchemas()
{ {
Console.WriteLine("Building schemas..."); Console.WriteLine("Building schemas...");
@@ -90,6 +112,10 @@ namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
bar.Refresh(Loader.ItemCount, "Done!"); bar.Refresh(Loader.ItemCount, "Done!");
} }
/// <summary>
/// Сохранение данных.
/// </summary>
/// <param name="targetDir">Папка для сохранения.</param>
public void Save(string targetDir) public void Save(string targetDir)
{ {
Console.WriteLine("Dumping schemas..."); Console.WriteLine("Dumping schemas...");
@@ -127,6 +153,12 @@ namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
Console.WriteLine("Done!"); Console.WriteLine("Done!");
} }
/// <summary>
/// Сохранение конкретной схемы.
/// </summary>
/// <param name="dir">Корневая папка для сохранения этой категории схем.</param>
/// <param name="name">Название сохраняемой схемы.</param>
/// <param name="schema">Сохраняемая схема.</param>
protected void SaveSchema(string dir, string name, SchemaNode schema) protected void SaveSchema(string dir, string name, SchemaNode schema)
{ {
var invalids = System.IO.Path.GetInvalidFileNameChars(); var invalids = System.IO.Path.GetInvalidFileNameChars();
@@ -134,7 +166,6 @@ namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium
var path = Path.Combine(dir, fileName + ".txt"); var path = Path.Combine(dir, fileName + ".txt");
var dirPath = Path.GetDirectoryName(path); var dirPath = Path.GetDirectoryName(path);
if (!Directory.Exists(dirPath)) if (!Directory.Exists(dirPath))
@@ -1,22 +1,39 @@
using System.Reflection.PortableExecutable; using Newtonsoft.Json.Linq;
using System.Text.RegularExpressions;
using Konsole;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium; using Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry.Compendium;
using Pf2eDataScrapper.SchemaBuilder;
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry
{ {
internal class CompendiumLoader(Pf2eFoundryImporter importer) /// <summary>
/// Загрузчик данных компендиума
/// </summary>
/// <param name="importer">Импортёр</param>
internal class CompendiumLoader(Pf2EFoundryImporter importer)
{ {
public readonly Pf2eFoundryImporter Importer = importer; /// <summary>
/// Импортёр
/// </summary>
public readonly Pf2EFoundryImporter Importer = importer;
/// <summary>
/// Набор папок компендиума
/// </summary>
public CompendiumFolderCollection Collection = []; public CompendiumFolderCollection Collection = [];
/// <summary>
/// Счётчик папок
/// </summary>
private int _folderCount = 0; private int _folderCount = 0;
/// <summary>
/// Общее количество элементов в компендиуме
/// </summary>
public int ItemCount { get; protected set; } = 0; public int ItemCount { get; protected set; } = 0;
/// <summary>
/// Обработка папок компендиума в поле `packFolders`.
/// </summary>
/// <param name="folders"></param>
/// <param name="parent"></param>
protected void ProcessPackFolders(JArray folders, CompendiumFolder? parent) protected void ProcessPackFolders(JArray folders, CompendiumFolder? parent)
{ {
foreach (var token in folders) foreach (var token in folders)
@@ -53,6 +70,9 @@ namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry
} }
} }
/// <summary>
/// Подготовка набора папок компендиума
/// </summary>
public void Prepare() public void Prepare()
{ {
using var systemFile = new StreamReader(Path.Combine(Importer.Dir, "static", "system.json")); using var systemFile = new StreamReader(Path.Combine(Importer.Dir, "static", "system.json"));
@@ -104,10 +124,5 @@ namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry
Console.WriteLine($"Total items: {ItemCount}"); Console.WriteLine($"Total items: {ItemCount}");
} }
public void Run()
{
this.Prepare();
}
} }
} }
@@ -1,43 +1,52 @@
using System; namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.Importers.Implementations.Pf2eFoundry
{ {
internal class Pf2eFoundryImporter : AbstractImporter /// <summary>
/// Импортёр для модуля Pf2e FoundryVTT.
/// </summary>
internal class Pf2EFoundryImporter : AbstractImporter
{ {
/// <summary> /// <summary>
/// Отображаемое имя импортёра /// Отображаемое имя импортёра.
/// </summary> /// </summary>
public override string Name => "Pf2e FoundryVTT module"; public override string Name => "Pf2e FoundryVTT module";
/// <summary> /// <summary>
/// Короткое имя импортёра /// Короткое имя импортёра.
/// </summary> /// </summary>
public override string Slug => "pf2e"; public override string Slug => "pf2e";
/// <summary> /// <summary>
/// Ссылка на git-репозиторий для клонирования /// Ссылка на git-репозиторий для клонирования.
/// </summary> /// </summary>
public override string GitUrl => "https://github.com/foundryvtt/pf2e.git"; public override string GitUrl => "https://github.com/foundryvtt/pf2e.git";
/// <summary>
/// Загрузчик данных компендиума.
/// </summary>
public CompendiumLoader Loader { get; } public CompendiumLoader Loader { get; }
public Pf2eFoundryImporter() /// <summary>
/// Конструктор импортёра.
/// </summary>
public Pf2EFoundryImporter()
{ {
Loader = new CompendiumLoader(this); Loader = new CompendiumLoader(this);
} }
/// <summary>
/// Метод подготовки данных для импорта.
/// </summary>
public override void Prepare() public override void Prepare()
{ {
Loader.Prepare(); Loader.Prepare();
} }
/// <summary>
/// Импорт данных.
/// </summary>
public override void Import() public override void Import()
{ {
Loader.Run(); Loader.Prepare();
} }
} }
} }
+2 -2
View File
@@ -9,10 +9,10 @@ EnvSettings.Load();
Console.WriteLine("Hello, World!"); Console.WriteLine("Hello, World!");
//var loader = new GitLoader(EnvSettings.DataDirPath); //var loader = new GitLoader(EnvSettings.RepositoryDirPath);
var importer = new Pf2eFoundryImporter(); var importer = new Pf2EFoundryImporter();
//loader.UpdateImporter(importer); //loader.UpdateImporter(importer);
+58 -8
View File
@@ -1,41 +1,76 @@
using System; using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.SchemaBuilder.Types; using Pf2eDataScrapper.SchemaBuilder.Types;
namespace Pf2eDataScrapper.SchemaBuilder namespace Pf2eDataScrapper.SchemaBuilder
{ {
/// <summary>
/// Элемент схемы данных.
/// </summary>
internal class SchemaNode internal class SchemaNode
{ {
/// <summary>
/// Отображаемое имя элемента.
/// </summary>
public string Name = ""; public string Name = "";
/// <summary>
/// Путь к элементу.
/// </summary>
public string Path = ""; public string Path = "";
/// <summary>
/// Родительский элемент.
/// </summary>
public SchemaNode? Parent = null; public SchemaNode? Parent = null;
/// <summary>
/// Указание, является ли элемент корневым.
/// </summary>
public bool Root = false; public bool Root = false;
/// <summary>
/// Корневой элемент схемы данных.
/// </summary>
public SchemaNode RootNode => Root ? this : Parent!.RootNode; public SchemaNode RootNode => Root ? this : Parent!.RootNode;
/// <summary>
/// Типы данных, использующиеся в элементе.
/// </summary>
public List<AbstractSchemaType> Types = []; public List<AbstractSchemaType> Types = [];
/// <summary>
/// Указание, может ли этот элемент отсутсвовать в структуре данных.
/// </summary>
public bool IsNullable = false; public bool IsNullable = false;
/// <summary>
/// Объект для определения типа поля в схеме данных.
/// </summary>
public TypeResolver Resolver = new(); public TypeResolver Resolver = new();
/// <summary>
/// Количество значений, добавленных в тип.
/// </summary>
public int Values { get; protected set; } = 0; public int Values { get; protected set; } = 0;
/// <summary>
/// Указание, является ли тип элемента простым.
/// </summary>
public bool IsSimple => Types.Count == 1; public bool IsSimple => Types.Count == 1;
/// <summary>
/// Конструктор корневого элемента.
/// </summary>
public SchemaNode() public SchemaNode()
{ {
this.Root = true; this.Root = true;
} }
/// <summary>
/// Конструктор элемента схемы данных.
/// </summary>
/// <param name="parent">Родительский элемент.</param>
/// <param name="name">Имя элемента.</param>
public SchemaNode(SchemaNode parent, string name) public SchemaNode(SchemaNode parent, string name)
{ {
this.Parent = parent; this.Parent = parent;
@@ -44,6 +79,10 @@ namespace Pf2eDataScrapper.SchemaBuilder
this.Resolver = parent.Resolver; this.Resolver = parent.Resolver;
} }
/// <summary>
/// Добавление нового возможного значения.
/// </summary>
/// <param name="value">Добавляемое значение</param>
public void AddValue(JToken? value) public void AddValue(JToken? value)
{ {
if (value is null || Resolver.TokenType(value) == JTokenType.Null) if (value is null || Resolver.TokenType(value) == JTokenType.Null)
@@ -68,6 +107,10 @@ namespace Pf2eDataScrapper.SchemaBuilder
Types.Add(newType); Types.Add(newType);
} }
/// <summary>
/// Получение строкового представоения типа элемента.
/// </summary>
/// <returns>Строковое представоение типа элемента</returns>
public string GetTypeName() public string GetTypeName()
{ {
if (IsSimple) if (IsSimple)
@@ -75,11 +118,15 @@ namespace Pf2eDataScrapper.SchemaBuilder
return Types[0].GetTypeName(); return Types[0].GetTypeName();
} }
List<string> typeNames = Types.Select(t => t.GetTypeName()).ToList(); var typeNames = Types.Select(t => t.GetTypeName()).ToList();
return string.Join("|", typeNames); return string.Join("|", typeNames);
} }
/// <summary>
/// Полное строковое представление элемента.
/// </summary>
/// <returns>Строковое представление элемента.</returns>
public override string ToString() public override string ToString()
{ {
if (IsSimple) if (IsSimple)
@@ -97,6 +144,9 @@ namespace Pf2eDataScrapper.SchemaBuilder
return string.Join("\n" + AbstractSchemaType.Tab, result); return string.Join("\n" + AbstractSchemaType.Tab, result);
} }
/// <summary>
/// Подготовка элемента к красивому представлению.
/// </summary>
public void Beautify() public void Beautify()
{ {
foreach (var type in Types) foreach (var type in Types)
+33 -8
View File
@@ -1,15 +1,19 @@
using System; using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using Pf2eDataScrapper.SchemaBuilder.Types; using Pf2eDataScrapper.SchemaBuilder.Types;
namespace Pf2eDataScrapper.SchemaBuilder namespace Pf2eDataScrapper.SchemaBuilder
{ {
/// <summary>
/// Класс для определения типа поля в схеме данных.
/// </summary>
internal class TypeResolver 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) public bool CheckPath(SchemaNode node, string path)
{ {
if (!path.Contains("*")) if (!path.Contains("*"))
@@ -27,12 +31,23 @@ namespace Pf2eDataScrapper.SchemaBuilder
return !pathParts.Where((t, i) => t != "*" && t != nodeParts[i]).Any(); return !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) public bool CheckPath(SchemaNode node, ICollection<string> paths)
{ {
return paths.Any(path => CheckPath(node, path)); return paths.Any(path => CheckPath(node, path));
} }
/// <summary>
/// Получение типа значения.
/// </summary>
/// <param name="value">Значение.</param>
/// <returns>Типа значения.</returns>
public JTokenType TokenType(JToken? value) public JTokenType TokenType(JToken? value)
{ {
if (value is null || value.Type is JTokenType.Null or JTokenType.Undefined or JTokenType.None) if (value is null || value.Type is JTokenType.Null or JTokenType.Undefined or JTokenType.None)
@@ -43,13 +58,23 @@ namespace Pf2eDataScrapper.SchemaBuilder
return value.Type; return value.Type;
} }
/// <summary>
/// Определение типа поля.
/// </summary>
/// <param name="node">Поле схемы данных, для которого создаётся тип.</param>
/// <param name="value">Добавляемое значение.</param>
/// <returns>Новый тип для схемы данных</returns>
/// <exception cref="ArgumentNullException">
/// Возникает, если у `value` значение идентично `null`
/// (такие значения должны обрабаотываться в `SchemaNode`).
/// </exception>
public virtual AbstractSchemaType Resolve(SchemaNode node, JToken? value) public virtual AbstractSchemaType Resolve(SchemaNode node, JToken? value)
{ {
var type = TokenType(value); var type = TokenType(value);
if (type == JTokenType.Null) if (type == JTokenType.Null)
{ {
throw new Exception("Value is null, cannot determine type"); throw new ArgumentNullException();
} }
return type switch return type switch
@@ -1,58 +1,76 @@
using System.ComponentModel.Design.Serialization; using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Linq;
namespace Pf2eDataScrapper.SchemaBuilder.Types namespace Pf2eDataScrapper.SchemaBuilder.Types
{ {
/// <summary>
/// Абстрактный тип представоления данных в схеме.
/// </summary>
/// <param name="parent">Элемент, к которому добавляется тип.</param>
internal abstract class AbstractSchemaType(SchemaNode parent) internal abstract class AbstractSchemaType(SchemaNode parent)
{ {
/// <summary>
/// Элемент схемы данных, к которому относится тип.
/// </summary>
public SchemaNode Parent = parent; public SchemaNode Parent = parent;
/// <summary>
/// Корневой элемент схемы данных.
/// </summary>
public SchemaNode Root => Parent.RootNode; public SchemaNode Root => Parent.RootNode;
/// <summary>
/// Объект для определения типа поля в схеме данных.
/// </summary>
public TypeResolver Resolver => Root.Resolver; public TypeResolver Resolver => Root.Resolver;
/// <summary> /// <summary>
/// Check if type is compatible with current type. /// Проверка, что значение совместимо с текущим типом.
/// </summary> /// </summary>
/// <param name="value">Value to be checked</param> /// <param name="value">Проверяемое значение.</param>
/// <returns>True if value is compatible with current type</returns> /// <returns>`true`, если значение совместимо с текущим типом.</returns>
public abstract bool IsCompatibleValue(JToken value); public abstract bool IsCompatibleValue(JToken value);
/// <summary> /// <summary>
/// Add new possible value. /// Добавление нового значения.
/// </summary> /// </summary>
/// <param name="value">New value</param> /// <param name="value">Новое значение.</param>
public abstract void AddValue(JToken value); public abstract void AddValue(JToken value);
/// <summary>
/// Подготовка типа к красивому представлению.
/// </summary>
public abstract void Beautify(); public abstract void Beautify();
/// <summary> /// <summary>
/// String representation of type. /// Получение строкового представоения типа элемента.
/// </summary> /// </summary>
/// <returns></returns> /// <returns>Строковое представоение типа элемента</returns>
public abstract string GetTypeName(); public abstract string GetTypeName();
/// <summary>
/// Полное строковое представление элемента.
/// </summary>
/// <returns>Строковое представление элемента.</returns>
public override string ToString() public override string ToString()
{ {
return $"{GetTypeName()} {Parent.Path}"; return $"{GetTypeName()} {Parent.Path}";
} }
/// <summary> /// <summary>
/// Tabulation characters. /// Отступ для строкового представления.
/// </summary> /// </summary>
public const string Tab = " "; public const string Tab = " ";
/// <summary> /// <summary>
/// Limit of elements in list or dictionary to be displayed. /// Предел количество отображаемых элементов.
/// </summary> /// </summary>
public const int ElementLimit = 20; public const int ElementLimit = 10;
/// <summary> /// <summary>
/// Indent text. /// Увеличение отступа строки.
/// </summary> /// </summary>
/// <param name="text">Text to be indented</param> /// <param name="text">Изменяемая строка.</param>
/// <returns>Indented text</returns> /// <returns>Строка с добавленными отступами.</returns>
public static string Indent(string text) public static string Indent(string text)
{ {
return text.Replace("\n", "\n" + Tab); return text.Replace("\n", "\n" + Tab);
@@ -1,28 +1,42 @@
using System; using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Transactions;
using Newtonsoft.Json.Linq;
namespace Pf2eDataScrapper.SchemaBuilder.Types namespace Pf2eDataScrapper.SchemaBuilder.Types
{ {
/// <summary>
/// Тип представоления массива в схеме.
/// </summary>
/// <param name="parent">Элемент, к которому добавляется тип.</param>
internal class ArraySchemaType(SchemaNode parent) : AbstractSchemaType(parent) internal class ArraySchemaType(SchemaNode parent) : AbstractSchemaType(parent)
{ {
/// <summary>
/// Минимальное количество элементов в массиве.
/// </summary>
public int MinCount = int.MaxValue; public int MinCount = int.MaxValue;
/// <summary>
/// Максимальное количество элементов в массиве.
/// </summary>
public int MaxCount = int.MinValue; public int MaxCount = int.MinValue;
/// <summary>
/// Схема элемента массива.
/// </summary>
protected SchemaNode ElementSchema = new(parent, "[]"); protected SchemaNode ElementSchema = new(parent, "[]");
protected List<JToken?> Values = []; /// <summary>
/// Проверка, что значение совместимо с текущим типом.
/// </summary>
/// <param name="value">Проверяемое значение.</param>
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
public override bool IsCompatibleValue(JToken value) public override bool IsCompatibleValue(JToken value)
{ {
return Resolver.TokenType(value) == JTokenType.Array; return Resolver.TokenType(value) == JTokenType.Array;
} }
/// <summary>
/// Добавление нового значения.
/// </summary>
/// <param name="value">Новое значение.</param>
public override void AddValue(JToken value) public override void AddValue(JToken value)
{ {
if (value.Type != JTokenType.Array) if (value.Type != JTokenType.Array)
@@ -41,16 +55,27 @@ namespace Pf2eDataScrapper.SchemaBuilder.Types
} }
} }
/// <summary>
/// Подготовка типа к красивому представлению.
/// </summary>
public override void Beautify() public override void Beautify()
{ {
ElementSchema.Beautify();
} }
/// <summary>
/// Получение строкового представоения типа элемента.
/// </summary>
/// <returns>Строковое представоение типа элемента</returns>
public override string GetTypeName() public override string GetTypeName()
{ {
return $"array<{ElementSchema.GetTypeName()}>"; return $"array<{ElementSchema.GetTypeName()}>";
} }
/// <summary>
/// Полное строковое представление элемента.
/// </summary>
/// <returns>Строковое представление элемента.</returns>
public override string ToString() public override string ToString()
{ {
return string.Join("\n" + Tab, [ return string.Join("\n" + Tab, [
@@ -1,23 +1,37 @@
using System; using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
namespace Pf2eDataScrapper.SchemaBuilder.Types namespace Pf2eDataScrapper.SchemaBuilder.Types
{ {
/// <summary>
/// Тип представоления словаря в схеме.
/// </summary>
/// <param name="parent">Элемент, к которому добавляется тип.</param>
internal class DictionarySchemaType(SchemaNode parent) : AbstractSchemaType(parent) internal class DictionarySchemaType(SchemaNode parent) : AbstractSchemaType(parent)
{ {
/// <summary>
/// Схема ключей словаря.
/// </summary>
public SchemaNode KeyNode = new(parent, "[key]"); public SchemaNode KeyNode = new(parent, "[key]");
/// <summary>
/// Схема значений словаря.
/// </summary>
public SchemaNode ItemNode = new(parent, "[value]"); public SchemaNode ItemNode = new(parent, "[value]");
/// <summary>
/// Проверка, что значение совместимо с текущим типом.
/// </summary>
/// <param name="value">Проверяемое значение.</param>
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
public override bool IsCompatibleValue(JToken value) public override bool IsCompatibleValue(JToken value)
{ {
return Resolver.TokenType(value) == JTokenType.Object; return Resolver.TokenType(value) == JTokenType.Object;
} }
/// <summary>
/// Добавление нового значения.
/// </summary>
/// <param name="value">Новое значение.</param>
public override void AddValue(JToken value) public override void AddValue(JToken value)
{ {
var obj = (JObject)value; var obj = (JObject)value;
@@ -29,16 +43,28 @@ namespace Pf2eDataScrapper.SchemaBuilder.Types
} }
} }
/// <summary>
/// Подготовка типа к красивому представлению.
/// </summary>
public override void Beautify() public override void Beautify()
{ {
KeyNode.Beautify();
ItemNode.Beautify();
} }
/// <summary>
/// Получение строкового представоения типа элемента.
/// </summary>
/// <returns>Строковое представоение типа элемента</returns>
public override string GetTypeName() public override string GetTypeName()
{ {
return $"dict<{KeyNode.GetTypeName()}, {ItemNode.GetTypeName()}>"; return $"dict<{KeyNode.GetTypeName()}, {ItemNode.GetTypeName()}>";
} }
/// <summary>
/// Полное строковое представление элемента.
/// </summary>
/// <returns>Строковое представление элемента.</returns>
public override string ToString() public override string ToString()
{ {
return string.Join("\n" + Tab, [ return string.Join("\n" + Tab, [
@@ -1,24 +1,42 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.SchemaBuilder.Types namespace Pf2eDataScrapper.SchemaBuilder.Types
{ {
/// <summary>
/// Тип, указывающий, что данные игнорируются.
/// </summary>
/// <param name="parent">Элемент, к которому добавляется тип.</param>
internal class IgnoreSchemaType(SchemaNode parent) : AbstractSchemaType(parent) internal class IgnoreSchemaType(SchemaNode parent) : AbstractSchemaType(parent)
{ {
/// <summary>
/// Проверка, что значение совместимо с текущим типом.
/// </summary>
/// <param name="value">Проверяемое значение.</param>
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
public override bool IsCompatibleValue(JToken value) public override bool IsCompatibleValue(JToken value)
{ {
return true; return true;
} }
/// <summary>
/// Добавление нового значения.
/// </summary>
/// <param name="value">Новое значение.</param>
public override void AddValue(JToken value) public override void AddValue(JToken value)
{ {
} }
/// <summary>
/// Подготовка типа к красивому представлению.
/// </summary>
public override void Beautify() public override void Beautify()
{ {
} }
/// <summary>
/// Получение строкового представоения типа элемента.
/// </summary>
/// <returns>Строковое представоение типа элемента</returns>
public override string GetTypeName() public override string GetTypeName()
{ {
return "ignore"; return "ignore";
@@ -1,21 +1,32 @@
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.SchemaBuilder.Types namespace Pf2eDataScrapper.SchemaBuilder.Types
{ {
/// <summary>
/// Тип представоления объектов в схеме.
/// </summary>
/// <param name="parent">Элемент, к которому добавляется тип.</param>
internal class ObjectSchemaType(SchemaNode parent) : AbstractSchemaType(parent) internal class ObjectSchemaType(SchemaNode parent) : AbstractSchemaType(parent)
{ {
/// <summary>
/// Элемнты полей объекта.
/// </summary>
public List<SchemaNode> Fields = []; public List<SchemaNode> Fields = [];
/// <summary>
/// Проверка, что значение совместимо с текущим типом.
/// </summary>
/// <param name="value">Проверяемое значение.</param>
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
public override bool IsCompatibleValue(JToken value) public override bool IsCompatibleValue(JToken value)
{ {
return Resolver.TokenType(value) == JTokenType.Object; return Resolver.TokenType(value) == JTokenType.Object;
} }
/// <summary>
/// Добавление нового значения.
/// </summary>
/// <param name="value">Новое значение.</param>
public override void AddValue(JToken value) public override void AddValue(JToken value)
{ {
var obj = (JObject)value; var obj = (JObject)value;
@@ -42,6 +53,9 @@ namespace Pf2eDataScrapper.SchemaBuilder.Types
} }
} }
/// <summary>
/// Подготовка типа к красивому представлению.
/// </summary>
public override void Beautify() public override void Beautify()
{ {
Fields.Sort((a, b) => Fields.Sort((a, b) =>
@@ -51,7 +65,7 @@ namespace Pf2eDataScrapper.SchemaBuilder.Types
return 1; return 1;
} }
return a.Name.CompareTo(b.Name); return string.Compare(a.Name, b.Name, StringComparison.Ordinal);
}); });
foreach (var field in Fields) foreach (var field in Fields)
@@ -60,11 +74,19 @@ namespace Pf2eDataScrapper.SchemaBuilder.Types
} }
} }
/// <summary>
/// Получение строкового представоения типа элемента.
/// </summary>
/// <returns>Строковое представоение типа элемента</returns>
public override string GetTypeName() public override string GetTypeName()
{ {
return "object"; return "object";
} }
/// <summary>
/// Полное строковое представление элемента.
/// </summary>
/// <returns>Строковое представление элемента.</returns>
public override string ToString() public override string ToString()
{ {
List<string> rows = [base.ToString()]; List<string> rows = [base.ToString()];
@@ -2,17 +2,37 @@
namespace Pf2eDataScrapper.SchemaBuilder.Types namespace Pf2eDataScrapper.SchemaBuilder.Types
{ {
/// <summary>
/// Тип представоления простых значений в схеме.
/// </summary>
/// <param name="parent">Элемент, к которому добавляется тип.</param>
/// <param name="type">Тип значения.</param>
internal class StaticSchemaType(SchemaNode parent, JTokenType type) : AbstractSchemaType(parent) internal class StaticSchemaType(SchemaNode parent, JTokenType type) : AbstractSchemaType(parent)
{ {
/// <summary>
/// Тип значения.
/// </summary>
protected JTokenType Type = type; protected JTokenType Type = type;
/// <summary>
/// Набор возможных значений и их количества.
/// </summary>
protected Dictionary<JToken, int> Values = []; protected Dictionary<JToken, int> Values = [];
/// <summary>
/// Проверка, что значение совместимо с текущим типом.
/// </summary>
/// <param name="value">Проверяемое значение.</param>
/// <returns>`true`, если значение совместимо с текущим типом.</returns>
public override bool IsCompatibleValue(JToken value) public override bool IsCompatibleValue(JToken value)
{ {
return value.Type == Type; return value.Type == Type;
} }
/// <summary>
/// Добавление нового значения.
/// </summary>
/// <param name="value">Новое значение.</param>
public override void AddValue(JToken value) public override void AddValue(JToken value)
{ {
Values.TryAdd(value, 0); Values.TryAdd(value, 0);
@@ -20,6 +40,9 @@ namespace Pf2eDataScrapper.SchemaBuilder.Types
Values[value]++; Values[value]++;
} }
/// <summary>
/// Подготовка типа к красивому представлению.
/// </summary>
public override void Beautify() public override void Beautify()
{ {
if (Type == JTokenType.String) if (Type == JTokenType.String)
@@ -39,11 +62,19 @@ namespace Pf2eDataScrapper.SchemaBuilder.Types
Values = Values.OrderByDescending(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value); Values = Values.OrderByDescending(pair => pair.Value).ToDictionary(pair => pair.Key, pair => pair.Value);
} }
/// <summary>
/// Получение строкового представоения типа элемента.
/// </summary>
/// <returns>Строковое представоение типа элемента</returns>
public override string GetTypeName() public override string GetTypeName()
{ {
return Type.ToString().ToLower(); return Type.ToString().ToLower();
} }
/// <summary>
/// Полное строковое представление элемента.
/// </summary>
/// <returns>Строковое представление элемента.</returns>
public override string ToString() public override string ToString()
{ {
var result = base.ToString(); var result = base.ToString();