using System.Collections; using System.Reflection; using LinqToDB.Extensions; using Pf2eModel.Model.Common.I18n; namespace Pf2eViewer.EntityTreeViewer; /// /// Класс для построения структуры данных класса. /// internal class StructureBuilder { /// /// Список обработанных объектов. /// private readonly List _processedObjects = []; /// /// Создание поля структуры. /// /// Название поля. /// Значение поля. /// Глубина, на котором поле отображается. /// Поле структуры. public StructureFieldComplex Create(string name, object value, int depth = 0) { _ = RegisterProcessed(value); StructureFieldComplex field = new(this, value, name, depth); CompleteProcessed(value, field); return field; } /// /// Регистрация обработанного объекта. /// /// Регистрируемый объект. /// `true`, если объект был добавлен; `false`, если объект был добавлен ранее. public bool RegisterProcessed(object value) { if (_processedObjects.Any(p => p.Value == value)) { return false; } StructureProcessedObject processed = new(value); _processedObjects.Add(processed); return true; } /// /// Дополнить обработанный объект. /// /// Значение. /// Поле объекта. public void CompleteProcessed(object value, StructureFieldComplex field) { StructureProcessedObject? q = _processedObjects.FirstOrDefault(p => p.Value == value); if (q == null) { return; } q.Field = field; } /// /// Выбор поля структуры по значению. /// /// Название поля. /// Значение поля. /// Глубина, на котором поле отображается. /// public StructureField Resolve(string name, object? value, int depth) { switch (GetFieldType(value)) { case StructureFieldType.Null: return new StructureFieldNull(this, name, depth); case StructureFieldType.Primitive: return new StructureFieldPrimitive(this, value!, name, depth); case StructureFieldType.Enum: return new StructureFieldEnum(this, value!, name, depth); case StructureFieldType.Reference: break; case StructureFieldType.Complex: default: StructureProcessedObject? processed = _processedObjects.FirstOrDefault(p => p.Value == value ); if (processed is null) { return Create(name, value!, depth); } StructureFieldReference reference = new StructureFieldReference( this, name, null, depth ); processed.AddReference(reference); return reference; } throw new("Impossible"); } /// /// Получить тип поля структуры по значению. /// /// Значение поля. /// Тип поля структуры. public static StructureFieldType GetFieldType(object? value) { if (value is null) { return StructureFieldType.Null; } Type type = value.GetType(); if (type.IsEnum) { return StructureFieldType.Enum; } if (type.IsPrimitive || type == typeof(string)) { return StructureFieldType.Primitive; } return StructureFieldType.Complex; } } /// /// Класс для хранения обработанного объекта. /// /// Объект. internal class StructureProcessedObject(object value) { public readonly object Value = value; private StructureFieldComplex? _field = null; public StructureFieldComplex? Field { get => _field; set { _field = value; foreach (StructureFieldReference reference in _references) { reference.Reference = Field; } } } private readonly List _references = []; public void AddReference(StructureFieldReference reference) { _references.Add(reference); if (_field is not null) { reference.Reference = Field; } } } /// /// Тип поля структуры. /// internal enum StructureFieldType { /// /// Пустое поле. /// Null, /// /// Простое поле. /// Primitive, /// /// Поле с перечислением. /// Enum, /// /// Поле с объектом. /// Complex, /// /// Поле с ссылкой на объект. /// Reference, } /// /// Тип лестницы глубины. /// internal enum LadderType { /// /// Пустой элемент: [ ]. /// Empty, /// /// Последний элемент: [└]. /// Last, /// /// Проходной элемент: [├] или [│]. /// Through, } /// /// Базовый класс для полей структуры. /// /// Класс построения структуры данных. /// Название поля. /// Глубина поля. internal abstract class StructureField(StructureBuilder builder, string name, int depth) { /// /// Название поля. /// public readonly string Name = name; /// /// Тип поля структуры. /// public abstract StructureFieldType FieldType { get; } /// /// Класс построения структуры данных. /// public readonly StructureBuilder Builder = builder; /// /// Флаг, указывающий, отображается ли поле. /// public virtual bool Visible { get; set; } = true; /// /// Глубина поля структуры. /// public readonly int Depth = depth; /// /// Получить строку, представляющую тип поля. /// /// Строка, представляющая тип поля. public abstract string GetTypeString(); /// /// Получить строку, представляющую значение поля. /// /// Строка, представляющая значение поля. public abstract string GetValueString(); /// /// Получить набор строк, используя результат функция. /// /// Тип результирующей строки. /// Функция, оборажения поля в результирующую строку. /// Коллекция, содержащая набор строк. public virtual ICollection GetRows(Func func) { StructureField[] q = [this]; return q.Select(func).ToList(); } } /// /// Пустое поле структуры. /// /// Класс построения структуры данных. /// Название поля. /// Глубина поля. internal class StructureFieldNull(StructureBuilder builder, string name, int depth) : StructureField(builder, name, depth) { /// public override StructureFieldType FieldType => StructureFieldType.Null; /// public override string GetTypeString() => "null"; /// public override string GetValueString() => ""; } /// /// Простое поле структуры. /// /// Класс построения структуры данных. /// Значение поля. /// Название поля. /// Глубина поля. internal class StructureFieldPrimitive( StructureBuilder builder, object value, string name, int depth ) : StructureField(builder, name, depth) { /// public override StructureFieldType FieldType => StructureFieldType.Primitive; /// /// Значение поля. /// public readonly object Value = value; /// /// Тип значения поля. /// public readonly Type Type = value.GetType(); /// public override string GetTypeString() { return Type.GetTypeCode(Type) switch { TypeCode.Boolean => "bool", TypeCode.Int32 => "int", TypeCode.String => "string", TypeCode.Double => "double", _ => Type.Name, }; } /// public override string GetValueString() { if (Type == typeof(string)) { return "\"" + ((string)Value).Replace("\"", "\\\"") + "\""; } return Value.ToString() ?? string.Empty; } } /// /// Поле структуры с перечислением. /// /// Класс построения структуры данных. /// Значение поля. /// Название поля. /// Глубина поля. internal class StructureFieldEnum(StructureBuilder builder, object value, string name, int depth) : StructureField(builder, name, depth) { /// public override StructureFieldType FieldType => StructureFieldType.Enum; /// /// Значение поля. /// public readonly object Value = value; /// /// Тип значения поля. /// public readonly Type Type = value.GetType(); /// public override string GetTypeString() => "enum"; /// public override string GetValueString() => $"{Type.Name}.{Value}"; } /// /// Поле стркутуры с объектом. /// internal class StructureFieldComplex : StructureField { /// public override StructureFieldType FieldType => StructureFieldType.Complex; /// /// Набор полей объекта. /// public readonly List Fields = []; /// /// Тип значения поля. /// public readonly Type Type; /// /// Флаг, указывающий, отображается ли поля объекта. /// public bool Collapsed { get => _collapsed; set { _collapsed = value; UpdateFieldVisibility(); } } private bool _collapsed = false; /// public override bool Visible { get => _visible; set { _visible = value; UpdateFieldVisibility(); } } private bool _visible = true; /// /// Конструктор класса поля структуры с объектом. /// /// Класс построения структуры данных. /// Значение поля. /// Название поля. /// Глубина поля. public StructureFieldComplex(StructureBuilder builder, object value, string name, int depth) : base(builder, name, depth) { Type = value.GetType(); SetFields((dynamic)value); } /// /// Обновить видимость полей структуры. /// private void UpdateFieldVisibility() { foreach (StructureField field in Fields) { field.Visible = Visible && !Collapsed; } } /// /// Установить поля структуры `LocaleString`. /// /// Объект строки локализации. private void SetFields(LocaleString localeString) { SetFieldsFromRecords( localeString .Select(field => new FieldRecord(field.Locale.ToString(), field.Value)) .ToList() ); } /// /// Установить поля структуры `IEnumerable`. /// /// Объект-перечисление. private void SetFields(IEnumerable enumerable) { int i = 0; List records = []; foreach (object? item in enumerable) { records.Add(new($"[{i++}]", item)); } SetFieldsFromRecords(records); } /// /// Установить поля структуры общего объекта. /// /// Объект. private void SetFields(object value) { Type type = value.GetType(); PropertyInfo[] properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance); SetFieldsFromRecords( properties .Where(property => property.GetAttribute() is null ) .Select(property => new FieldRecord(property.Name, property.GetValue(value, null))) .ToList() ); } /// /// Установить поля структуры из списка записей. /// /// private void SetFieldsFromRecords(List 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) ); } } /// /// Запись поля структуры. /// /// Название поля. /// Значение поля. private class FieldRecord(string name, object? value) { /// /// Название поля. /// public readonly string Name = name; /// /// Значение поля. /// public readonly object? Value = value; /// /// Тип поля структуры. /// public StructureFieldType FieldType => StructureBuilder.GetFieldType(Value); /// /// Флаг, указывающий, использовать ли поле. /// public bool Allowed = true; } /// public override string GetTypeString() { if (Type.IsGenericEnumerableType()) { Type elementType = Type.GetGenericArguments().Single(); return $"{elementType.Name}[]"; } else { return Type.Name; } } /// public override string GetValueString() { if (Type.IsGenericEnumerableType()) { return Fields.Count.ToString() + (Fields.Count == 1 ? " item" : " items"); } return ""; } /// public override ICollection GetRows(Func func) { return [.. base.GetRows(func), .. Fields.SelectMany(f => f.GetRows(func)).ToList()]; } } /// /// Поле структуры с ссылкой на объект. /// /// Класс построения структуры данных. /// Название поля. /// Ссылка на объект. /// Глубина поля. internal class StructureFieldReference( StructureBuilder builder, string name, StructureFieldComplex? reference, int depth ) : StructureField(builder, name, depth) { /// /// Ссылка на объект. /// public StructureFieldComplex? Reference = reference; /// public override StructureFieldType FieldType => StructureFieldType.Reference; /// public override string GetTypeString() => Reference?.GetTypeString() ?? "???"; /// public override string GetValueString() => ""; }