Salmon/Salmon.Core/Cliff/Translator.cs
2024-04-11 21:30:36 +02:00

239 lines
6.6 KiB
C#

using System.ComponentModel;
using System.Data.SqlTypes;
using System.Diagnostics;
using System.Reflection;
namespace Salmon.Core.Cliff;
public class Translator
{
public const string TYPE_PREDICATE = "is";
private class TypeInfo
{
public Func<Triplet[], Element> Decoder;
public Func<Element, Triplet[]> Encoder;
}
private class FieldData
{
public PropertyInfo FieldInfo { get; set; }
public string Predicate { get; set; }
}
private Dictionary<string, TypeInfo> Informations = new ();
public void DiscoverAllTypes()
{
Informations.Clear();
var elemType = typeof(Element);
CreateTypeInfo(elemType);
foreach (var a in AppDomain.CurrentDomain.GetAssemblies())
foreach (var t in a.GetTypes())
if (t.IsClass && t.IsSubclassOf(elemType))
CreateTypeInfo(t);
}
public string GetTypename(Type t)
{
string typename = t.Name;
var typeAttr = t.GetCustomAttribute(typeof(SetType), false) as SetType;
if (typeAttr is not null)
typename = typeAttr.Name;
return typename;
}
public static bool IsNullable(Type type)
{
return Nullable.GetUnderlyingType(type) != null;
}
private TypeInfo CreateTypeInfo(Type t)
{
var ret = new TypeInfo();
Dictionary<string, FieldData> Fields = new();
string typename = GetTypename(t);
foreach (var field in t.GetProperties())
{
var persistenceAttributes = field.GetCustomAttributes(typeof(PersistentField), true);
if (persistenceAttributes.Length == 0)
continue;
var persistence = (PersistentField)persistenceAttributes[0];
string name = persistence.Name ?? field.Name;
Fields.Add(name, new FieldData
{
FieldInfo = field,
Predicate = name
});
}
var constructor = t.GetConstructor(new[] { typeof(string) });
if (constructor == null)
throw new Exception($"Cannot find contructor(string) for type {t.FullName}");
ret.Encoder = (Element o) =>
{
Debug.Assert(o is not null);
Debug.Assert(t.IsInstanceOfType(o));
List<Triplet> triplets = new();
triplets.Add(new Triplet
{
key = o.UniqueId,
predicate = TYPE_PREDICATE,
value = typename
});
foreach (var fd in Fields)
{
var val = fd.Value.FieldInfo.GetValue(o);
/*if (val is null)
continue;*/
triplets.Add(new Triplet
{
key = o.UniqueId,
predicate = fd.Value.Predicate,
value = val
});
}
foreach (var kv in o.SupplementaryProperties)
triplets.Add(new Triplet
{
key = o.UniqueId,
predicate = kv.Key,
value = kv.Value
});
return triplets.ToArray();
};
ret.Decoder = (Triplet[] triplets) =>
{
if (triplets.Length == 0)
throw new Exception($"Cannot decode without any triplets");
var ret = constructor.Invoke(new[] { triplets[0].key }) as Element;
if(ret is null)
throw new Exception($"Calling contructor(string) for type {t.FullName} return null");
foreach (var t in triplets)
{
string fname = t.predicate;
if(Fields.ContainsKey(fname))
{
var fieldinfo = Fields[fname].FieldInfo;
var objval = t.value;
if (objval is string && fieldinfo.PropertyType == typeof(DateTime?))
objval = DateTime.Parse((string)objval);
if(objval is not null && !objval.GetType().IsAssignableTo(fieldinfo.PropertyType))
{
objval = Convert.ChangeType(objval, Nullable.GetUnderlyingType(fieldinfo.PropertyType));
}
fieldinfo.SetValue(ret, objval);
}
else
ret.SupplementaryProperties.Add(fname, t.value);
}
return ret;
};
if(!Informations.TryAdd(typename, ret))
Informations[typename] = ret;
return ret;
}
TypeInfo? GetInfo(IEnumerable<Triplet> triplets)
{
string? type = null;
foreach (var triplet in triplets)
if (triplet.predicate == TYPE_PREDICATE)
{
type = triplet.value as string;
break;
}
if (type == null)
return null;
if (Informations.Count == 0)
DiscoverAllTypes();
if (Informations.TryGetValue(type, out var ret))
return ret;
return null;
}
TypeInfo? GetInfo(Type info)
{
string typename = GetTypename(info);
if (Informations.Count == 0)
DiscoverAllTypes();
if (Informations.TryGetValue(typename, out var typeInfo))
return typeInfo;
return null;
}
public IEnumerable<Triplet> Encode(Element instance)
{
var typeinfo = GetInfo(instance.GetType());
if (typeinfo is null)
yield break;
foreach(var t in typeinfo.Encoder(instance))
yield return t;
}
public Element Decode(IEnumerable<Triplet> triples, bool greedy = true)
{
var typeinfo = GetInfo(triples);
if (typeinfo is null)
{
if(greedy == false)
throw new Exception($"Cannot load type information for triplet list");
typeinfo = GetInfo(typeof(Element));
if (typeinfo is null)
throw new Exception($"GetInfo(typeof(Element)) return null.");
}
var decoded = typeinfo.Decoder(triples.ToArray());
if (decoded is null)
throw new Exception($"Cannot deserialize triplets set as {typeinfo}.");
return decoded;
}
public T Decode<T>(IEnumerable<Triplet> triples) where T : Element
{
var decoded = Decode(triples);
var typed = decoded as T;
if (typed is null)
throw new Exception($"Cannot convert decoded type {decoded.GetType()} as {typeof(T)}.");
return typed;
}
}