303 lines
11 KiB
C#
303 lines
11 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Reflection;
|
|
using System.Linq;
|
|
using System.Collections;
|
|
using ln.types.net;
|
|
using ln.types.odb.ng.storage;
|
|
using ln.collections;
|
|
using ln.types.odb.ng.mappings;
|
|
using ln.types.odb.ng.storage.fs;
|
|
using ln.objects.catalog;
|
|
|
|
namespace ln.types.odb.ng
|
|
{
|
|
public delegate ODBEntity ODBMap(Mapper mapper, object value);
|
|
public delegate object ODBUnmap(Mapper mapper, ODBEntity oval);
|
|
public delegate ODBEntity ODBMap<T>(Mapper mapper, T value);
|
|
public delegate T ODBUnmap<T>(Mapper mapper, ODBEntity oval);
|
|
|
|
public partial class Mapper : IDisposable
|
|
{
|
|
public static Mapper Default { get; set; } = new Mapper((IStorageContainer)null);
|
|
|
|
public IStorageContainer StorageContainer { get; private set; }
|
|
public IdentityCache IdentityCache { get; } = new IdentityCache();
|
|
|
|
Dictionary<Type, IODBMapping> mappings = new Dictionary<Type, IODBMapping>();
|
|
|
|
mappings.ObjectMapping ObjectMapping { get; }
|
|
|
|
public Mapper(string basePath)
|
|
:this(new FSStorageContainer(basePath))
|
|
{
|
|
StorageContainer.Open();
|
|
}
|
|
public Mapper(IStorageContainer storageContainer)
|
|
{
|
|
if (Default?.StorageContainer == null)
|
|
Default = this;
|
|
|
|
this.StorageContainer = storageContainer;
|
|
|
|
RegisterMapping<string>(
|
|
(mapper, value) => new ODBStringValue(value),
|
|
(mapper, oval) => oval.As<String>()
|
|
);
|
|
RegisterMapping<int>(
|
|
(mapper, value) => new ODBInteger(value),
|
|
(mapper, oval) => oval.As<int>()
|
|
);
|
|
RegisterMapping<short>(
|
|
(mapper, value) => new ODBInteger(value),
|
|
(mapper, oval) => oval.As<short>()
|
|
);
|
|
RegisterMapping<byte>(
|
|
(mapper, value) => new ODBInteger(value),
|
|
(mapper, oval) => oval.As<Byte>()
|
|
);
|
|
|
|
RegisterMapping<uint>(
|
|
(mapper, value) => new ODBUInteger(value),
|
|
(mapper, oval) => oval.As<uint>()
|
|
);
|
|
RegisterMapping<ushort>(
|
|
(mapper, value) => new ODBUInteger(value),
|
|
(mapper, oval) => oval.As<ushort>()
|
|
);
|
|
RegisterMapping<char>(
|
|
(mapper, value) => new ODBUInteger(value),
|
|
(mapper, oval) => oval.As<Char>()
|
|
);
|
|
|
|
RegisterMapping<double>(
|
|
(mapper, value) => new ODBDouble(value),
|
|
(mapper, oval) => oval.As<double>()
|
|
);
|
|
RegisterMapping<float>(
|
|
(mapper, value) => new ODBDouble(value),
|
|
(mapper, oval) => oval.As<float>()
|
|
);
|
|
|
|
RegisterMapping<DateTime>(
|
|
(mapper, value) => new ODBLong(DateTime.MinValue.Equals(value) ? 0 : new DateTimeOffset(value.ToUniversalTime()).ToUnixTimeMilliseconds() ),
|
|
(mapper, oval) => DateTimeOffset.FromUnixTimeMilliseconds(oval.As<long>()).DateTime
|
|
);
|
|
RegisterMapping<TimeSpan>(
|
|
(mapper, value) => new ODBDouble(value.TotalMilliseconds),
|
|
(mapper, oval) => TimeSpan.FromMilliseconds(oval.As<double>())
|
|
);
|
|
|
|
RegisterMapping<Guid>(
|
|
(mapper, value) => new ODBGuid(value),
|
|
(mapper, oval) => oval.As<Guid>()
|
|
);
|
|
|
|
RegisterMapping<long>(
|
|
(mapper, value) => new ODBLong(value),
|
|
(mapper, oval) => oval.As<long>()
|
|
);
|
|
RegisterMapping<ulong>(
|
|
(mapper, value) => new ODBULong(value),
|
|
(mapper, oval) => oval.As<ulong>()
|
|
);
|
|
|
|
RegisterMapping<bool>(
|
|
(mapper, value) => (bool)value ? ODBBool.True : ODBBool.False,
|
|
(mapper, oval) => oval.As<bool>()
|
|
);
|
|
|
|
RegisterMapping<IPv4>(
|
|
(mapper, value) => new ODBUInteger(value.AsUInt),
|
|
(mapper, oval) => new IPv4(oval.As<uint>())
|
|
);
|
|
|
|
RegisterMapping<IPv6>(
|
|
(mapper, value) => new ODBByteBuffer(value.ToCIDRBytes()),
|
|
(mapper, oval) => new IPv6((oval.As<byte[]>()))
|
|
);
|
|
|
|
ObjectMapping = new mappings.ObjectMapping();
|
|
RegisterMapping(typeof(object),ObjectMapping);
|
|
}
|
|
|
|
public void RegisterMapping(Type nativeType, IODBMapping mapping)
|
|
{
|
|
lock (mappings)
|
|
{
|
|
mappings[nativeType] = mapping;
|
|
}
|
|
}
|
|
public void RegisterMapping(Type nativeType, ODBMap map, ODBUnmap unmap)
|
|
{
|
|
lock (mappings)
|
|
{
|
|
mappings[nativeType] = new mappings.SimpleMapping(map, unmap);
|
|
}
|
|
}
|
|
public void RegisterMapping<T>(ODBMap<T> map, ODBUnmap<T> unmap)
|
|
{
|
|
lock (mappings)
|
|
{
|
|
mappings[typeof(T)] = new mappings.SimpleMapping(
|
|
(mapper, value) => map(mapper, (T)value),
|
|
(mapper, value) => unmap(mapper, value)
|
|
);
|
|
}
|
|
}
|
|
|
|
public IStorage GetStorage(Type type) => GetStorage(type, type.FullName);
|
|
public IStorage GetStorage(Type type,string typeName)
|
|
{
|
|
IStorage storage = StorageContainer.GetStorage(typeName);
|
|
if (!storage.IsOpen)
|
|
storage.Open();
|
|
return storage;
|
|
}
|
|
|
|
public IODBMapping GetMapping<T>() => GetMapping(typeof(T));
|
|
public IODBMapping GetMapping(Type type)
|
|
{
|
|
lock (this.mappings)
|
|
{
|
|
if (type == null)
|
|
throw new ArgumentNullException();
|
|
|
|
if (mappings.ContainsKey(type))
|
|
return mappings[type];
|
|
|
|
if (typeof(string).Equals(type))
|
|
{
|
|
return null;
|
|
}
|
|
else if (type.IsGenericType && (type.GetGenericTypeDefinition().Equals(typeof(Dictionary<,>))))
|
|
{
|
|
mappings.Add(type, new mappings.DictionaryMapping());
|
|
return mappings[type];
|
|
}
|
|
else if (type.IsGenericType && (type.GetGenericTypeDefinition().Equals(typeof(List<>)) || type.GetGenericTypeDefinition().Equals(typeof(HashSet<>))))
|
|
{
|
|
mappings.Add(type, new mappings.ListMapping(type));
|
|
return mappings[type];
|
|
}
|
|
else if (type.GetInterfaces().Contains(typeof(IDictionary)))
|
|
{
|
|
mappings.Add(type, new mappings.DictionaryMapping());
|
|
return mappings[type];
|
|
}
|
|
else if (type.IsArray)
|
|
{
|
|
mappings.Add(type, new mappings.ListMapping(type));
|
|
return mappings[type];
|
|
}
|
|
else if (type.IsEnum)
|
|
{
|
|
mappings.Add(type, new SimpleMapping(
|
|
(mapper, value) => new ODBStringValue(Enum.GetName(type, value)),
|
|
(mapper, oval) => Enum.Parse(type, (oval as ODBStringValue).Value as String)
|
|
));
|
|
return mappings[type];
|
|
}
|
|
else if (!type.IsPrimitive)
|
|
{
|
|
mappings.Add(type, new mappings.ClassMapping(type));
|
|
return mappings[type];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
//public object GetDocumentID(object o)
|
|
//{
|
|
// IODBMapping mapping = GetMapping(o.GetType());
|
|
// if (mapping is mappings.ClassMapping)
|
|
// {
|
|
// mappings.ClassMapping classMapping = mapping as mappings.ClassMapping;
|
|
// return classMapping.getID(o);
|
|
// }
|
|
// return null;
|
|
//}
|
|
//public Type GetDocumentIDType(Type type)
|
|
//{
|
|
// IODBMapping mapping = GetMapping(type);
|
|
// if (mapping is mappings.ClassMapping)
|
|
// {
|
|
// mappings.ClassMapping classMapping = mapping as mappings.ClassMapping;
|
|
// return classMapping.IDType;
|
|
// }
|
|
// return null;
|
|
//}
|
|
|
|
public virtual ODBEntity MapValue(object value)
|
|
{
|
|
if (value == null)
|
|
return ODBNull.Instance;
|
|
|
|
IODBMapping mapping = GetMapping(value.GetType());
|
|
if (mapping != null)
|
|
return mapping.MapValue(this,value);
|
|
|
|
throw new NotSupportedException(String.Format("Can't map {0} ({1})",value.GetType(),value));
|
|
}
|
|
public virtual object UnmapValue(Type targetType,ODBEntity value)
|
|
{
|
|
if (ODBNull.Instance.Equals(value))
|
|
return null;
|
|
|
|
if (value is Document)
|
|
{
|
|
Document doc = value as Document;
|
|
String asmname = doc["__asm__"].As<string>();
|
|
String typename = doc["__type__"].As<string>();
|
|
|
|
if (typename != null)
|
|
targetType = Type.GetType(String.Format("{0}, {1}",typename,asmname)); //Assembly.Load(asmname).GetType(typename);
|
|
}
|
|
//else if (value is ODBTypedValue)
|
|
//{
|
|
// ODBTypedValue typedValue = value as ODBTypedValue;
|
|
// targetType = typedValue.TargetType;
|
|
//}
|
|
|
|
IODBMapping mapping = GetMapping(targetType);
|
|
if (mapping != null)
|
|
return mapping.UnmapValue(this,value);
|
|
|
|
return value.As(targetType);
|
|
}
|
|
|
|
public virtual object TryImplicitMapping(object value,Type targetType)
|
|
{
|
|
Type sourceType = value.GetType();
|
|
|
|
foreach (MethodInfo mop in sourceType.GetMethods(BindingFlags.Static | BindingFlags.Public))
|
|
{
|
|
if (mop.Name.Equals("op_Implicit") &&
|
|
(mop.ReturnType.Equals(targetType)))
|
|
if(
|
|
(sourceType.Equals(mop.GetParameters().FirstOrDefault().ParameterType))
|
|
)
|
|
{
|
|
return mop.Invoke(null, new object[] { value });
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
public void Dispose()
|
|
{}
|
|
|
|
public static String GetTypeName(Type type)
|
|
{
|
|
if (type == null)
|
|
return null;
|
|
return string.Format("{0}, {1}",type.FullName,type.Assembly.GetName().Name);
|
|
}
|
|
}
|
|
}
|