AquaDX/AquaMai/AquaMai.Config/Reflection/ReflectionManager.cs

179 lines
6.2 KiB
C#

using System.Reflection;
using System.Linq;
using System.Collections.Generic;
using AquaMai.Config.Attributes;
using AquaMai.Config.Interfaces;
using System;
namespace AquaMai.Config.Reflection;
public class ReflectionManager : IReflectionManager
{
public record Entry : IReflectionManager.IEntry
{
public string Path { get; init; }
public string Name { get; init; }
public IReflectionField Field { get; init; }
public IConfigEntryAttribute Attribute { get; init; }
}
public record Section : IReflectionManager.ISection
{
public string Path { get; init; }
public IReflectionType Type { get; init; }
public IConfigSectionAttribute Attribute { get; init; }
public List<Entry> entries;
public List<IReflectionManager.IEntry> Entries => entries.Cast<IReflectionManager.IEntry>().ToList();
}
private readonly Dictionary<string, Section> sections = new(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, Entry> entries = new(StringComparer.OrdinalIgnoreCase);
private readonly Dictionary<string, Section> sectionsByFullName = [];
public ReflectionManager(IReflectionProvider reflectionProvider)
{
var prefix = "AquaMai.Mods.";
var types = reflectionProvider.GetTypes().Where(t => t.FullName.StartsWith(prefix));
var collapsedNamespaces = new HashSet<string>();
foreach (var type in types)
{
var sectionAttribute = type.GetCustomAttribute<ConfigSectionAttribute>();
if (sectionAttribute == null) continue;
if (collapsedNamespaces.Contains(type.Namespace))
{
throw new Exception($"Collapsed namespace {type.Namespace} contains multiple sections");
}
var path = type.FullName.Substring(prefix.Length);
if (type.GetCustomAttribute<ConfigCollapseNamespaceAttribute>() != null)
{
var separated = path.Split('.');
if (separated[separated.Length - 2] != separated[separated.Length - 1])
{
throw new Exception($"Type {type.FullName} is not collapsable");
}
path = string.Join(".", separated.Take(separated.Length - 1));
collapsedNamespaces.Add(type.Namespace);
}
var sectionEntries = new List<Entry>();
foreach (var field in type.GetFields(BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic))
{
var entryAttribute = field.GetCustomAttribute<ConfigEntryAttribute>();
if (entryAttribute == null) continue;
var transformedName = Utility.ToPascalCase(field.Name);
var entryPath = $"{path}.{transformedName}";
var entry = new Entry()
{
Path = entryPath,
Name = transformedName,
Field = field,
Attribute = entryAttribute
};
sectionEntries.Add(entry);
entries.Add(entryPath, entry);
}
var section = new Section()
{
Path = path,
Type = type,
Attribute = sectionAttribute,
entries = sectionEntries
};
sections.Add(path, section);
sectionsByFullName.Add(type.FullName, section);
}
var order = reflectionProvider.GetEnum("AquaMai.Mods.SectionNameOrder");
sections = sections
.OrderBy(x => x.Key)
.OrderBy(x =>
{
var parts = x.Key.Split('.');
for (int i = parts.Length; i > 0; i--)
{
var key = string.Join("_", parts.Take(i));
if (order.TryGetValue(key, out var value))
{
return (int)value;
}
}
Utility.Log($"Section {x.Key} has no order defined, defaulting to int.MaxValue");
return int.MaxValue;
})
.ToDictionary(x => x.Key, x => x.Value, StringComparer.OrdinalIgnoreCase);
}
public IEnumerable<Section> SectionValues => sections.Values;
public IEnumerable<IReflectionManager.ISection> Sections => sections.Values.Cast<IReflectionManager.ISection>();
public IEnumerable<Entry> EntryValues => entries.Values;
public IEnumerable<IReflectionManager.IEntry> Entries => entries.Values.Cast<IReflectionManager.IEntry>();
public bool ContainsSection(string path)
{
return sections.ContainsKey(path);
}
public bool TryGetSection(string path, out IReflectionManager.ISection section)
{
if (sections.TryGetValue(path, out var sectionValue))
{
section = sectionValue;
return true;
}
section = null;
return false;
}
public bool TryGetSection(Type type, out IReflectionManager.ISection section)
{
bool result = sectionsByFullName.TryGetValue(type.FullName, out var sectionValue);
section = sectionValue;
return result;
}
public IReflectionManager.ISection GetSection(string path)
{
if (!TryGetSection(path, out var section))
{
throw new KeyNotFoundException($"Section {path} not found");
}
return section;
}
public IReflectionManager.ISection GetSection(Type type)
{
if (!TryGetSection(type.FullName, out var section))
{
throw new KeyNotFoundException($"Section {type.FullName} not found");
}
return section;
}
public bool ContainsEntry(string path)
{
return entries.ContainsKey(path);
}
public bool TryGetEntry(string path, out IReflectionManager.IEntry entry)
{
if (entries.TryGetValue(path, out var entryValue))
{
entry = entryValue;
return true;
}
entry = null;
return false;
}
public IReflectionManager.IEntry GetEntry(string path)
{
if (!TryGetEntry(path, out var entry))
{
throw new KeyNotFoundException($"Entry {path} not found");
}
return entry;
}
}