226 lines
7.2 KiB
C#
226 lines
7.2 KiB
C#
using System;
|
|
using ln.types.odb.values;
|
|
using System.Reflection;
|
|
using System.Collections.Generic;
|
|
using ln.logging;
|
|
using System.Linq;
|
|
using ln.types.collections;
|
|
|
|
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();
|
|
}
|
|
}
|
|
|
|
}
|