ln.objects/ng/mappings/ClassMapping.cs

226 lines
7.2 KiB
C#

using System;
using System.Reflection;
using System.Collections.Generic;
using ln.logging;
using System.Linq;
using ln.types.collections;
using ln.objects.catalog;
namespace ln.types.odb.ng.mappings
{
public class ClassMapping : IODBMapping
{
public delegate object GetID(object o);
public delegate object SetID(object o, object id);
public Type MappedType { get; }
public bool IsReferenceType => !MappedType.IsValueType;
List<FieldInfo> mappedFields = new List<FieldInfo>();
Func<Mapper, Document, object> createObjectHook;
Func<FieldInfo, bool> filterFieldsHook;
private ClassMapping() { }
public ClassMapping(Type type) : this(type, null, null) { }
public ClassMapping(Type type, Func<Mapper, Document, object> createObjectHook, Func<FieldInfo, bool> filterFieldsHook)
{
Logging.Log(LogLevel.DEBUG, "Constructing ClassMapping for {0}", type);
MappedType = type;
this.createObjectHook = createObjectHook;
this.filterFieldsHook = filterFieldsHook;
AddFields(type);
}
private void AddFields(Type type)
{
foreach (FieldInfo fieldinfo in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
if ((filterFieldsHook == null) || filterFieldsHook(fieldinfo))
mappedFields.Add(fieldinfo);
}
if ((type != null) && !type.IsValueType && (!typeof(object).Equals(type.BaseType)))
{
AddFields(type.BaseType);
}
}
public bool HasField(String name)
{
foreach (FieldInfo fieldInfo in mappedFields)
if (fieldInfo.Name.Equals(name))
return true;
return false;
}
private object GetObjectForDocument(Mapper mapper,Document document)
{
if (IsReferenceType)
{
if (mapper.IdentityCache.TryGetValue(document.ID, out object o, () => (createObjectHook != null) ? createObjectHook(mapper, document) : Activator.CreateInstance(MappedType, true) ))
return o;
}
return (createObjectHook != null) ? createObjectHook(mapper,document) : Activator.CreateInstance(MappedType, true);
}
public object UnmapValue(Mapper mapper,ODBEntity oval)
{
Document document = oval as Document;
object o = GetObjectForDocument(mapper,document);
Apply(mapper, document, o);
return o;
}
public void Apply(Mapper mapper,Document document,object o)
{
foreach (FieldInfo fieldInfo in mappedFields)
{
object fv = mapper.UnmapValue(fieldInfo.FieldType, document[fieldInfo.Name]);
if (!object.ReferenceEquals(fv, null))
{
Type st = fv.GetType();
if (st != fieldInfo.FieldType)
{
fv = mapper.TryImplicitMapping(fv, fieldInfo.FieldType);
st = fv.GetType();
}
if (st != fieldInfo.FieldType)
fv = Convert.ChangeType(fv, fieldInfo.FieldType);
}
if (!object.ReferenceEquals(null, fv))
fieldInfo.SetValue(o, fv);
}
}
public Document MapDocument(Mapper mapper,Guid documentID,object value)
{
Document document = new Document(documentID);
document["__asm__"] = new ODBStringValue(value.GetType().Assembly.GetName().Name);
document["__type__"] = new ODBStringValue(value.GetType().FullName);
foreach (FieldInfo fieldInfo in mappedFields)
{
object fv = fieldInfo.GetValue(value);
ODBEntity ov = null;
ov = mapper.MapValue(fv);
document[fieldInfo.Name] = ov;
}
if (IsReferenceType)
{
mapper.IdentityCache.Ensure(documentID, value);
}
return document;
}
public ODBEntity MapValue(Mapper mapper,object value)
{
if (Object.ReferenceEquals(value, null))
return ODBNull.Instance;
if (!mapper.IdentityCache.TryGetIdentity(value, out Guid documentID))
documentID = Guid.NewGuid();
return MapDocument(mapper, documentID, value);
}
public Type GetFieldType(Mapper mapper,string fieldName)
{
foreach (FieldInfo fieldInfo in mappedFields)
if (fieldInfo.Name.Equals(fieldName))
return fieldInfo.FieldType;
throw new KeyNotFoundException();
}
}
public class ObjectMapping : IODBMapping
{
public ODBEntity MapValue(Mapper mapper, object value)
{
return new Document();
}
public object UnmapValue(Mapper mapper, ODBEntity oval)
{
if (oval is Document)
{
Document document = oval as Document;
if (!document.Contains(new ODBStringValue("__type__")))
return new object();
Type dType = Type.GetType(String.Format("{0}, {1}",document["__type__"].As<String>(), document["__asm__"].As<String>()));
return mapper.UnmapValue(dType, oval);
} else if (oval is ODBList)
{
ODBList list = oval as ODBList;
Array array = (Array)mapper.UnmapValue(typeof(object[]), list);
if (array.Length > 0)
{
/* Magic: Try to find out about the arrays real element type */
HashSet<Type> eTypes = null;
foreach (object v in array)
{
HashSet<Type> myTypes = new HashSet<Type>();
Type eType = v?.GetType();
while (eType != null)
{
myTypes.Add(eType);
eType = eType.BaseType;
}
if (eTypes == null)
{
eTypes = myTypes;
}
else
{
eTypes.IntersectWith(myTypes);
}
}
foreach (Type t in eTypes.ToArray())
{
if (eTypes.Contains(t.BaseType))
eTypes.Remove(t.BaseType);
}
Type baseElementType = eTypes.First();
if (!array.GetType().GetElementType().Equals(baseElementType))
{
Array tarray = Array.CreateInstance(baseElementType, array.Length);
Array.Copy(array, tarray, array.Length);
array = tarray;
}
}
return array;
}
else if (oval is ODBValue)
{
return (oval as ODBValue).Value;
}
throw new NotImplementedException();
}
}
}