Files
HSUI/Config/PluginConfigObjectConverter.cs
T

311 lines
11 KiB
C#

using Dalamud.Logging;
using HSUI.Interface.EnemyList;
using HSUI.Interface.GeneralElements;
using HSUI.Interface.Party;
using HSUI.Interface.StatusEffects;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Runtime.Serialization;
namespace HSUI.Config
{
public abstract class PluginConfigObjectConverter : JsonConverter
{
protected Dictionary<string, PluginConfigObjectFieldConverter> FieldConvertersMap = new Dictionary<string, PluginConfigObjectFieldConverter>();
public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer)
{
var genericMethod = GetType().GetMethod("ConvertJson");
var method = genericMethod?.MakeGenericMethod(objectType);
return method?.Invoke(this, new object[] { reader, serializer });
}
public T? ConvertJson<T>(JsonReader reader, JsonSerializer serializer) where T : PluginConfigObject
{
Type type = typeof(T);
T? config = null;
try
{
ConstructorInfo? constructor = type.GetConstructor(new Type[] { });
if (constructor != null)
{
config = (T?)Activator.CreateInstance<T>();
}
else
{
config = (T?)ConfigurationManager.GetDefaultConfigObjectForType(type);
}
// last resource, hackily create an instance without calling the constructor
if (config == null)
{
#pragma warning disable SYSLIB0050 // Type or member is obsolete
config = (T)FormatterServices.GetUninitializedObject(type);
#pragma warning restore SYSLIB0050 // Type or member is obsolete
}
}
catch (Exception e)
{
Plugin.Logger.Error($"Error creating a {type.Name}: " + e.Message);
}
if (config == null) { return null; }
try
{
JObject? jsonObject = (JObject?)serializer.Deserialize(reader);
if (jsonObject == null) { return null; }
Dictionary<string, object> ValuesMap = new Dictionary<string, object>();
// get values from json
foreach (JProperty property in jsonObject.Properties())
{
string propertyName = property.Name;
object? value = null;
// convert values if needed
if (FieldConvertersMap.TryGetValue(propertyName, out PluginConfigObjectFieldConverter? fieldConverter) && fieldConverter != null)
{
(propertyName, value) = fieldConverter.Convert(property.Value);
}
// read value from json
else
{
FieldInfo? field = type.GetField(propertyName);
if (field != null)
{
value = property.Value.ToObject(field.FieldType);
}
}
if (value != null)
{
ValuesMap.Add(propertyName, value);
}
}
// apply values
foreach (string key in ValuesMap.Keys)
{
string[] fields = key.Split(".", StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
object? currentObject = config;
object value = ValuesMap[key];
for (int i = 0; i < fields.Length; i++)
{
FieldInfo? field = currentObject?.GetType().GetField(fields[i]);
if (field == null) { break; }
if (i == fields.Length - 1)
{
try
{
field.SetValue(currentObject, value);
}
catch { }
}
else
{
currentObject = field.GetValue(currentObject);
}
}
}
}
catch (Exception e)
{
Plugin.Logger.Error($"Error deserializing {type.Name}: " + e.Message);
}
return config;
}
public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer)
{
if (value == null) { return; }
JObject jsonObject = new JObject();
Type type = value.GetType();
jsonObject.Add("$type", type.FullName + ", HSUI");
FieldInfo[] fields = type.GetFields();
foreach (FieldInfo field in fields)
{
if (field.GetCustomAttribute<JsonIgnoreAttribute>() != null) { continue; }
object? fieldValue = field.GetValue(value);
if (fieldValue != null)
{
jsonObject.Add(field.Name, JToken.FromObject(fieldValue, serializer));
}
}
jsonObject.WriteTo(writer);
}
}
#region contract resolver
public class PluginConfigObjectsContractResolver : DefaultContractResolver
{
private static Dictionary<Type, Type> ConvertersMap = new Dictionary<Type, Type>()
{
[typeof(UnitFrameConfig)] = typeof(ColorByHealthFieldsConverter),
[typeof(PlayerUnitFrameConfig)] = typeof(ColorByHealthFieldsConverter),
[typeof(TargetUnitFrameConfig)] = typeof(ColorByHealthFieldsConverter),
[typeof(TargetOfTargetUnitFrameConfig)] = typeof(ColorByHealthFieldsConverter),
[typeof(FocusTargetUnitFrameConfig)] = typeof(ColorByHealthFieldsConverter),
[typeof(PartyFramesColorsConfig)] = typeof(ColorByHealthFieldsConverter),
[typeof(PartyFramesRoleIconConfig)] = typeof(PartyFramesIconsConverter),
[typeof(PartyFramesLeaderIconConfig)] = typeof(PartyFramesIconsConverter),
[typeof(PartyFramesRaiseTrackerConfig)] = typeof(PartyFramesTrackerConfigConverter),
[typeof(PartyFramesInvulnTrackerConfig)] = typeof(PartyFramesTrackerConfigConverter),
[typeof(PartyFramesManaBarConfig)] = typeof(PartyFramesManaBarConfigConverter),
[typeof(StatusEffectsBlacklistConfig)] = typeof(StatusEffectsBlacklistConfigConverter),
[typeof(HUDOptionsConfig)] = typeof(HUDOptionsConfigConverter),
[typeof(CastbarConfig)] = typeof(CastbarConfigConverter),
[typeof(UnitFrameCastbarConfig)] = typeof(CastbarConfigConverter),
[typeof(PlayerCastbarConfig)] = typeof(CastbarConfigConverter),
[typeof(TargetCastbarConfig)] = typeof(CastbarConfigConverter),
[typeof(TargetOfTargetCastbarConfig)] = typeof(CastbarConfigConverter),
[typeof(FocusTargetCastbarConfig)] = typeof(CastbarConfigConverter),
[typeof(PartyFramesCastbarConfig)] = typeof(CastbarConfigConverter),
[typeof(EnemyListCastbarConfig)] = typeof(CastbarConfigConverter),
};
protected override JsonObjectContract CreateObjectContract(Type objectType)
{
JsonObjectContract contract = base.CreateObjectContract(objectType);
if (ConvertersMap.TryGetValue(objectType, out Type? converterType) && converterType != null)
{
contract.Converter = (JsonConverter?)Activator.CreateInstance(converterType);
}
return contract;
}
}
#endregion
#region field converters
public abstract class PluginConfigObjectFieldConverter
{
public readonly string NewFieldPath;
public PluginConfigObjectFieldConverter(string newFieldPath)
{
NewFieldPath = newFieldPath;
}
public abstract (string, object) Convert(JToken token);
}
public class NewTypeFieldConverter<TOld, TNew> : PluginConfigObjectFieldConverter
where TOld : struct
where TNew : struct
{
private TNew DefaultValue;
private Func<TOld, TNew> Func;
public NewTypeFieldConverter(string newFieldPath, TNew defaultValue, Func<TOld, TNew> func) : base(newFieldPath)
{
DefaultValue = defaultValue;
Func = func;
}
public override (string, object) Convert(JToken token)
{
TNew result = DefaultValue;
TOld? oldValue = token.ToObject<TOld>();
if (oldValue.HasValue)
{
result = Func(oldValue.Value);
}
return (NewFieldPath, result);
}
}
public class SameTypeFieldConverter<T> : NewTypeFieldConverter<T, T> where T : struct
{
public SameTypeFieldConverter(string newFieldPath, T defaultValue)
: base(newFieldPath, defaultValue, (oldValue) => { return oldValue; })
{
}
}
public class NewClassFieldConverter<TOld, TNew> : PluginConfigObjectFieldConverter
where TOld : class
where TNew : class
{
private TNew DefaultValue;
private Func<TOld, TNew> Func;
public NewClassFieldConverter(string newFieldPath, TNew defaultValue, Func<TOld, TNew> func)
: base(newFieldPath)
{
DefaultValue = defaultValue;
Func = func;
}
public override (string, object) Convert(JToken token)
{
TNew result = DefaultValue;
TOld? oldValue = token.ToObject<TOld>();
if (oldValue != null)
{
result = Func(oldValue);
}
return (NewFieldPath, result);
}
}
public class SameClassFieldConverter<T> : NewClassFieldConverter<T, T> where T : class
{
public SameClassFieldConverter(string newFieldPath, T defaultValue)
: base(newFieldPath, defaultValue, (oldValue) => { return oldValue; })
{
}
}
public class TypeToClassFieldConverter<TOld, TNew> : PluginConfigObjectFieldConverter
where TOld : struct
where TNew : class
{
private TNew DefaultValue;
private Func<TOld, TNew> Func;
public TypeToClassFieldConverter(string newFieldPath, TNew defaultValue, Func<TOld, TNew> func) : base(newFieldPath)
{
DefaultValue = defaultValue;
Func = func;
}
public override (string, object) Convert(JToken token)
{
TNew result = DefaultValue;
TOld? oldValue = token.ToObject<TOld>();
if (oldValue.HasValue)
{
result = Func(oldValue.Value);
}
return (NewFieldPath, result);
}
}
#endregion
}