ln.types/odb/ng/mappings/ClassMapping.cs

237 lines
7.8 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
{
WeakKeyDictionary<object, Guid> cache = new WeakKeyDictionary<object, Guid>();
public delegate object GetID(object o);
public delegate object SetID(object o, object id);
public Type MappedType { get; }
List<FieldInfo> mappedFields = new List<FieldInfo>();
public ClassMapping(Type type)
{
Logging.Log(LogLevel.DEBUG, "Constructing ClassMapping for {0}",type);
MappedType = type;
AddFields(type);
}
private void AddFields(Type type)
{
foreach (FieldInfo fieldinfo in type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
//if (fieldinfo.GetCustomAttribute<IgnoreFieldAttribute>() == null)
//{
mappedFields.Add(fieldinfo);
//if (fieldinfo.GetCustomAttribute<DocumentIDAttribute>() != null)
//{
// getID = (o) => fieldinfo.GetValue(o);
// IDType = fieldinfo.FieldType;
//}
//}
}
foreach (PropertyInfo propInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
{
//if (propInfo.GetCustomAttribute<DocumentIDAttribute>() != null)
//{
// getID = (o) => propInfo.GetValue(o);
// IDType = propInfo.PropertyType;
//}
}
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;
}
public object UnmapValue(Mapper mapper,ODBEntity oval)
{
Document document = oval as Document;
object o = Activator.CreateInstance(MappedType, true);
foreach (FieldInfo fieldInfo in mappedFields)
{
//ByReferenceAttribute byReferenceAttribute = fieldInfo.GetCustomAttribute<ByReferenceAttribute>();
if (false) //(byReferenceAttribute != null)
{
// ToDo: Implement Referentiel Mapping
//Type ceType = byReferenceAttribute.ElementType ?? fieldInfo.FieldType;
//String cAlias = byReferenceAttribute.Alias ?? ceType.FullName;
//ObjectCollection objectCollection = mapper.GetStorage(ceType, cAlias);
//ODBValue refID = document[fieldInfo.Name];
//fieldInfo.SetValue(o, objectCollection.SelectByID(refID));
}
else
{
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);
}
}
return o;
}
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;
if (false) //(fieldInfo.GetCustomAttribute<ByReferenceAttribute>() != null)
{
if (!object.ReferenceEquals(fv, null))
{
// ToDo: Implement referential mapping
//mapper.GetStorage(fieldInfo.FieldType).Ensure(fv);
//ov = mapper.MapValue(mapper.GetDocumentID(fv));
}
else
ov = ODBNull.Instance;
}
else
{
ov = mapper.MapValue(fv);
}
document[fieldInfo.Name] = ov;
}
return document;
}
public ODBEntity MapValue(Mapper mapper,object value)
{
if (Object.ReferenceEquals(value, null))
return ODBNull.Instance;
if (!cache.TryGetValue(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();
}
}
}