2019-03-13 08:22:08 +01:00
|
|
|
|
// /**
|
|
|
|
|
// * File: ODB.cs
|
|
|
|
|
// * Author: haraldwolff
|
|
|
|
|
// *
|
|
|
|
|
// * This file and it's content is copyrighted by the Author and / or copyright holder.
|
|
|
|
|
// * Any use wihtout proper permission is illegal and may lead to legal actions.
|
|
|
|
|
// *
|
|
|
|
|
// *
|
|
|
|
|
// **/
|
|
|
|
|
using System;
|
|
|
|
|
using System.IO;
|
|
|
|
|
using System.Collections.Generic;
|
2019-03-20 14:28:56 +01:00
|
|
|
|
using Castle.DynamicProxy;
|
|
|
|
|
using ln.types.serialize;
|
|
|
|
|
using Castle.Components.DictionaryAdapter;
|
|
|
|
|
using System.Linq;
|
2019-03-13 08:22:08 +01:00
|
|
|
|
namespace ln.types.odb
|
|
|
|
|
{
|
2019-03-20 14:28:56 +01:00
|
|
|
|
public class ODB<T> : ODB
|
|
|
|
|
{
|
|
|
|
|
public T Root
|
|
|
|
|
{
|
|
|
|
|
get => (T)RootObject;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public ODB(String basePath)
|
|
|
|
|
:base(basePath, typeof(T))
|
|
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
public partial class ODB
|
2019-03-13 08:22:08 +01:00
|
|
|
|
{
|
2019-03-20 14:28:56 +01:00
|
|
|
|
public object RootObject { get; protected set; }
|
2019-03-13 08:22:08 +01:00
|
|
|
|
|
2019-03-20 14:28:56 +01:00
|
|
|
|
public String BasePath { get; set; }
|
2019-03-13 08:22:08 +01:00
|
|
|
|
|
2019-03-20 14:28:56 +01:00
|
|
|
|
private ProxyGenerator proxyGenerator = new ProxyGenerator();
|
|
|
|
|
private Dictionary<Guid, WeakReference<IPersistent>> persistentObjects = new Dictionary<Guid, WeakReference<IPersistent>>();
|
|
|
|
|
private Dictionary<Guid, IPersistent> persistentProxies = new Dictionary<Guid, IPersistent>();
|
2019-03-13 08:22:08 +01:00
|
|
|
|
|
2019-03-20 14:28:56 +01:00
|
|
|
|
private Type RootType;
|
2019-03-13 08:22:08 +01:00
|
|
|
|
|
2019-03-20 14:28:56 +01:00
|
|
|
|
public ODB(string basePath)
|
2019-03-13 08:22:08 +01:00
|
|
|
|
{
|
2019-03-20 14:28:56 +01:00
|
|
|
|
BasePath = Path.GetFullPath(basePath);
|
2019-03-13 08:22:08 +01:00
|
|
|
|
if (!Directory.Exists(BasePath))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(BasePath);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-20 14:28:56 +01:00
|
|
|
|
public ODB(String basePath, Type rootType)
|
|
|
|
|
: this(basePath)
|
|
|
|
|
{
|
|
|
|
|
RootType = rootType;
|
|
|
|
|
|
|
|
|
|
Initialize();
|
|
|
|
|
}
|
|
|
|
|
public ODB(String basePath,object rootObject)
|
|
|
|
|
: this(basePath)
|
|
|
|
|
{
|
|
|
|
|
RootType = rootObject.GetType();
|
|
|
|
|
RootObject = rootObject;
|
|
|
|
|
|
|
|
|
|
Initialize();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void Initialize()
|
|
|
|
|
{
|
|
|
|
|
if (RootObject == null)
|
|
|
|
|
{
|
|
|
|
|
string rootHint = Path.Combine(BasePath, "root.hint");
|
|
|
|
|
if (File.Exists(rootHint))
|
|
|
|
|
{
|
|
|
|
|
using (ObjectReader objectReader = new ObjectReader(rootHint))
|
|
|
|
|
{
|
|
|
|
|
Guid persistenceID = objectReader.Read<Guid>();
|
|
|
|
|
RootObject = GetPersistent(persistenceID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
RootObject = Activator.CreateInstance(RootType);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
public bool Contains(Guid persistenceID)
|
|
|
|
|
{
|
|
|
|
|
string fn = Path.Combine(GetPersistentPath(persistenceID), String.Format("{0}.0",persistenceID));
|
|
|
|
|
return File.Exists(fn);
|
|
|
|
|
}
|
2019-03-20 14:28:56 +01:00
|
|
|
|
|
|
|
|
|
IPersistent LoadPersistent(Guid guid)
|
|
|
|
|
{
|
|
|
|
|
IPersistent o = GetCachedPersistent(guid);
|
|
|
|
|
if (o == null)
|
|
|
|
|
{
|
2019-03-21 07:43:39 +01:00
|
|
|
|
|
2019-03-20 14:28:56 +01:00
|
|
|
|
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
return o;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
public void SavePersistent(IPersistent o) => SavePersistent(o, true);
|
|
|
|
|
public void SavePersistent(IPersistent o,bool recurse)
|
|
|
|
|
{
|
|
|
|
|
SaveCollector saveCollector = new SaveCollector(this);
|
|
|
|
|
saveCollector.AddPersistent(o, recurse);
|
|
|
|
|
|
|
|
|
|
foreach (Guid persistenceID in saveCollector.GetCollectedPersistenceIDs())
|
|
|
|
|
{
|
|
|
|
|
SaveBytes(persistenceID, saveCollector.GetPersistentBytes(persistenceID));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* File I/O */
|
|
|
|
|
private string GetPersistentPath(Guid persistenceID)
|
|
|
|
|
{
|
|
|
|
|
byte[] idBytes = persistenceID.ToByteArray();
|
|
|
|
|
return Path.Combine(BasePath, BitConverter.ToString(idBytes, 0, 4));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SaveBytes(Guid persistenceID,byte[] bytes)
|
2019-03-20 14:28:56 +01:00
|
|
|
|
{
|
2019-03-21 07:43:39 +01:00
|
|
|
|
String p = GetPersistentPath(persistenceID);
|
|
|
|
|
String fnbase = Path.Combine(p, persistenceID.ToString());
|
2019-03-20 14:28:56 +01:00
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
if (!Directory.Exists(p))
|
|
|
|
|
Directory.CreateDirectory(p);
|
2019-03-20 14:28:56 +01:00
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
String fnn = String.Format("{0}.new", fnbase);
|
|
|
|
|
|
|
|
|
|
using (FileStream fs = new FileStream(fnn, FileMode.CreateNew))
|
2019-03-20 14:28:56 +01:00
|
|
|
|
{
|
2019-03-21 07:43:39 +01:00
|
|
|
|
fs.Write(bytes,0,bytes.Length);
|
|
|
|
|
fs.Close();
|
2019-03-20 14:28:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
for (int n=5;n>0;n--)
|
|
|
|
|
{
|
|
|
|
|
string fn1 = String.Format("{0}.{1}",fnbase,n-1);
|
|
|
|
|
string fn2 = String.Format("{0}.{1}", fnbase, n);
|
|
|
|
|
|
|
|
|
|
if (File.Exists(fn1))
|
|
|
|
|
File.Move(fn1, fn2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
string fn = String.Format("{0}.{1}", fnbase, 0);
|
|
|
|
|
File.Move(fnn, fn);
|
2019-03-20 14:28:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2019-03-20 14:28:56 +01:00
|
|
|
|
private IPersistent GetCachedPersistent(Guid persistenceID)
|
|
|
|
|
{
|
|
|
|
|
lock (this)
|
|
|
|
|
{
|
|
|
|
|
if (persistentObjects.ContainsKey(persistenceID))
|
|
|
|
|
{
|
|
|
|
|
WeakReference<IPersistent> weak = persistentObjects[persistenceID];
|
|
|
|
|
IPersistent persistent = null;
|
|
|
|
|
|
|
|
|
|
if (weak.TryGetTarget(out persistent))
|
|
|
|
|
{
|
|
|
|
|
return persistent;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
persistentObjects.Remove(persistenceID);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* GetPersistent(..): Return Instance of an IPersistent (may be a proxy)
|
|
|
|
|
*
|
|
|
|
|
**/
|
2019-03-13 08:22:08 +01:00
|
|
|
|
public IPersistent GetPersistent(Guid persistenceID)
|
|
|
|
|
{
|
2019-03-20 14:28:56 +01:00
|
|
|
|
if (Guid.Empty.Equals(persistenceID))
|
|
|
|
|
return null;
|
|
|
|
|
|
2019-03-13 08:22:08 +01:00
|
|
|
|
if (persistentObjects.ContainsKey(persistenceID))
|
|
|
|
|
{
|
2019-03-20 14:28:56 +01:00
|
|
|
|
IPersistent o = GetCachedPersistent(persistenceID);
|
|
|
|
|
if (o == null)
|
|
|
|
|
{
|
2019-03-21 07:43:39 +01:00
|
|
|
|
o = LoadPersistent(persistenceID);
|
2019-03-20 14:28:56 +01:00
|
|
|
|
}
|
|
|
|
|
return o;
|
2019-03-13 08:22:08 +01:00
|
|
|
|
}
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
class SaveCollector
|
2019-03-20 14:28:56 +01:00
|
|
|
|
{
|
|
|
|
|
public ODB ODB { get; }
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
public Guid[] GetCollectedPersistenceIDs()
|
2019-03-20 14:28:56 +01:00
|
|
|
|
{
|
2019-03-21 07:43:39 +01:00
|
|
|
|
return persistentBytes.Keys.ToArray();
|
2019-03-20 14:28:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
public byte[] GetPersistentBytes(Guid persistenceID)
|
2019-03-20 14:28:56 +01:00
|
|
|
|
{
|
2019-03-21 07:43:39 +01:00
|
|
|
|
return persistentBytes[persistenceID];
|
2019-03-20 14:28:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
private Dictionary<Guid, byte[]> persistentBytes = new Dictionary<Guid, byte[]>();
|
2019-03-13 08:22:08 +01:00
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
public SaveCollector(ODB odb)
|
2019-03-20 14:28:56 +01:00
|
|
|
|
{
|
|
|
|
|
ODB = odb;
|
|
|
|
|
}
|
2019-03-13 08:22:08 +01:00
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
public void AddPersistent(IPersistent persistent,bool recurse)
|
2019-03-20 14:28:56 +01:00
|
|
|
|
{
|
2019-03-21 07:43:39 +01:00
|
|
|
|
MemoryStream objectStream = new MemoryStream();
|
|
|
|
|
ODBObjectWriter objectWriter = new ODBObjectWriter(ODB, objectStream);
|
|
|
|
|
objectWriter.Write(persistent);
|
|
|
|
|
|
|
|
|
|
persistentBytes.Add(persistent.GetPersistenceID(), objectStream.ToArray());
|
|
|
|
|
|
|
|
|
|
if (recurse)
|
|
|
|
|
{
|
|
|
|
|
foreach (IPersistent referencedPersistent in objectWriter.ReferencedPersistents)
|
|
|
|
|
{
|
|
|
|
|
AddPersistent(referencedPersistent,recurse);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
2019-03-20 14:28:56 +01:00
|
|
|
|
{
|
2019-03-21 07:43:39 +01:00
|
|
|
|
foreach (IPersistent referencedPersistent in objectWriter.ReferencedPersistents)
|
|
|
|
|
{
|
|
|
|
|
if (!ODB.Contains(referencedPersistent.GetPersistenceID()))
|
|
|
|
|
{
|
|
|
|
|
AddPersistent(referencedPersistent, recurse);
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-20 14:28:56 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-03-21 07:43:39 +01:00
|
|
|
|
|
|
|
|
|
|
2019-03-20 14:28:56 +01:00
|
|
|
|
class LazyInterceptor : IInterceptor
|
|
|
|
|
{
|
|
|
|
|
ODB odb;
|
|
|
|
|
WeakReference<IPersistent> target = null;
|
|
|
|
|
|
|
|
|
|
public LazyInterceptor(ODB odb, Guid persistentID)
|
|
|
|
|
{
|
|
|
|
|
this.odb = odb;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void Intercept(IInvocation invocation)
|
|
|
|
|
{
|
|
|
|
|
object t = target;
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
}
|
|
|
|
|
}
|
2019-03-13 08:22:08 +01:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|