Работа над новым отображением сущностей
This commit is contained in:
@@ -0,0 +1,395 @@
|
||||
using System.Collections;
|
||||
using System.Reflection;
|
||||
using LinqToDB.Extensions;
|
||||
using Pf2eModel.Model.Common.I18n;
|
||||
|
||||
namespace Pf2eViewer.EntityTreeViewer;
|
||||
|
||||
internal class StructureBuilder
|
||||
{
|
||||
private readonly List<StructureProcessedObject> _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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var 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.Complex:
|
||||
default:
|
||||
|
||||
StructureProcessedObject? processed = _processedObjects.FirstOrDefault(p =>
|
||||
p.Value == value
|
||||
);
|
||||
|
||||
if (processed is not null)
|
||||
{
|
||||
var reference = new StructureFieldReference(this, name, null, depth);
|
||||
processed.AddReference(reference);
|
||||
return reference;
|
||||
}
|
||||
|
||||
return Create(name, value!, depth);
|
||||
}
|
||||
}
|
||||
|
||||
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 (var reference in _references)
|
||||
{
|
||||
reference.Reference = Field;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<StructureFieldReference> _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 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 int Depth = depth;
|
||||
|
||||
public abstract string GetTypeString();
|
||||
|
||||
public abstract string GetValueString();
|
||||
|
||||
public virtual ICollection<EntityTreeViewerRow> GetTextRows(EntityTreeViewerControl control)
|
||||
{
|
||||
return [new EntityTreeViewerRow(control, this)];
|
||||
}
|
||||
}
|
||||
|
||||
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 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 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<StructureField> Fields = [];
|
||||
|
||||
public readonly Type Type;
|
||||
|
||||
private bool _collapsed = false;
|
||||
|
||||
public bool Collapsed
|
||||
{
|
||||
get => _collapsed;
|
||||
set
|
||||
{
|
||||
_collapsed = 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);
|
||||
}
|
||||
|
||||
public override bool Visible
|
||||
{
|
||||
get => _visible;
|
||||
set
|
||||
{
|
||||
_visible = value;
|
||||
UpdateFieldVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateFieldVisibility()
|
||||
{
|
||||
foreach (var field in Fields)
|
||||
{
|
||||
field.Visible = Visible && !Collapsed;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetFields(LocaleString localeString)
|
||||
{
|
||||
List<FieldRecord> records = [];
|
||||
|
||||
foreach (var field in localeString)
|
||||
{
|
||||
records.Add(new FieldRecord(field.Locale.ToString(), field.Value));
|
||||
}
|
||||
|
||||
SetFieldsFromRecords(records);
|
||||
}
|
||||
|
||||
private void SetFields(IEnumerable enumrable)
|
||||
{
|
||||
int i = 0;
|
||||
List<FieldRecord> records = [];
|
||||
|
||||
foreach (var item in enumrable)
|
||||
{
|
||||
records.Add(new FieldRecord($"[{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<Newtonsoft.Json.JsonIgnoreAttribute>() is null
|
||||
)
|
||||
.Select(property => new FieldRecord(property.Name, property.GetValue(value, null)))
|
||||
.ToList()
|
||||
);
|
||||
}
|
||||
|
||||
private void SetFieldsFromRecords(List<FieldRecord> records)
|
||||
{
|
||||
foreach (FieldRecord record in records)
|
||||
{
|
||||
if (record.FieldType == StructureFieldType.Complex)
|
||||
{
|
||||
record.Allowed = Builder.RegisterProcessed(record.Value!);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var record in records)
|
||||
{
|
||||
if (record.FieldType == StructureFieldType.Complex && record.Allowed)
|
||||
{
|
||||
Fields.Add(Builder.Create(record.Name, record.Value!, Depth + 1));
|
||||
}
|
||||
else
|
||||
{
|
||||
Fields.Add(Builder.Resolve(record.Name, record.Value!, Depth + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override ICollection<EntityTreeViewerRow> GetTextRows(EntityTreeViewerControl control)
|
||||
{
|
||||
List<EntityTreeViewerRow> rows = base.GetTextRows(control).ToList();
|
||||
|
||||
rows.AddRange(Fields.SelectMany(f => f.GetTextRows(control)).ToList());
|
||||
|
||||
return rows;
|
||||
}
|
||||
|
||||
private class FieldRecord(string name, object? value)
|
||||
{
|
||||
public string Name = name;
|
||||
|
||||
public 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 "";
|
||||
}
|
||||
}
|
||||
|
||||
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() => "<Reference>";
|
||||
}
|
||||
Reference in New Issue
Block a user