diff --git a/Pf2eDataScrapper.sln b/Pf2eDataScrapper.sln index cb8e73e..bd0d4fb 100644 --- a/Pf2eDataScrapper.sln +++ b/Pf2eDataScrapper.sln @@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "output", "output", "{36D591 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pf2eModel", "Pf2eModel\Pf2eModel.csproj", "{D32B7787-D912-4B21-B341-7C1F42F62D36}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pf2eViewer", "Pf2eViewer\Pf2eViewer.csproj", "{9F492EA7-1E1A-439D-A028-506BA445ABE4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -31,6 +33,10 @@ Global {D32B7787-D912-4B21-B341-7C1F42F62D36}.Debug|Any CPU.Build.0 = Debug|Any CPU {D32B7787-D912-4B21-B341-7C1F42F62D36}.Release|Any CPU.ActiveCfg = Release|Any CPU {D32B7787-D912-4B21-B341-7C1F42F62D36}.Release|Any CPU.Build.0 = Release|Any CPU + {9F492EA7-1E1A-439D-A028-506BA445ABE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F492EA7-1E1A-439D-A028-506BA445ABE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F492EA7-1E1A-439D-A028-506BA445ABE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F492EA7-1E1A-439D-A028-506BA445ABE4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/Pf2eModel/EntityOfTypeAttribute.cs b/Pf2eModel/EntityOfTypeAttribute.cs new file mode 100644 index 0000000..0c267e3 --- /dev/null +++ b/Pf2eModel/EntityOfTypeAttribute.cs @@ -0,0 +1,37 @@ +using Pf2eModel.Model.Base; +using Pf2eModel.Model; +using Pf2eModel.Model.Enum; +using System.Reflection; + +namespace Pf2eModel; + +[AttributeUsage(AttributeTargets.Class)] +public class EntityOfTypeAttribute(EntityType type) : Attribute +{ + public EntityType Type { get; } = type; + + public static Type Resolve(Entity entity) + { + foreach (Type type in Assembly.GetExecutingAssembly().GetTypes()) + { + if (type.GetCustomAttributes(typeof(EntityOfTypeAttribute), true).Length <= 0) + { + continue; + } + + if (!type.IsAssignableTo(typeof(IEntity))) + { + throw new Exception($"'EntityOfTypeAttribute' attribute should only be applied to children of 'IEntity' class. '{type.FullName}' is not."); + } + + EntityOfTypeAttribute attribute = type.GetCustomAttribute()!; + + if (attribute.Type == entity.Type) + { + return type; + } + } + + throw new Exception($"Can't resolve class associated with '{entity.Type}' EntityType."); + } +} diff --git a/Pf2eModel/Model/Base/EntityAlias.cs b/Pf2eModel/Model/Base/EntityAlias.cs index a2b06c1..5af3a05 100644 --- a/Pf2eModel/Model/Base/EntityAlias.cs +++ b/Pf2eModel/Model/Base/EntityAlias.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; using Pf2eModel.Model.Enum; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -18,5 +19,6 @@ public partial class EntityAlias [MaxLength(Pf2eDbContext.NameLength)] public string Alias { get; set; } = null!; + [JsonIgnore] public virtual Entity Entity { get; set; } = null!; } diff --git a/Pf2eModel/Model/Base/Keywords/Keyword.cs b/Pf2eModel/Model/Base/Keywords/Keyword.cs index cdc9cc2..0b75901 100644 --- a/Pf2eModel/Model/Base/Keywords/Keyword.cs +++ b/Pf2eModel/Model/Base/Keywords/Keyword.cs @@ -13,6 +13,7 @@ public partial class Keyword [Column(Order = 2)] public string Slug { get; set; } = null!; + [Newtonsoft.Json.JsonIgnore] public virtual ICollection Entities { get; set; } = []; public virtual ICollection Aliases { get; set; } = []; diff --git a/Pf2eModel/Model/Base/Keywords/KeywordAlias.cs b/Pf2eModel/Model/Base/Keywords/KeywordAlias.cs index 920f31f..a7815d7 100644 --- a/Pf2eModel/Model/Base/Keywords/KeywordAlias.cs +++ b/Pf2eModel/Model/Base/Keywords/KeywordAlias.cs @@ -17,5 +17,6 @@ public partial class KeywordAlias [MaxLength(Pf2eDbContext.NameLength)] public string Value { get; set; } = null!; + [Newtonsoft.Json.JsonIgnore] public virtual Keyword Keyword { get; set; } = null!; } diff --git a/Pf2eModel/Model/Base/Source/EntitySource.cs b/Pf2eModel/Model/Base/Source/EntitySource.cs index 26eb0ec..aca54c0 100644 --- a/Pf2eModel/Model/Base/Source/EntitySource.cs +++ b/Pf2eModel/Model/Base/Source/EntitySource.cs @@ -22,6 +22,7 @@ public partial class EntitySource [Comment("Флаг, указывающий, что объект является частью ремастера")] public bool IsRemaster { get; set; } + [Newtonsoft.Json.JsonIgnore] public virtual Entity Entity { get; set; } = null!; public virtual Source Source { get; set; } = null!; diff --git a/Pf2eModel/Model/Base/Source/Source.cs b/Pf2eModel/Model/Base/Source/Source.cs index a5c66cd..749d269 100644 --- a/Pf2eModel/Model/Base/Source/Source.cs +++ b/Pf2eModel/Model/Base/Source/Source.cs @@ -22,5 +22,6 @@ public partial class Source public DateOnly? ReleaseDate { get; set; } + [Newtonsoft.Json.JsonIgnore] public ICollection EntitySources { get; set; } = []; } diff --git a/Pf2eModel/Model/Base/Targets/EntityExternal.cs b/Pf2eModel/Model/Base/Targets/EntityExternal.cs index 58ffbbd..86f02f0 100644 --- a/Pf2eModel/Model/Base/Targets/EntityExternal.cs +++ b/Pf2eModel/Model/Base/Targets/EntityExternal.cs @@ -1,4 +1,5 @@ using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; @@ -23,6 +24,7 @@ public partial class EntityExternal [Comment("Контроль версии объекта")] public string Md5 { get; set; } = null!; + [Newtonsoft.Json.JsonIgnore] public virtual Entity Entity { get; set; } = null!; public virtual ExternalTarget Target { get; set; } = null!; diff --git a/Pf2eModel/Model/Base/Targets/ExternalTarget.cs b/Pf2eModel/Model/Base/Targets/ExternalTarget.cs index 17ec6cd..ca9c55d 100644 --- a/Pf2eModel/Model/Base/Targets/ExternalTarget.cs +++ b/Pf2eModel/Model/Base/Targets/ExternalTarget.cs @@ -23,5 +23,6 @@ public partial class ExternalTarget public string Prefix { get; set; } = null!; [Column(Order = 4)] + [Newtonsoft.Json.JsonIgnore] public virtual ICollection EntityExternals { get; set; } = []; } diff --git a/Pf2eModel/Model/IEntity.cs b/Pf2eModel/Model/IEntity.cs index 863bbf7..ff4b33e 100644 --- a/Pf2eModel/Model/IEntity.cs +++ b/Pf2eModel/Model/IEntity.cs @@ -1,4 +1,5 @@ -using Pf2eModel.Model.Base; +using Newtonsoft.Json; +using Pf2eModel.Model.Base; namespace Pf2eModel.Model; @@ -6,6 +7,7 @@ public interface IEntity { public int Id { get; set; } + [JsonIgnore] public Entity Entity { get; set; } } diff --git a/Pf2eModel/Model/Structure/Action.cs b/Pf2eModel/Model/Structure/Action.cs index afebe40..0cc0dc5 100644 --- a/Pf2eModel/Model/Structure/Action.cs +++ b/Pf2eModel/Model/Structure/Action.cs @@ -4,6 +4,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Action)] public partial class Action : IEntity { [System.ComponentModel.DataAnnotations.Key] diff --git a/Pf2eModel/Model/Structure/Ancestry.cs b/Pf2eModel/Model/Structure/Ancestry.cs index 40544be..a02d58e 100644 --- a/Pf2eModel/Model/Structure/Ancestry.cs +++ b/Pf2eModel/Model/Structure/Ancestry.cs @@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Ancestry)] public partial class Ancestry : IEntity { [System.ComponentModel.DataAnnotations.Key] diff --git a/Pf2eModel/Model/Structure/Background.cs b/Pf2eModel/Model/Structure/Background.cs index 04be35f..bc3dc25 100644 --- a/Pf2eModel/Model/Structure/Background.cs +++ b/Pf2eModel/Model/Structure/Background.cs @@ -1,10 +1,12 @@ using Pf2eModel.Model.Base; +using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure.BackgroundRelation; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Background)] public partial class Background : IEntity { [Key] diff --git a/Pf2eModel/Model/Structure/Class.cs b/Pf2eModel/Model/Structure/Class.cs index 458ff5a..6d03c91 100644 --- a/Pf2eModel/Model/Structure/Class.cs +++ b/Pf2eModel/Model/Structure/Class.cs @@ -1,10 +1,12 @@ using Pf2eModel.Model.Base; +using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure.ClassRelation; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Class)] public partial class Class : IEntity { [Key] diff --git a/Pf2eModel/Model/Structure/Condition.cs b/Pf2eModel/Model/Structure/Condition.cs index a9a3954..a67f1b5 100644 --- a/Pf2eModel/Model/Structure/Condition.cs +++ b/Pf2eModel/Model/Structure/Condition.cs @@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Condition)] public partial class Condition : IEntity { [Key] diff --git a/Pf2eModel/Model/Structure/Deity.cs b/Pf2eModel/Model/Structure/Deity.cs index c58d955..b4fd3e3 100644 --- a/Pf2eModel/Model/Structure/Deity.cs +++ b/Pf2eModel/Model/Structure/Deity.cs @@ -7,6 +7,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Deity)] public partial class Deity : IEntity { [Key] diff --git a/Pf2eModel/Model/Structure/Feat.cs b/Pf2eModel/Model/Structure/Feat.cs index 247c56b..298213c 100644 --- a/Pf2eModel/Model/Structure/Feat.cs +++ b/Pf2eModel/Model/Structure/Feat.cs @@ -6,6 +6,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Feat)] public partial class Feat : IEntity { [Key] diff --git a/Pf2eModel/Model/Structure/Heritage.cs b/Pf2eModel/Model/Structure/Heritage.cs index ad19f3f..38ab84a 100644 --- a/Pf2eModel/Model/Structure/Heritage.cs +++ b/Pf2eModel/Model/Structure/Heritage.cs @@ -1,9 +1,11 @@ using Pf2eModel.Model.Base; +using Pf2eModel.Model.Enum; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Heritage)] public partial class Heritage : IEntity { [Key] diff --git a/Pf2eModel/Model/Structure/Spell.cs b/Pf2eModel/Model/Structure/Spell.cs index 9e3e786..10b065f 100644 --- a/Pf2eModel/Model/Structure/Spell.cs +++ b/Pf2eModel/Model/Structure/Spell.cs @@ -1,10 +1,12 @@ using Pf2eModel.Model.Base; +using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure.SpellRelation; using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Spell)] public partial class Spell : IEntity { [Key] diff --git a/Pf2eModel/Model/Structure/Trait.cs b/Pf2eModel/Model/Structure/Trait.cs index fee868a..234370a 100644 --- a/Pf2eModel/Model/Structure/Trait.cs +++ b/Pf2eModel/Model/Structure/Trait.cs @@ -5,6 +5,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure; +[EntityOfType(EntityType.Trait)] public partial class Trait : IEntity { [Key] @@ -17,5 +18,6 @@ public partial class Trait : IEntity [Column(Order = 2)] public TraitType Type { get; set; } + [Newtonsoft.Json.JsonIgnore] public virtual ICollection Entities { get; set; } = []; } diff --git a/Pf2eModel/Pf2eModel.csproj b/Pf2eModel/Pf2eModel.csproj index d4d89f4..884de5a 100644 --- a/Pf2eModel/Pf2eModel.csproj +++ b/Pf2eModel/Pf2eModel.csproj @@ -22,6 +22,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Pf2eViewer/EntityConventor.cs b/Pf2eViewer/EntityConventor.cs new file mode 100644 index 0000000..ad3eb7e --- /dev/null +++ b/Pf2eViewer/EntityConventor.cs @@ -0,0 +1,114 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Pf2eModel; +using Pf2eModel.Model; +using Pf2eModel.Model.Base; +using Pf2eModel.Model.Structure; +using Processor = System.Func; + +namespace Pf2eViewer; +internal class EntityConventor : JsonConverter +{ + private Pf2eDbContext Context = new(); + + public override bool CanConvert(Type objectType) + { + return objectType == typeof(Entity) || objectType.IsAssignableTo(typeof(IEntity)); + } + + public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) + { + JToken token = JToken.FromObject(value!); + + if (token.Type != JTokenType.Object) + { + token.WriteTo(writer); + return; + } + + JObject obj = (JObject) token; + + switch (value) + { + case Entity e: + obj = ProcessEntity(obj, e); + break; + } + + + obj.WriteTo(writer); + } + + JObject ProcessEntity(JObject obj, Entity e) + { + var traitIds = e.Traits.Select(t => t.Id).ToList(); + var traits = Context.Traits.Where(t => traitIds.Contains(t.Id)).Include(t => t.Entity).ToList(); + + + foreach (var item in (JArray) obj.SelectToken("Traits")) + { + JObject o = (JObject) item; + int id = o.SelectToken("Id").ToObject(); + + o.Add("Name", traits.FirstOrDefault(t => t.Id == id)!.Entity.Name); + } + + obj.Remove("Description"); + + + + return obj; + } + + + + + private JArray GetFromSet(DbSet set, Func where) where T : Type + { + return new(set.Where(where).Select(JToken.FromObject).ToArray()); + } + + + private JObject Process(JsonWriter writer, Entity e, JsonSerializer serializer) + { + JObject result = new(); + + + + // result.Add("Aliases", GetFromSet(context.EntityAliases, o => o.Entity == e)); + result.Add("Sources", new JArray(e.Sources.Select(o => JToken.FromObject(o)).ToArray())); + result.Add("Keywords", new JArray(e.Keywords.Select(o => JToken.FromObject(o)).ToArray())); + result.Add("Externals", new JArray(e.Externals.Select(o => JToken.FromObject(o)).ToArray())); + result.Add("Traits", new JArray(e.Traits.Select(o => JToken.FromObject(o)).ToArray())); + + return result; + } + + + + + + + + + public override bool CanRead => false; + + public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) + { + throw new NotImplementedException("Unnecessary because CanRead is false. The type will skip the converter."); + } + + + private abstract class Conventer(JsonWriter writer, T value, JsonSerializer serializer, Pf2eDbContext context) + { + public JsonWriter Writer { get; } = writer; + public T Value { get; } = value; + public JsonSerializer Serializer { get; } = serializer; + public Pf2eDbContext Context { get; } = context; + + public abstract JObject Process(); + } + +} diff --git a/Pf2eViewer/MainViewForm.Designer.cs b/Pf2eViewer/MainViewForm.Designer.cs new file mode 100644 index 0000000..9c3bb3b --- /dev/null +++ b/Pf2eViewer/MainViewForm.Designer.cs @@ -0,0 +1,190 @@ +namespace Pf2eViewer; + +partial class MainViewForm +{ + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + RootSplitContainer = new SplitContainer(); + InnerSplitContainer = new SplitContainer(); + TypesListBox = new ListBox(); + EntityGridView = new DataGridView(); + ResultSplitContainer = new SplitContainer(); + EntityTreeView = new TreeView(); + DescriptionTextBox = new TextBox(); + ((System.ComponentModel.ISupportInitialize) RootSplitContainer).BeginInit(); + RootSplitContainer.Panel1.SuspendLayout(); + RootSplitContainer.Panel2.SuspendLayout(); + RootSplitContainer.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize) InnerSplitContainer).BeginInit(); + InnerSplitContainer.Panel1.SuspendLayout(); + InnerSplitContainer.Panel2.SuspendLayout(); + InnerSplitContainer.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize) EntityGridView).BeginInit(); + ((System.ComponentModel.ISupportInitialize) ResultSplitContainer).BeginInit(); + ResultSplitContainer.Panel1.SuspendLayout(); + ResultSplitContainer.Panel2.SuspendLayout(); + ResultSplitContainer.SuspendLayout(); + SuspendLayout(); + // + // RootSplitContainer + // + RootSplitContainer.Dock = DockStyle.Fill; + RootSplitContainer.Location = new Point(0, 0); + RootSplitContainer.Name = "RootSplitContainer"; + // + // RootSplitContainer.Panel1 + // + RootSplitContainer.Panel1.Controls.Add(InnerSplitContainer); + RootSplitContainer.Panel1MinSize = 200; + // + // RootSplitContainer.Panel2 + // + RootSplitContainer.Panel2.Controls.Add(ResultSplitContainer); + RootSplitContainer.Panel2MinSize = 400; + RootSplitContainer.Size = new Size(811, 688); + RootSplitContainer.SplitterDistance = 324; + RootSplitContainer.TabIndex = 0; + // + // InnerSplitContainer + // + InnerSplitContainer.Dock = DockStyle.Fill; + InnerSplitContainer.Location = new Point(0, 0); + InnerSplitContainer.Name = "InnerSplitContainer"; + InnerSplitContainer.Orientation = Orientation.Horizontal; + // + // InnerSplitContainer.Panel1 + // + InnerSplitContainer.Panel1.Controls.Add(TypesListBox); + // + // InnerSplitContainer.Panel2 + // + InnerSplitContainer.Panel2.Controls.Add(EntityGridView); + InnerSplitContainer.Size = new Size(324, 688); + InnerSplitContainer.SplitterDistance = 274; + InnerSplitContainer.TabIndex = 1; + // + // TypesListBox + // + TypesListBox.Dock = DockStyle.Fill; + TypesListBox.FormattingEnabled = true; + TypesListBox.ItemHeight = 15; + TypesListBox.Location = new Point(0, 0); + TypesListBox.Name = "TypesListBox"; + TypesListBox.Size = new Size(324, 274); + TypesListBox.TabIndex = 0; + TypesListBox.SelectedIndexChanged += TypesListBox_SelectedIndexChanged; + // + // EntityGridView + // + EntityGridView.AllowUserToAddRows = false; + EntityGridView.AllowUserToDeleteRows = false; + EntityGridView.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.AllCells; + EntityGridView.ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize; + EntityGridView.Dock = DockStyle.Fill; + EntityGridView.Location = new Point(0, 0); + EntityGridView.MultiSelect = false; + EntityGridView.Name = "EntityGridView"; + EntityGridView.ReadOnly = true; + EntityGridView.RowHeadersVisible = false; + EntityGridView.RowHeadersWidthSizeMode = DataGridViewRowHeadersWidthSizeMode.AutoSizeToDisplayedHeaders; + EntityGridView.ShowEditingIcon = false; + EntityGridView.Size = new Size(324, 410); + EntityGridView.TabIndex = 0; + EntityGridView.RowStateChanged += EntityGridView_RowStateChanged; + // + // ResultSplitContainer + // + ResultSplitContainer.Dock = DockStyle.Fill; + ResultSplitContainer.Location = new Point(0, 0); + ResultSplitContainer.Name = "ResultSplitContainer"; + ResultSplitContainer.Orientation = Orientation.Horizontal; + // + // ResultSplitContainer.Panel1 + // + ResultSplitContainer.Panel1.Controls.Add(EntityTreeView); + // + // ResultSplitContainer.Panel2 + // + ResultSplitContainer.Panel2.Controls.Add(DescriptionTextBox); + ResultSplitContainer.Size = new Size(483, 688); + ResultSplitContainer.SplitterDistance = 269; + ResultSplitContainer.TabIndex = 1; + // + // EntityTreeView + // + EntityTreeView.Dock = DockStyle.Fill; + EntityTreeView.Location = new Point(0, 0); + EntityTreeView.Name = "EntityTreeView"; + EntityTreeView.Size = new Size(483, 269); + EntityTreeView.TabIndex = 0; + // + // DescriptionTextBox + // + DescriptionTextBox.Dock = DockStyle.Fill; + DescriptionTextBox.Location = new Point(0, 0); + DescriptionTextBox.Multiline = true; + DescriptionTextBox.Name = "DescriptionTextBox"; + DescriptionTextBox.ReadOnly = true; + DescriptionTextBox.ScrollBars = ScrollBars.Vertical; + DescriptionTextBox.Size = new Size(483, 415); + DescriptionTextBox.TabIndex = 0; + // + // MainViewForm + // + AutoScaleDimensions = new SizeF(7F, 15F); + AutoScaleMode = AutoScaleMode.Font; + ClientSize = new Size(811, 688); + Controls.Add(RootSplitContainer); + Name = "MainViewForm"; + Text = "DataBaseViewer"; + RootSplitContainer.Panel1.ResumeLayout(false); + RootSplitContainer.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize) RootSplitContainer).EndInit(); + RootSplitContainer.ResumeLayout(false); + InnerSplitContainer.Panel1.ResumeLayout(false); + InnerSplitContainer.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize) InnerSplitContainer).EndInit(); + InnerSplitContainer.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize) EntityGridView).EndInit(); + ResultSplitContainer.Panel1.ResumeLayout(false); + ResultSplitContainer.Panel2.ResumeLayout(false); + ResultSplitContainer.Panel2.PerformLayout(); + ((System.ComponentModel.ISupportInitialize) ResultSplitContainer).EndInit(); + ResultSplitContainer.ResumeLayout(false); + ResumeLayout(false); + } + + #endregion + + private SplitContainer RootSplitContainer; + private SplitContainer InnerSplitContainer; + private ListBox TypesListBox; + private DataGridView EntityGridView; + private TreeView EntityTreeView; + private SplitContainer ResultSplitContainer; + private TextBox DescriptionTextBox; +} diff --git a/Pf2eViewer/MainViewForm.cs b/Pf2eViewer/MainViewForm.cs new file mode 100644 index 0000000..23248cd --- /dev/null +++ b/Pf2eViewer/MainViewForm.cs @@ -0,0 +1,167 @@ +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Serialization; +using Pf2eModel; +using Pf2eModel.Model; +using Pf2eModel.Model.Base; +using Pf2eModel.Model.Enum; +using static LinqToDB.Common.Configuration; + +namespace Pf2eViewer; + +public partial class MainViewForm : Form +{ + public Pf2eDbContext Context = new(); + + public MainViewForm() + { + InitializeComponent(); + Reset(); + + EntityGridView.SelectionMode = DataGridViewSelectionMode.FullRowSelect; + } + + public void Reset() + { + Context.Dispose(); + Context = new(); + + TypesListBox.DataSource = Context.Entities.Select(e => e.Type).Distinct().ToList(); + TypesListBox.SelectedIndex = 0; + } + + private void TypesListBox_SelectedIndexChanged(object sender, EventArgs e) + { + if (TypesListBox.SelectedItem is null) + { + return; + } + + EntityGridView.DataSource = Context.Entities + .Where(e => e.Type == (EntityType) TypesListBox.SelectedItem) + .OrderBy(e => e.Id) + .Include(e => e.Sources).Include(e => e.Aliases).Include(e => e.Keywords).Include(e => e.Externals) + .Include(e => e.Traits) + .Select(e => new EntityGridRow(e)) + .ToList(); + EntityGridView.Rows[0].Selected = true; + } + + private void EntityGridView_RowStateChanged(object sender, DataGridViewRowStateChangedEventArgs e) + { + var rowsCount = EntityGridView.SelectedRows.Count; + if (rowsCount == 0 || rowsCount > 1){ + return; + } + + var row = EntityGridView.SelectedRows[0]; + + var data = (EntityGridRow) row.DataBoundItem; + + var json = SerializeEntity(data.GetEntity()); + + JsonToTreeview(EntityTreeView, json, "Entity"); + + DescriptionTextBox.Text = data.GetEntity().Description; + } + + public string SerializeEntity(Entity entity) + { + var settings = new JsonSerializerSettings() + { + Converters = [ new EntityConventor() ] + // ContractResolver = new CamelCasePropertyNamesContractResolver(), + }; + + string result = JsonConvert.SerializeObject(entity, settings); + + Type type = EntityOfTypeAttribute.Resolve(entity); + string subReuslt = JsonConvert.SerializeObject(Context.Find(type, entity.Id), settings); + + return result[..^1] + $",\"SubEntity\":{subReuslt}}}"; + } + + + + public static void JsonToTreeview(TreeView treeView, string jsonString, string rootName) + { + object json = jsonString.StartsWith('[') + ? JArray.Parse(jsonString) + : JObject.Parse(jsonString); + + treeView.Nodes.Clear(); + + foreach(TreeNode node in Element2TreeNode(json, rootName).Nodes) + { + treeView.Nodes.Add(node); + } + } + + private static TreeNode Element2TreeNode(object element, string text = "") + { + List subNodes = []; + + switch (element.GetType().Name) + { + case "JObject": + JObject jObject = (JObject) element; + foreach (KeyValuePair token in jObject) + { + subNodes.Add(Element2TreeNode(token.Value, token.Key)); + } + break; + + case "JArray": + JArray jArray = (JArray) element; + int i = 0; + foreach (JToken oJtok in jArray) + { + subNodes.Add(Element2TreeNode(oJtok, string.Format("[{0}]", i++))); + } + + if (i == 0) + { + text += ": []"; + } + break; + + case "JValue": + JValue jValue = (JValue) element; + text += $": ({jValue.Type}) {jValue}"; + break; + + default: + throw new Exception("clsJSON2Treeview can't interpret object:" + element.GetType().Name); + } + + TreeNode treeNode = new(text); + + if (subNodes.Count > 0) + { + treeNode.Nodes.AddRange(subNodes + .OrderBy(node => node.Nodes.Count > 0 ? 1 : 0) + .ToArray() + ); + } + + return treeNode; + } + + + + + + + private class EntityGridRow(Entity e) + { + public int Id { get; set; } = e.Id; + public string Name { get; set; } = e.Name; + //public string Slug { get; set; } = e.Slug; + //public EntityRarity Rarity { get; set; } = e.Rarity; + + private readonly Entity _e = e; + public Entity GetEntity() => _e; + + } +} diff --git a/Pf2eViewer/MainViewForm.resx b/Pf2eViewer/MainViewForm.resx new file mode 100644 index 0000000..8b2ff64 --- /dev/null +++ b/Pf2eViewer/MainViewForm.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Pf2eViewer/Pf2eViewer.csproj b/Pf2eViewer/Pf2eViewer.csproj new file mode 100644 index 0000000..a76dae8 --- /dev/null +++ b/Pf2eViewer/Pf2eViewer.csproj @@ -0,0 +1,23 @@ + + + + WinExe + net8.0-windows + enable + true + enable + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Pf2eViewer/Program.cs b/Pf2eViewer/Program.cs new file mode 100644 index 0000000..20e2262 --- /dev/null +++ b/Pf2eViewer/Program.cs @@ -0,0 +1,16 @@ +namespace Pf2eViewer; + +internal static class Program +{ + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + // To customize application configuration such as set high DPI settings or default font, + // see https://aka.ms/applicationconfiguration. + ApplicationConfiguration.Initialize(); + Application.Run(new MainViewForm()); + } +} \ No newline at end of file