240 lines
6.9 KiB
C#
240 lines
6.9 KiB
C#
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Reflection;
|
|
using LinqToDB.Extensions;
|
|
using Pf2e.Database.Model.Common.I18n;
|
|
|
|
namespace Pf2e.WPF.EntityViewer.EntityViewStructure;
|
|
|
|
/// <summary>
|
|
/// Поле стркутуры с объектом.
|
|
/// </summary>
|
|
internal class StructureFieldComplex : StructureField
|
|
{
|
|
/// <inheritdoc/>
|
|
public override StructureFieldType FieldType => StructureFieldType.Complex;
|
|
|
|
/// <summary>
|
|
/// Набор полей объекта.
|
|
/// </summary>
|
|
public readonly List<StructureField> Fields = [];
|
|
|
|
/// <summary>
|
|
/// Тип значения поля.
|
|
/// </summary>
|
|
public readonly Type Type;
|
|
|
|
/// <summary>
|
|
/// Флаг, указывающий, отображается ли поля объекта.
|
|
/// </summary>
|
|
public bool Collapsed
|
|
{
|
|
get => _collapsed;
|
|
set
|
|
{
|
|
_collapsed = value;
|
|
UpdateFieldVisibility();
|
|
OnPropertyChanged();
|
|
}
|
|
}
|
|
private bool _collapsed;
|
|
|
|
/// <summary>
|
|
/// Флаг, указывающий, что у этот элемент можно свернуть.
|
|
/// </summary>
|
|
public bool Collapsable
|
|
{
|
|
get => Fields.Count > 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Значение поля идентификатора элемента.
|
|
/// </summary>
|
|
public string? IdValue { get; private set; }
|
|
|
|
/// <inheritdoc/>
|
|
public override bool Visible
|
|
{
|
|
get => base.Visible;
|
|
set
|
|
{
|
|
base.Visible = value;
|
|
UpdateFieldVisibility();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Конструктор класса поля структуры с объектом.
|
|
/// </summary>
|
|
/// <param name="builder">Класс построения структуры данных.</param>
|
|
/// <param name="value">Значение поля.</param>
|
|
/// <param name="name">Название поля.</param>
|
|
/// <param name="depth">Глубина поля.</param>
|
|
public StructureFieldComplex(StructureBuilder builder, object value, string name, int depth)
|
|
: base(builder, name, depth)
|
|
{
|
|
Type = value.GetType();
|
|
|
|
SetFields((dynamic)value);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Обновить видимость полей структуры.
|
|
/// </summary>
|
|
private void UpdateFieldVisibility()
|
|
{
|
|
foreach (StructureField field in Fields)
|
|
{
|
|
field.Visible = Visible && !Collapsed;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Установить поля структуры `LocaleString`.
|
|
/// </summary>
|
|
/// <param name="localeString">Объект строки локализации.</param>
|
|
private void SetFields(LocaleString localeString)
|
|
{
|
|
SetFieldsFromRecords(
|
|
localeString
|
|
.Select(field => new FieldRecord(field.Locale.ToString(), field.Value))
|
|
.ToList()
|
|
);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Установить поля структуры `IEnumerable`.
|
|
/// </summary>
|
|
/// <param name="enumerable">Объект-перечисление.</param>
|
|
private void SetFields(IEnumerable enumerable)
|
|
{
|
|
int i = 0;
|
|
|
|
List<FieldRecord> records = [];
|
|
|
|
foreach (object? item in enumerable)
|
|
{
|
|
records.Add(new($"[{i++}]", item));
|
|
}
|
|
|
|
SetFieldsFromRecords(records);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Установить поля структуры общего объекта.
|
|
/// </summary>
|
|
/// <param name="value">Объект.</param>
|
|
private void SetFields(object value)
|
|
{
|
|
Type type = value.GetType();
|
|
PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
|
|
|
|
var records = properties
|
|
.Where(property => property.GetAttribute<Newtonsoft.Json.JsonIgnoreAttribute>() is null)
|
|
.Select(property => new FieldRecord(property.Name, property.GetValue(value, null)))
|
|
.ToList();
|
|
|
|
var idRecord = records.FirstOrDefault(record => record.Name == "Id");
|
|
|
|
if (idRecord is not null && idRecord.Value is not null)
|
|
{
|
|
records.Remove(idRecord);
|
|
IdValue = idRecord.Value.ToString();
|
|
}
|
|
|
|
SetFieldsFromRecords(records);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Установить поля структуры из списка записей.
|
|
/// </summary>
|
|
/// <param name="records"></param>
|
|
private void SetFieldsFromRecords(List<FieldRecord> records)
|
|
{
|
|
foreach (
|
|
FieldRecord record in records.Where(record =>
|
|
record.FieldType == StructureFieldType.Complex
|
|
)
|
|
)
|
|
{
|
|
record.Allowed = Builder.RegisterProcessed(record.Value!);
|
|
}
|
|
|
|
foreach (FieldRecord record in records)
|
|
{
|
|
Fields.Add(
|
|
record is { FieldType: StructureFieldType.Complex, Allowed: true }
|
|
? Builder.Create(record.Name, record.Value!, Depth + 1)
|
|
: Builder.Resolve(record.Name, record.Value!, Depth + 1)
|
|
);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Запись поля структуры.
|
|
/// </summary>
|
|
/// <param name="name">Название поля.</param>
|
|
/// <param name="value">Значение поля.</param>
|
|
private class FieldRecord(string name, object? value)
|
|
{
|
|
/// <summary>
|
|
/// Название поля.
|
|
/// </summary>
|
|
public readonly string Name = name;
|
|
|
|
/// <summary>
|
|
/// Значение поля.
|
|
/// </summary>
|
|
public readonly object? Value = value;
|
|
|
|
/// <summary>
|
|
/// Тип поля структуры.
|
|
/// </summary>
|
|
public StructureFieldType FieldType => StructureBuilder.GetFieldType(Value);
|
|
|
|
/// <summary>
|
|
/// Флаг, указывающий, использовать ли поле.
|
|
/// </summary>
|
|
public bool Allowed = true;
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override string GetTypeString()
|
|
{
|
|
if (Type.IsGenericEnumerableType())
|
|
{
|
|
Type elementType = Type.GetGenericArguments().Single();
|
|
|
|
return $"{elementType.Name}[]";
|
|
}
|
|
else
|
|
{
|
|
return Type.Name;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override string GetValueString()
|
|
{
|
|
if (Type.IsGenericEnumerableType())
|
|
{
|
|
return Fields.Count.ToString() + (Fields.Count == 1 ? " item" : " items");
|
|
}
|
|
|
|
if (IdValue is not null)
|
|
{
|
|
return $"#{IdValue}";
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/// <inheritdoc/>
|
|
public override ICollection<T> GetRows<T>(Func<StructureField, T> func)
|
|
{
|
|
return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()];
|
|
}
|
|
}
|