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 mappedFields = new List(); Func createObjectHook; Func filterFieldsHook; private ClassMapping() { } public ClassMapping(Type type) : this(type, null, null) { } public ClassMapping(Type type, Func createObjectHook, Func 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(), document["__asm__"].As())); 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 eTypes = null; foreach (object v in array) { HashSet myTypes = new HashSet(); 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(); } } }