diff --git a/.gitignore b/.gitignore index bf793ed..2452a0c 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ Thumbs.db # dotCover *.dotCover + +ln.logging \ No newline at end of file diff --git a/ln.types.csproj b/ln.types.csproj index 9eb57fa..65f6970 100644 --- a/ln.types.csproj +++ b/ln.types.csproj @@ -53,7 +53,6 @@ - @@ -121,6 +120,15 @@ + + + + + + + + + @@ -143,7 +151,7 @@ - + {D471A566-9FB6-41B2-A777-3C32874ECD0E} ln.logging diff --git a/ln.types.sln b/ln.types.sln new file mode 100644 index 0000000..c542083 --- /dev/null +++ b/ln.types.sln @@ -0,0 +1,29 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.types", "ln.types.csproj", "{8D9AB9A5-E513-4BA7-A450-534F6456BF28}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.types.test", "ln.types.test\ln.types.test.csproj", "{E39D8B11-7CF6-4C78-B723-F7E100121704}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ln.logging", "ln.logging\ln.logging.csproj", "{D471A566-9FB6-41B2-A777-3C32874ECD0E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8D9AB9A5-E513-4BA7-A450-534F6456BF28}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8D9AB9A5-E513-4BA7-A450-534F6456BF28}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8D9AB9A5-E513-4BA7-A450-534F6456BF28}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8D9AB9A5-E513-4BA7-A450-534F6456BF28}.Release|Any CPU.Build.0 = Release|Any CPU + {E39D8B11-7CF6-4C78-B723-F7E100121704}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E39D8B11-7CF6-4C78-B723-F7E100121704}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E39D8B11-7CF6-4C78-B723-F7E100121704}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E39D8B11-7CF6-4C78-B723-F7E100121704}.Release|Any CPU.Build.0 = Release|Any CPU + {D471A566-9FB6-41B2-A777-3C32874ECD0E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D471A566-9FB6-41B2-A777-3C32874ECD0E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D471A566-9FB6-41B2-A777-3C32874ECD0E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D471A566-9FB6-41B2-A777-3C32874ECD0E}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/ln.types.test/Program.cs b/ln.types.test/Program.cs new file mode 100644 index 0000000..dabeed0 --- /dev/null +++ b/ln.types.test/Program.cs @@ -0,0 +1,90 @@ +// /** +// * File: Program.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 ln.types.odb.ng.storage; +using ln.types.odb.ng; +using ln.logging; +using System.Linq; + +namespace ln.types.test +{ + class MainClass + { + public static void Main(string[] args) + { + Logger.ConsoleLogger.MaxLogLevel = LogLevel.DEBUGDETAIL; + Logging.Log(LogLevel.DEBUG, "ln.types.odb.ng test suite"); + + using (IStorageContainer container = new Session(new FSStorageContainer("."))) + { + container.Open(); + + IStorage storage = container.GetStorage("test"); + + if (!storage.IsOpen) + storage.Open(); + + foreach (Guid documentID in storage.GetDocumentIDs()) + { + Logging.Log(LogLevel.INFO, "DocumentID: {0}", documentID); + Document doc = storage.Load(documentID); + Logging.Log(LogLevel.INFO, doc.ToString()); + doc["FeldC"] = doc["FeldC"].AsLong + 1; + storage.Save(doc); + } + + Document document = new Document(); + + document["FeldA"] = "Ich bin das Feld A"; + document["FeldB"] = "Ich bin das Feld B"; + document["FeldC"] = new Random().Next(); + + storage.Save(document); + storage.Save(document); + + ODBMapper mapper = new ODBMapper(container); + + foreach (MapableClass mapableClass in mapper.GetDocumentIDs().Select((id)=>mapper.Load(id))) + { + Logging.Log(LogLevel.INFO, "mapper found: {0}",mapableClass); + mapableClass.ANumber++; + mapper.Save(mapableClass); + } + + MapableClass mapable = new MapableClass(); + mapper.Save(mapable); + + + storage.Close(); + container.Close(); + } + } + } + + class MapableClass + { + public int ANumber { get; set; } + public String MyString; + + public MapableClass() + { + ANumber = new Random().Next(); + MyString = ANumber.ToString(); + } + + public override string ToString() + { + return String.Format("[MapableClass ANumber={0} MyString={1}]",ANumber,MyString); + } + + + } + +} diff --git a/ln.types.test/Properties/AssemblyInfo.cs b/ln.types.test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..d21506a --- /dev/null +++ b/ln.types.test/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +// /** +// * File: AssemblyInfo.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.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("ln.types.test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] diff --git a/ln.types.test/ln.types.test.csproj b/ln.types.test/ln.types.test.csproj new file mode 100644 index 0000000..d4b68a2 --- /dev/null +++ b/ln.types.test/ln.types.test.csproj @@ -0,0 +1,47 @@ + + + + Debug + AnyCPU + {E39D8B11-7CF6-4C78-B723-F7E100121704} + Exe + ln.types.test + ln.types.test + v4.7 + + + true + full + false + bin\Debug + DEBUG; + prompt + 4 + true + + + true + bin\Release + prompt + 4 + true + + + + + + + + + + + {8D9AB9A5-E513-4BA7-A450-534F6456BF28} + ln.types + + + {D471A566-9FB6-41B2-A777-3C32874ECD0E} + ln.logging + + + + \ No newline at end of file diff --git a/odb/Storage.cs b/odb/Storage.cs deleted file mode 100644 index 45b2c21..0000000 --- a/odb/Storage.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -namespace ln.types.odb -{ - //public abstract class Storage - //{ - // public Storage() - // { - // } - - - // public abstract bool Contains(Guid persistenceID); - - // public abstract bool Store(PreparedObject preparedObject); - // public abstract bool Load(PreparedObject preparedObject); - - - - //} -} diff --git a/odb/ng/Document.cs b/odb/ng/Document.cs index 03b72f5..c811f54 100644 --- a/odb/ng/Document.cs +++ b/odb/ng/Document.cs @@ -10,13 +10,19 @@ namespace ln.types.odb.ng private Dictionary properties = new Dictionary(); public Document() - :base(0x1000) + :base(0x1001) { - ID = Guid.NewGuid(); + ID = Guid.NewGuid(); } - public Guid ID { get; } - public DateTime StorageTimeStamp { get; private set; } + public Document(Guid id) + :base(0x1001) + { + ID = id; + } + + public Guid ID { get; } + public DateTime StorageTimeStamp { get; set; } public ODBCollection Collection { get; internal set; } @@ -24,7 +30,7 @@ namespace ln.types.odb.ng : this(documentID, bytes, 0, bytes.Length) { } public Document(Guid documentID,byte[] bytes,int offset,int length) - :this() + :this(documentID) { ID = documentID; @@ -53,9 +59,10 @@ namespace ln.types.odb.ng } set { - if (ODBNull.Instance.Equals(value) && properties.ContainsKey(propName)) + if (ODBNull.Instance.Equals(value)) { - properties.Remove(propName); + if (properties.ContainsKey(propName)) + properties.Remove(propName); } else { @@ -73,11 +80,22 @@ namespace ln.types.odb.ng return !ODBNull.Instance.Equals(this[propName]); } - public override byte[] ToStorage() + public override ODBValue Clone() + { + Document clone = new Document(ID); + foreach (ODBValue fieldName in properties.Keys) + { + clone[fieldName] = this[fieldName].Clone(); + } + return clone; + } + + public override byte[] ToStorage() { MemoryStream stream = new MemoryStream(); BinaryWriter writer = new BinaryWriter(stream); + writer.Write(ID.ToByteArray()); writer.Write(properties.Count); foreach (ODBValue propName in properties.Keys) @@ -101,15 +119,15 @@ namespace ln.types.odb.ng } public override bool Equals(object obj) { - if (obj is ODBDocument) + if (obj is Document) { - ODBDocument you = obj as ODBDocument; + Document you = obj as Document; return ID.Equals(you.ID); } return false; } - public bool ContentEquals(ODBDocument other) + public bool ContentEquals(Document other) { if (object.ReferenceEquals(null, other)) return false; @@ -127,10 +145,10 @@ namespace ln.types.odb.ng public override bool ValueEquals(ODBValue other) { - return ContentEquals(other as ODBDocument); + return ContentEquals(other as Document); } - public int CompareContent(ODBDocument other) + public int CompareContent(Document other) { ODBValue[] keys = Keys.Union(other.Keys).ToArray(); @@ -149,11 +167,11 @@ namespace ln.types.odb.ng public override int CompareInType(ODBValue other) { - return CompareContent(other as ODBDocument); + return CompareContent(other as Document); } public override int CompareValueInType(ODBValue other) { - return CompareContent(other as ODBDocument); + return CompareContent(other as Document); } static Document() diff --git a/odb/ng/IStorage.cs b/odb/ng/IStorage.cs index b7185fb..74d2867 100644 --- a/odb/ng/IStorage.cs +++ b/odb/ng/IStorage.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; + namespace ln.types.odb.ng { public interface IStorage @@ -8,8 +9,8 @@ namespace ln.types.odb.ng void Close(); bool IsOpen { get; } - ODBDocument Load(Guid documentID); - ODBDocument Save(ODBDocument document); + Document Load(Guid documentID); + void Save(Document document); IEnumerable GetDocumentIDs(); } diff --git a/odb/ng/IStorageContainer.cs b/odb/ng/IStorageContainer.cs new file mode 100644 index 0000000..74932fe --- /dev/null +++ b/odb/ng/IStorageContainer.cs @@ -0,0 +1,23 @@ +// /** +// * File: IStorageContainer.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.Collections.Generic; +namespace ln.types.odb.ng +{ + public interface IStorageContainer : IDisposable + { + void Open(); + void Close(); + bool IsOpen { get; } + + IStorage GetStorage(string storageName); + IEnumerable GetStorageNames(); + } +} diff --git a/odb/ng/ODBMapper.API.cs b/odb/ng/ODBMapper.API.cs new file mode 100644 index 0000000..93e8712 --- /dev/null +++ b/odb/ng/ODBMapper.API.cs @@ -0,0 +1,88 @@ +using ln.types.btree; +using System.Collections.Generic; +using System; + +namespace ln.types.odb.ng +{ + + public partial class ODBMapper + { + BTree forwardCache = new BTree(); + Dictionary reverseCache = new Dictionary(); + + public T Load(Guid documentID) => (T)Load(typeof(T), documentID); + public object Load(Type type,Guid documentID) + { + lock (this) + { + if (forwardCache.ContainsKey(documentID)) + return forwardCache[documentID].Instance; + + IStorage storage = StorageContainer.GetStorage(type.FullName); + Document document = storage.Load(documentID); + + object instance = ObjectMapping.UnmapValue(this, document); + + CachedObject cachedObject = new CachedObject(document, instance); + forwardCache.Add(cachedObject.Document.ID,cachedObject); + reverseCache.Add(cachedObject.Instance, cachedObject); + + return cachedObject.Instance; + } + } + + public void Save(T instance) => Save(typeof(T), instance); + public void Save(Type type,object instance) + { + lock (this) + { + IStorage storage = StorageContainer.GetStorage(type.FullName); + CachedObject cachedObject; + Document document; + + if (reverseCache.ContainsKey(instance)) + { + cachedObject = reverseCache[instance]; + document = (GetMapping(type) as mappings.ClassMapping).MapDocument(this, cachedObject.Document.ID, instance); + + storage.Save(document); + + cachedObject.Document = document; + } + else + { + document = (GetMapping(type) as mappings.ClassMapping).MapDocument(this, Guid.NewGuid() ,instance) as Document; + cachedObject = new CachedObject(document, instance); + + storage.Save(document); + + forwardCache.Add(cachedObject.Document.ID, cachedObject); + reverseCache.Add(instance, cachedObject); + } + } + } + + public IEnumerable GetDocumentIDs() => GetDocumentIDs(typeof(T)); + public IEnumerable GetDocumentIDs(Type type) + { + IStorage storage = StorageContainer.GetStorage(type.FullName); + return storage.GetDocumentIDs(); + } + + + + struct CachedObject + { + public object Instance; + public Document Document; + + public CachedObject(Document document, object instance) + { + Document = document; + Instance = instance; + } + } + + } + +} \ No newline at end of file diff --git a/odb/ng/ODBMapper.cs b/odb/ng/ODBMapper.cs index ac8001f..5ac9827 100644 --- a/odb/ng/ODBMapper.cs +++ b/odb/ng/ODBMapper.cs @@ -5,6 +5,9 @@ using System.Reflection; using System.Linq; using System.Collections; using ln.types.net; +using ln.types.odb.ng.storage; +using ln.types.btree; +using ln.types.odb.ng.mappings; namespace ln.types.odb.ng { @@ -13,24 +16,24 @@ namespace ln.types.odb.ng public delegate ODBValue ODBMap(ODBMapper mapper, T value); public delegate T ODBUnmap(ODBMapper mapper, ODBValue oval); - public class ODBMapper + public partial class ODBMapper { public static ODBMapper Default { get; set; } - - public ODB ODB { get; private set; } - - Dictionary objectCollections = new Dictionary(); + public IStorageContainer StorageContainer { get; private set; } + Dictionary mappings = new Dictionary(); + + mappings.ObjectMapping ObjectMapping { get; } - public ODBMapper(string odbPath) - :this(new ODB(odbPath)) + public ODBMapper(string basePath) + :this(new FSStorageContainer(basePath)) { } - public ODBMapper(ODB odb) + public ODBMapper(IStorageContainer storageContainer) { if (Default == null) Default = this; - this.ODB = odb; + this.StorageContainer = storageContainer; RegisterMapping( (mapper, value) => new ODBStringValue(value), @@ -104,10 +107,11 @@ namespace ln.types.odb.ng (mapper, oval) => new IPv4(oval.AsUInt) ); - RegisterMapping(typeof(object),new mappings.ObjectMapping()); + ObjectMapping = new mappings.ObjectMapping(); + RegisterMapping(typeof(object),ObjectMapping); } - public void RegisterMapping(Type nativeType, IODBMapping mapping) + public void RegisterMapping(Type nativeType, IODBMapping mapping) { mappings[nativeType] = mapping; } @@ -123,20 +127,10 @@ namespace ln.types.odb.ng ); } - public ObjectCollection GetCollection(Type type) => GetCollection(type, type.FullName); - public ObjectCollection GetCollection(Type type,string typeName) + public IStorage GetStorage(Type type) => GetStorage(type, type.FullName); + public IStorage GetStorage(Type type,string typeName) { - if (!objectCollections.ContainsKey(typeName)) - { - ObjectCollection objectCollection = Activator.CreateInstance(typeof(ObjectCollection<>).MakeGenericType(type),this) as ObjectCollection; - objectCollections[typeName] = objectCollection; - } - return objectCollections[typeName]; - } - public ObjectCollection GetCollection() where T : class => GetCollection(typeof(T).FullName); - public ObjectCollection GetCollection(string aliasName) where T:class - { - return GetCollection(typeof(T),aliasName) as ObjectCollection; + return StorageContainer.GetStorage(typeName); } public IODBMapping GetMapping() => null; @@ -184,26 +178,29 @@ namespace ln.types.odb.ng } } - 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 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 ODBValue MapValue(object value) { @@ -221,9 +218,9 @@ namespace ln.types.odb.ng if (ODBNull.Instance.Equals(value)) return null; - if (value is ODBDocument) + if (value is Document) { - ODBDocument doc = value as ODBDocument; + Document doc = value as Document; String asmname = doc["__asm__"].AsString; String typename = doc["__type__"].AsString; @@ -270,6 +267,5 @@ namespace ln.types.odb.ng return null; return string.Format("{0}, {1}",type.FullName,type.Assembly.GetName().Name); } - - } + } } diff --git a/odb/ng/ObjectCollection.cs b/odb/ng/ObjectCollection.cs index 59fd776..5ee7999 100644 --- a/odb/ng/ObjectCollection.cs +++ b/odb/ng/ObjectCollection.cs @@ -6,303 +6,303 @@ using System.Collections.Generic; using System.Linq; namespace ln.types.odb.ng { - public class ObjectCollection : IEnumerable - { - public ODBMapper ODBMapper { get; } - public ODBCollection DocumentCollection { get; private set; } + //public class ObjectCollection : IEnumerable + //{ + // public ODBMapper ODBMapper { get; } + // public ODBCollection DocumentCollection { get; private set; } - public Type ElementType { get; } - public String CollectionName => DocumentCollection.CollectionName; + // public Type ElementType { get; } + // public String CollectionName => DocumentCollection.CollectionName; - public int Count => DocumentCollection.Count; + // public int Count => DocumentCollection.Count; - public Type IDType => ODBMapper.GetDocumentIDType(ElementType); + // public Type IDType => ODBMapper.GetDocumentIDType(ElementType); - internal ObjectCollection(ODBMapper odbmapper, Type elementType) - : this(odbmapper, elementType, elementType.FullName) - { } - internal ObjectCollection(ODBMapper odbmapper, Type elementType, String collectionAlias) - { - ODBMapper = odbmapper; - ElementType = elementType; - DocumentCollection = ODBMapper.ODB.GetCollection(elementType.FullName); - } + // internal ObjectCollection(ODBMapper odbmapper, Type elementType) + // : this(odbmapper, elementType, elementType.FullName) + // { } + // internal ObjectCollection(ODBMapper odbmapper, Type elementType, String collectionAlias) + // { + // ODBMapper = odbmapper; + // ElementType = elementType; + // DocumentCollection = ODBMapper.StorageContainer.GetCollection(elementType.FullName); + // } - public object SelectByID(object ID) - { - ODBValue documentID = ODBMapper.MapValue(ID); - return SelectByID(documentID); - } - public object SelectByID(ODBValue documentID) - { - if (ODBNull.Instance.Equals(documentID)) - return null; - lock (this) - { - object o = GetCachedObject(documentID); - if (object.ReferenceEquals(null, o)) - { - ODBDocument document = DocumentCollection.GetDocumentByID(documentID); - o = ODBMapper.UnmapValue(ElementType, document); - TouchCache(documentID, o); - } - return o; - } - } + // public object SelectByID(object ID) + // { + // ODBValue documentID = ODBMapper.MapValue(ID); + // return SelectByID(documentID); + // } + // public object SelectByID(ODBValue documentID) + // { + // if (ODBNull.Instance.Equals(documentID)) + // return null; + // lock (this) + // { + // object o = GetCachedObject(documentID); + // if (object.ReferenceEquals(null, o)) + // { + // ODBDocument document = DocumentCollection.GetDocumentByID(documentID); + // o = ODBMapper.UnmapValue(ElementType, document); + // TouchCache(documentID, o); + // } + // return o; + // } + // } - public IEnumerable Select(Query query) - { - lock (this) - { - return new ObjectEnumeration(this, query.Execute(DocumentCollection).ToArray()); - } - } + // public IEnumerable Select(Query query) + // { + // lock (this) + // { + // return new ObjectEnumeration(this, query.Execute(DocumentCollection).ToArray()); + // } + // } - public bool Ensure(object o) - { - if (!ElementType.IsInstanceOfType(o)) - throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o)); + // public bool Ensure(object o) + // { + // if (!ElementType.IsInstanceOfType(o)) + // throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o)); - lock (this) - { - ODBDocument document = ODBMapper.MapValue(o) as ODBDocument; - if (DocumentCollection.Ensure(document)) - { - TouchCache(document.ID, o); - return true; - } - return false; - } - } + // lock (this) + // { + // ODBDocument document = ODBMapper.MapValue(o) as ODBDocument; + // if (DocumentCollection.Ensure(document)) + // { + // TouchCache(document.ID, o); + // return true; + // } + // return false; + // } + // } - public bool Insert(object o) - { - lock (this) - { - if (!ElementType.IsInstanceOfType(o)) - throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o)); + // public bool Insert(object o) + // { + // lock (this) + // { + // if (!ElementType.IsInstanceOfType(o)) + // throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o)); - ODBDocument document = ODBMapper.MapValue(o) as ODBDocument; - if (DocumentCollection.Insert(document)) - { - TouchCache(document.ID, o); - return true; - } - return false; - } - } - public bool Update(object o) - { - lock (this) - { - lock (this) - { - if (!ElementType.IsInstanceOfType(o)) - throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o)); + // ODBDocument document = ODBMapper.MapValue(o) as ODBDocument; + // if (DocumentCollection.Insert(document)) + // { + // TouchCache(document.ID, o); + // return true; + // } + // return false; + // } + // } + // public bool Update(object o) + // { + // lock (this) + // { + // lock (this) + // { + // if (!ElementType.IsInstanceOfType(o)) + // throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o)); - ODBDocument document = ODBMapper.MapValue(o) as ODBDocument; - if (DocumentCollection.Update(document)) - { - TouchCache(document.ID, o); - return true; - } - return false; - } - } - } - public bool Upsert(object o) - { - if (!ElementType.IsInstanceOfType(o)) - throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o)); + // ODBDocument document = ODBMapper.MapValue(o) as ODBDocument; + // if (DocumentCollection.Update(document)) + // { + // TouchCache(document.ID, o); + // return true; + // } + // return false; + // } + // } + // } + // public bool Upsert(object o) + // { + // if (!ElementType.IsInstanceOfType(o)) + // throw new ArgumentException(String.Format("Object needs to be of type {0}", ElementType.FullName), nameof(o)); - lock (this) - { - ODBDocument document = ODBMapper.MapValue(o) as ODBDocument; - if (DocumentCollection.Upsert(document)) - { - TouchCache(document.ID, o); - return true; - } - return false; - } - } + // lock (this) + // { + // ODBDocument document = ODBMapper.MapValue(o) as ODBDocument; + // if (DocumentCollection.Upsert(document)) + // { + // TouchCache(document.ID, o); + // return true; + // } + // return false; + // } + // } - public bool Delete(object o) => Delete(ODBMapper.MapValue(ODBMapper.GetDocumentID(o))); - public bool Delete(ODBValue documentID) - { - lock (this) - { - if (DocumentCollection.Delete(documentID)) - { - if (objectCache.ContainsKey(documentID)) - objectCache.Remove(documentID); - return true; - } - return false; - } - } + // public bool Delete(object o) => Delete(ODBMapper.MapValue(ODBMapper.GetDocumentID(o))); + // public bool Delete(ODBValue documentID) + // { + // lock (this) + // { + // if (DocumentCollection.Delete(documentID)) + // { + // if (objectCache.ContainsKey(documentID)) + // objectCache.Remove(documentID); + // return true; + // } + // return false; + // } + // } - public bool HasProperty(string propName) - { - propName = IndexPath.TranslatePropertyPath(ElementType, propName); + // public bool HasProperty(string propName) + // { + // propName = IndexPath.TranslatePropertyPath(ElementType, propName); - ClassMapping classMapping = ODBMapper.GetMapping(ElementType) as ClassMapping; - if (classMapping != null) - { - return classMapping.HasField(propName); - } - return false; - } + // ClassMapping classMapping = ODBMapper.GetMapping(ElementType) as ClassMapping; + // if (classMapping != null) + // { + // return classMapping.HasField(propName); + // } + // return false; + // } - /* Indeces */ - public void EnsureIndex(string propertyPath, bool unique = false) - { - //string translatedPath = IndexPath.TranslatePropertyPath(ElementType, propertyPath); - //DocumentCollection.EnsureIndex(translatedPath, translatedPath); - EnsureIndeces(false, new string[] { propertyPath }); - } - public void EnsureIndeces(params string[] propertyPaths) => EnsureIndeces(false, propertyPaths); - public void EnsureIndeces(bool unique, params string[] propertyPaths) - { - for (int n = 0; n < propertyPaths.Length; n++) - propertyPaths[n] = IndexPath.TranslatePropertyPath(ElementType, propertyPaths[n]); + // /* Indeces */ + // public void EnsureIndex(string propertyPath, bool unique = false) + // { + // //string translatedPath = IndexPath.TranslatePropertyPath(ElementType, propertyPath); + // //DocumentCollection.EnsureIndex(translatedPath, translatedPath); + // EnsureIndeces(false, new string[] { propertyPath }); + // } + // public void EnsureIndeces(params string[] propertyPaths) => EnsureIndeces(false, propertyPaths); + // public void EnsureIndeces(bool unique, params string[] propertyPaths) + // { + // for (int n = 0; n < propertyPaths.Length; n++) + // propertyPaths[n] = IndexPath.TranslatePropertyPath(ElementType, propertyPaths[n]); - DocumentCollection.EnsureIndeces(propertyPaths, false); - } + // DocumentCollection.EnsureIndeces(propertyPaths, false); + // } - public void EnsureUniqueness(params string[] propertyPaths) - { - for (int n = 0; n < propertyPaths.Length; n++) - propertyPaths[n] = IndexPath.TranslatePropertyPath(ElementType, propertyPaths[n]); + // public void EnsureUniqueness(params string[] propertyPaths) + // { + // for (int n = 0; n < propertyPaths.Length; n++) + // propertyPaths[n] = IndexPath.TranslatePropertyPath(ElementType, propertyPaths[n]); - DocumentCollection.EnsureUniqueness(propertyPaths); - } + // DocumentCollection.EnsureUniqueness(propertyPaths); + // } - /* Object Cache */ - public bool UseStrongCache { get; private set; } - Dictionary objectCache = new Dictionary(); + // /* Object Cache */ + // public bool UseStrongCache { get; private set; } + // Dictionary objectCache = new Dictionary(); - public void EnableStrongCache(bool enable) - { - lock (this) - { - if (!enable) - { - foreach (ODBValue key in objectCache.Keys.ToArray()) - { - if (!(objectCache[key] is WeakReference)) - objectCache.Remove(key); - } - } - else - { - foreach (ODBValue key in objectCache.Keys.ToArray()) - { - if ((objectCache[key] is WeakReference)) - objectCache[key] = (objectCache[key] as WeakReference).Target; - } - } - } - } + // public void EnableStrongCache(bool enable) + // { + // lock (this) + // { + // if (!enable) + // { + // foreach (ODBValue key in objectCache.Keys.ToArray()) + // { + // if (!(objectCache[key] is WeakReference)) + // objectCache.Remove(key); + // } + // } + // else + // { + // foreach (ODBValue key in objectCache.Keys.ToArray()) + // { + // if ((objectCache[key] is WeakReference)) + // objectCache[key] = (objectCache[key] as WeakReference).Target; + // } + // } + // } + // } - private object GetCachedObject(ODBValue documentID) - { - if (objectCache.ContainsKey(documentID)) - { - object o = objectCache[documentID]; - if (o is WeakReference) - { - WeakReference weak = o as WeakReference; - if (weak.IsAlive) - return weak.Target; - else - return null; - } - return o; - } - return null; - } + // private object GetCachedObject(ODBValue documentID) + // { + // if (objectCache.ContainsKey(documentID)) + // { + // object o = objectCache[documentID]; + // if (o is WeakReference) + // { + // WeakReference weak = o as WeakReference; + // if (weak.IsAlive) + // return weak.Target; + // else + // return null; + // } + // return o; + // } + // return null; + // } - private void TouchCache(ODBValue documentID, object o) - { - if (object.ReferenceEquals(o, null) && objectCache.ContainsKey(documentID)) - { - objectCache.Remove(documentID); - } - else if (!object.ReferenceEquals(o, null)) - { - if (UseStrongCache) - objectCache[documentID] = o; - else - objectCache[documentID] = new WeakReference(o); - } - } + // private void TouchCache(ODBValue documentID, object o) + // { + // if (object.ReferenceEquals(o, null) && objectCache.ContainsKey(documentID)) + // { + // objectCache.Remove(documentID); + // } + // else if (!object.ReferenceEquals(o, null)) + // { + // if (UseStrongCache) + // objectCache[documentID] = o; + // else + // objectCache[documentID] = new WeakReference(o); + // } + // } - public object[] GetDocumentIDs() - { - return DocumentCollection.Index.Select((arg) => ODBMapper.UnmapValue(IDType, arg)).ToArray(); - } + // public object[] GetDocumentIDs() + // { + // return DocumentCollection.Index.Select((arg) => ODBMapper.UnmapValue(IDType, arg)).ToArray(); + // } - public IEnumerable GetEnumeration() - { - lock (this) - { - return new ObjectEnumeration(this, DocumentCollection.Index.ToArray()); - } - } + // public IEnumerable GetEnumeration() + // { + // lock (this) + // { + // return new ObjectEnumeration(this, DocumentCollection.Index.ToArray()); + // } + // } - public IEnumerator GetEnumerator() - { - return GetEnumeration().GetEnumerator(); - } + // public IEnumerator GetEnumerator() + // { + // return GetEnumeration().GetEnumerator(); + // } - public void Close() - { - DocumentCollection = null; - } + // public void Close() + // { + // DocumentCollection = null; + // } - class ObjectEnumeration : IEnumerable - { - ObjectCollection collection; - IEnumerable documentIDs; + // class ObjectEnumeration : IEnumerable + // { + // ObjectCollection collection; + // IEnumerable documentIDs; - public ObjectEnumeration(ObjectCollection collection,IEnumerable documentIDs) - { - this.collection = collection; - this.documentIDs = documentIDs; - } + // public ObjectEnumeration(ObjectCollection collection,IEnumerable documentIDs) + // { + // this.collection = collection; + // this.documentIDs = documentIDs; + // } - public IEnumerator GetEnumerator() - { - foreach (ODBValue documentID in this.documentIDs) - { - yield return this.collection.SelectByID(documentID); - } - } - } + // public IEnumerator GetEnumerator() + // { + // foreach (ODBValue documentID in this.documentIDs) + // { + // yield return this.collection.SelectByID(documentID); + // } + // } + // } - } + //} - public class ObjectCollection : ObjectCollection where T:class - { - public ObjectCollection(ODBMapper odbmapper) - :base(odbmapper,typeof(T)) - {} + //public class ObjectCollection : ObjectCollection where T:class + //{ + // public ObjectCollection(ODBMapper odbmapper) + // :base(odbmapper,typeof(T)) + // {} - public IEnumerable SelectQuery(Query query) => base.Select(query).Cast(); - public T Select(object id) => (T)base.SelectByID(id); + // public IEnumerable SelectQuery(Query query) => base.Select(query).Cast(); + // public T Select(object id) => (T)base.SelectByID(id); - public bool Ensure(T o) => base.Ensure(o); - public bool Insert(T o) => base.Insert(o); - public bool Update(T o) => base.Update(o); - public bool Upsert(T o) => base.Upsert(o); - public void Delete(T o) => base.Delete(o); - } + // public bool Ensure(T o) => base.Ensure(o); + // public bool Insert(T o) => base.Insert(o); + // public bool Update(T o) => base.Update(o); + // public bool Upsert(T o) => base.Upsert(o); + // public void Delete(T o) => base.Delete(o); + //} } diff --git a/odb/ng/Reference.cs b/odb/ng/Reference.cs index 2d482f7..8f89627 100644 --- a/odb/ng/Reference.cs +++ b/odb/ng/Reference.cs @@ -1,36 +1,36 @@ using System; namespace ln.types.odb.ng { - public class Reference where T:class - { - ODBMapper mapper; - ObjectCollection Collection => mapper.GetCollection(); + //public class Reference where T:class + //{ + // ODBMapper mapper; + // ObjectCollection Collection => mapper.GetCollection(); - internal object valueID; + // internal object valueID; - public Reference(ODBMapper mapper) - { - this.mapper = mapper; - } - public Reference(ODBMapper mapper,T value) - { - this.mapper = mapper; - this.Value = value; - } + // public Reference(ODBMapper mapper) + // { + // this.mapper = mapper; + // } + // public Reference(ODBMapper mapper,T value) + // { + // this.mapper = mapper; + // this.Value = value; + // } - public T Value - { - get - { - if (this.valueID == null) - return null; - return this.Collection.Select(valueID); - } - set - { - this.Collection.Ensure(value); - this.valueID = this.mapper.GetDocumentID(value); - } - } - } + // public T Value + // { + // get + // { + // if (this.valueID == null) + // return null; + // return this.Collection.Select(valueID); + // } + // set + // { + // this.Collection.Ensure(value); + // this.valueID = this.mapper.GetDocumentID(value); + // } + // } + //} } diff --git a/odb/ng/Session.cs b/odb/ng/Session.cs new file mode 100644 index 0000000..cfa6559 --- /dev/null +++ b/odb/ng/Session.cs @@ -0,0 +1,163 @@ +using System; +using System.Collections.Generic; +using ln.types.odb.values; +using ln.logging; +namespace ln.types.odb.ng +{ + public class Session : IStorageContainer + { + public IStorageContainer StorageContainer { get; private set; } + public ODBMapper ODBMapper { get; private set; } + + public bool IsOpen => StorageContainer.IsOpen; + + public Session(IStorageContainer storageContainer) + { + StorageContainer = storageContainer; + ODBMapper = ODBMapper.Default; + } + + private Dictionary storages = new Dictionary(); + public IStorage GetStorage(string storageName) + { + if (storages.ContainsKey(storageName)) + return storages[storageName]; + + IStorage storage = StorageContainer.GetStorage(storageName); + storages.Add(storageName, new SessionCache(storage)); + + if (!storage.IsOpen) + storage.Open(); + + return storages[storageName]; + } + + public void Open() + { + StorageContainer.Open(); + } + + public void Close() + { + StorageContainer.Close(); + } + + public IEnumerable GetStorageNames() + { + return StorageContainer.GetStorageNames(); + } + + public void Dispose() + { + } + + class SessionCache : IStorage + { + public IStorage Storage { get; } + public bool IsOpen => Storage.IsOpen; + + Dictionary documentCache = new Dictionary(); + + public SessionCache(IStorage storage) + { + Storage = storage; + } + + public bool Open() + { + return Storage.Open(); + } + + public void Close() + { + Storage.Close(); + } + + public Document Load(Guid documentID) + { + lock (this) + { + if (!documentCache.ContainsKey(documentID)) + { + Document cacheDocument = Storage.Load(documentID); + documentCache.Add(documentID, new CachedDocument(cacheDocument)); + } + return documentCache[documentID].WorkingCopy; + } + } + + public void Save(Document document) + { + lock (this) + { + if (!documentCache.ContainsKey(document.ID)) + { + Logging.Log(LogLevel.DEBUG, "SessionCache: Save(): saving new Document {0}",document.ID); + Storage.Save(document); + documentCache.Add(document.ID, new CachedDocument(document.Clone() as Document,document)); + } + else + { + lock (Storage) + { + Document storageDocument = Storage.Load(document.ID); + Document cacheDocument = documentCache[document.ID].CachedCopy; + bool changedFlag = false; + + foreach (ODBValue propertyName in document.Keys) + { + if (!document[propertyName].Equals(cacheDocument[propertyName])) + { + Logging.Log(LogLevel.DEBUG, "SessionCache: Save(): found changed field for Document {0}: {1}={2}", document.ID, propertyName, document[propertyName]); + storageDocument[propertyName] = document[propertyName]; + changedFlag = true; + } + } + foreach (ODBValue propertyName in cacheDocument.Keys) + { + if (!document.Contains(propertyName)) + { + storageDocument[propertyName] = ODBNull.Instance; + changedFlag = true; + } + } + + if (changedFlag) + { + Storage.Save(storageDocument); + documentCache[document.ID] = new CachedDocument(document.Clone() as Document,document); + } + else + { + Logging.Log(LogLevel.DEBUG, "SessionCache: Save(): No changes to be saved for document {0}",document.ID); + } + } + } + } + } + + public IEnumerable GetDocumentIDs() + { + return Storage.GetDocumentIDs(); + } + + struct CachedDocument + { + public Document CachedCopy; + public Document WorkingCopy; + + public CachedDocument(Document cachedCopy) + { + CachedCopy = cachedCopy; + WorkingCopy = cachedCopy.Clone() as Document; + } + public CachedDocument(Document cachedCopy,Document workingCopy) + { + CachedCopy = cachedCopy; + WorkingCopy = workingCopy; + } + } + } + + } +} diff --git a/odb/ng/mappings/ClassMapping.cs b/odb/ng/mappings/ClassMapping.cs index d7363a5..9dae66a 100644 --- a/odb/ng/mappings/ClassMapping.cs +++ b/odb/ng/mappings/ClassMapping.cs @@ -16,8 +16,8 @@ namespace ln.types.odb.ng.mappings public Type MappedType { get; } - public GetID getID { get; private set; } = (o) => Guid.NewGuid(); - public Type IDType { get; private set; } + //public GetID getID { get; private set; } = (o) => Guid.NewGuid(); + //public Type IDType { get; private set; } List mappedFields = new List(); @@ -36,20 +36,20 @@ namespace ln.types.odb.ng.mappings if (fieldinfo.GetCustomAttribute() == null) { mappedFields.Add(fieldinfo); - if (fieldinfo.GetCustomAttribute() != null) - { - getID = (o) => fieldinfo.GetValue(o); - IDType = fieldinfo.FieldType; - } + //if (fieldinfo.GetCustomAttribute() != null) + //{ + // getID = (o) => fieldinfo.GetValue(o); + // IDType = fieldinfo.FieldType; + //} } } foreach (PropertyInfo propInfo in type.GetProperties(BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public)) { - if (propInfo.GetCustomAttribute() != null) - { - getID = (o) => propInfo.GetValue(o); - IDType = propInfo.PropertyType; - } + //if (propInfo.GetCustomAttribute() != null) + //{ + // getID = (o) => propInfo.GetValue(o); + // IDType = propInfo.PropertyType; + //} } if ((type != null) && !type.IsValueType && (!typeof(object).Equals(type.BaseType))) @@ -68,7 +68,7 @@ namespace ln.types.odb.ng.mappings public object UnmapValue(ODBMapper mapper,ODBValue oval) { - ODBDocument document = oval as ODBDocument; + Document document = oval as Document; object o = Activator.CreateInstance(MappedType, true); foreach (FieldInfo fieldInfo in mappedFields) @@ -76,12 +76,13 @@ namespace ln.types.odb.ng.mappings ByReferenceAttribute byReferenceAttribute = fieldInfo.GetCustomAttribute(); if (byReferenceAttribute != null) { - Type ceType = byReferenceAttribute.ElementType ?? fieldInfo.FieldType; - String cAlias = byReferenceAttribute.Alias ?? ceType.FullName; + // ToDo: Implement Referentiel Mapping + //Type ceType = byReferenceAttribute.ElementType ?? fieldInfo.FieldType; + //String cAlias = byReferenceAttribute.Alias ?? ceType.FullName; - ObjectCollection objectCollection = mapper.GetCollection(ceType, cAlias); - ODBValue refID = document[fieldInfo.Name]; - fieldInfo.SetValue(o, objectCollection.SelectByID(refID)); + //ObjectCollection objectCollection = mapper.GetStorage(ceType, cAlias); + //ODBValue refID = document[fieldInfo.Name]; + //fieldInfo.SetValue(o, objectCollection.SelectByID(refID)); } else { @@ -104,53 +105,50 @@ namespace ln.types.odb.ng.mappings return o; } - public ODBValue MapValue(ODBMapper mapper,object value) + public Document MapDocument(ODBMapper mapper,Guid documentID,object value) + { + Document document = new Document(documentID); + document["__asm__"] = value.GetType().Assembly.GetName().Name; + document["__type__"] = value.GetType().FullName; + + foreach (FieldInfo fieldInfo in mappedFields) + { + object fv = fieldInfo.GetValue(value); + ODBValue ov = null; + + if (fieldInfo.GetCustomAttribute() != 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 ODBValue MapValue(ODBMapper mapper,object value) { if (Object.ReferenceEquals(value, null)) return ODBNull.Instance; - - ODBDocument document = new ODBDocument(); - document["__asm__"] = value.GetType().Assembly.GetName().Name; - document["__type__"] = value.GetType().FullName; - - foreach (FieldInfo fieldInfo in mappedFields) - { - object fv = fieldInfo.GetValue(value); - ODBValue ov = null; - - if (fieldInfo.GetCustomAttribute() != null) - { - if (!object.ReferenceEquals(fv, null)) - { - mapper.GetCollection(fieldInfo.FieldType).Ensure(fv); - ov = mapper.MapValue(mapper.GetDocumentID(fv)); - } - else - ov = ODBNull.Instance; - } - else - { - ov = mapper.MapValue(fv); - } - document[fieldInfo.Name] = ov; - } - - document.ID = mapper.MapValue(getID(value)); - - return document; + + return MapDocument(mapper, Guid.Empty, value); } public Type GetFieldType(ODBMapper mapper,string fieldName) { foreach (FieldInfo fieldInfo in mappedFields) if (fieldInfo.Name.Equals(fieldName)) - if (fieldInfo.GetCustomAttribute() != null) - { - return mapper.GetDocumentIDType(fieldInfo.FieldType); - } else - { - return fieldInfo.FieldType; - } + return fieldInfo.FieldType; throw new KeyNotFoundException(); } @@ -162,14 +160,14 @@ namespace ln.types.odb.ng.mappings public ODBValue MapValue(ODBMapper mapper, object value) { - return new ODBDocument(); + return new Document(); } public object UnmapValue(ODBMapper mapper, ODBValue oval) { - if (oval is ODBDocument) + if (oval is Document) { - ODBDocument document = oval.AsDocument; + Document document = oval as Document; if (!document.Contains("__type__")) return new object(); diff --git a/odb/ng/mappings/DictionaryMapping.cs b/odb/ng/mappings/DictionaryMapping.cs index b04c90b..af0f1f1 100644 --- a/odb/ng/mappings/DictionaryMapping.cs +++ b/odb/ng/mappings/DictionaryMapping.cs @@ -20,13 +20,13 @@ namespace ln.types.odb.ng.mappings if (dType.GetInterfaces().Contains(typeof(IDictionary))) { IDictionary dictionary = value as IDictionary; - ODBDocument document = new ODBDocument(); + Document document = new Document(); document["__asm__"] = value.GetType().Assembly.GetName().Name; document["__type__"] = value.GetType().FullName; - ODBDocument kTypes = new ODBDocument(); - ODBDocument vTypes = new ODBDocument(); + Document kTypes = new Document(); + Document vTypes = new Document(); document["__ktypes__"] = kTypes; document["__vtypes__"] = vTypes; @@ -47,7 +47,7 @@ namespace ln.types.odb.ng.mappings public object UnmapValue(ODBMapper mapper, ODBValue oval) { - ODBDocument document = oval.AsDocument; + Document document = oval as Document; Type dType = Type.GetType(String.Format("{0}, {1}",document["__type__"].AsString,document["__asm__"].AsString)); //;Assembly.Load(document["__asm__"].AsString).GetType(document["__type__"].AsString); if (dType.IsGenericType) @@ -59,8 +59,8 @@ namespace ln.types.odb.ng.mappings Type kType = dType.GetGenericArguments()[0]; Type vType = dType.GetGenericArguments()[1]; - ODBDocument ktypes = document.Contains("__ktypes__") ? document["__ktypes__"].AsDocument : new ODBDocument(); - ODBDocument vtypes = document.Contains("__vtypes__") ? document["__vtypes__"].AsDocument : new ODBDocument(); + Document ktypes = document.Contains("__ktypes__") ? document["__ktypes__"] as Document : new Document(); + Document vtypes = document.Contains("__vtypes__") ? document["__vtypes__"] as Document : new Document(); foreach (ODBValue key in document.Keys) { diff --git a/odb/ng/storage/FSStorage.cs b/odb/ng/storage/FSStorage.cs new file mode 100644 index 0000000..4261add --- /dev/null +++ b/odb/ng/storage/FSStorage.cs @@ -0,0 +1,292 @@ +// /** +// * File: FSSTorage.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.Collections.Generic; +using System.IO; +using System.Linq; +using System.Xml.Serialization.Advanced; +using ln.logging; + +namespace ln.types.odb.ng.storage +{ + /** + * FSStorage + * + * Directory Layout: + * + * / + * /data.odb Serialized Document Data + * /data.idx Serialized Lookup Index for Documents and Free Areas + * + * data.odb + * ---------- + * 0000 4 MAGIC Bytes + * 0004 4 Version + * 0008 8 LastCloseTimestamp + * 0010 4 FirstOffset + * 0014 4 GranularWidth + * 0018 8 Reserved 0 + * + **/ + public class FSStorage : IStorage + { + public byte[] MagicBytes { get; } = new byte[] { 0x0F, 0x0E, 0x0D, 0x0A }; + + public String StoragePath { get; } + public String StorageName { get; } + + public int FileVersion { get; private set; } + public long LastCloseTimestamp { get; private set; } + public int FirstOffset { get; private set; } + + public int GranularWidth { get; private set; } = 12; + public int GranularityMask => (1 << GranularWidth) - 1; + + public int AppendOffset { get; private set; } + + StorageAreaContainer storageAreas = new StorageAreaContainer(); + Dictionary documentAreas = new Dictionary(); + + FileStream fileStream; + + public FSStorage(string storagePath) + { + StoragePath = storagePath; + StorageName = Path.GetFileName(storagePath); + } + public FSStorage(string storagePath,int granularWidth) + :this(storagePath) + { + GranularWidth = granularWidth; + } + + private void AssertOpen() + { + if (fileStream == null) + throw new IOException("FSStorage not opened"); + } + + public bool IsOpen => (fileStream != null); + public bool Open() + { + if (!IsOpen) + { + try + { + if (!Directory.Exists(StoragePath)) + Directory.CreateDirectory(StoragePath); + + fileStream = new FileStream(Path.Combine(StoragePath, "data.odb"), FileMode.OpenOrCreate); + + if (fileStream.Length == 0) + { + FileVersion = 0; + LastCloseTimestamp = 0; + FirstOffset = (1 << GranularWidth); + if (FirstOffset < 0x20) + throw new NotSupportedException("Granularity too small"); + + AppendOffset = FirstOffset; + + Close(); + return Open(); + } + else + { + if (!fileStream.ReadBytes(4).SequenceEqual(MagicBytes)) + throw new IOException("Magic bytes do not match"); + + FileVersion = fileStream.ReadInteger(); + LastCloseTimestamp = fileStream.ReadLong(); + FirstOffset = fileStream.ReadInteger(); + GranularWidth = fileStream.ReadInteger(); + + Scan(); + } + } + catch (Exception e) + { + Logging.Log(e); + if (fileStream != null) + { + fileStream.Close(); + fileStream.Dispose(); + fileStream = null; + } + return false; + } + + return true; + } + return false; + } + + private void Scan() + { + int offset = FirstOffset; + + while (offset < fileStream.Length) + { + fileStream.Position = offset; + StorageArea storageArea = new StorageArea(offset, fileStream.ReadInteger()); + Guid documentID = new Guid(fileStream.ReadBytes(16)); + if (Guid.Empty.Equals(documentID)) + storageAreas.Push(storageArea); + else + { + if (documentAreas.ContainsKey(documentID)) + { + Document previousDoc = LoadDocument(documentAreas[documentID]); + Document currentDoc = LoadDocument(storageArea); + + if (previousDoc.StorageTimeStamp < currentDoc.StorageTimeStamp) + { + WriteStorageArea(documentAreas[documentID]); + storageAreas.Push(documentAreas[documentID]); + documentAreas[documentID] = storageArea; + } + else + { + WriteStorageArea(storageArea); + storageAreas.Push(storageArea); + } + } else + { + documentAreas.Add(documentID, storageArea); + } + } + offset = storageArea.NextOffset; + } + + AppendOffset = offset; + } + + public void Close() + { + lock (this){ + AssertOpen(); + + fileStream.Position = 0; + fileStream.WriteBytes(MagicBytes); + fileStream.WriteInteger(FileVersion); + LastCloseTimestamp = (long)DateTime.Now.ToUnixTimeMilliseconds(); + fileStream.WriteLong(LastCloseTimestamp); + fileStream.WriteInteger(FirstOffset); + fileStream.WriteInteger(GranularWidth); + + fileStream.Close(); + fileStream.Dispose(); + fileStream = null; + } + } + + public IEnumerable GetDocumentIDs() + { + lock (this) + { + return documentAreas.Keys.ToArray(); + } + } + + public Document Load(Guid documentID) + { + lock (this) + { + if (!documentAreas.ContainsKey(documentID)) + throw new KeyNotFoundException(); + + StorageArea storageArea = documentAreas[documentID]; + return LoadDocument(storageArea); + } + } + + private Document LoadDocument(StorageArea storageArea) + { + fileStream.Position = storageArea.Offset + 4; + Guid documentID = new Guid(fileStream.ReadBytes(16)); + byte[] storageBytes = fileStream.ReadBytes(storageArea.Size - 20); + + return new Document(documentID, storageBytes); + } + + public void Save(Document document) + { + lock (this) + { + document.StorageTimeStamp = DateTime.Now; + byte[] storageBytes = document.ToStorage(); + StorageArea storageArea = storageAreas.Pop(storageBytes.Length + 20); + if (storageArea == null) + storageArea = AppendStorageArea(storageBytes.Length + 20); + + int neededSize = storageBytes.Length + 20; + CheckGranularity(ref neededSize); + + if (storageArea.Size > neededSize) + { + StorageArea splitArea = storageArea.Split(storageArea.Size - neededSize); + WriteStorageArea(splitArea); + } + WriteStorageArea(storageArea, storageBytes); + + if (documentAreas.ContainsKey(document.ID)) + { + StorageArea oldStorageArea = documentAreas[document.ID]; + WriteStorageArea(oldStorageArea); + storageAreas.Push(oldStorageArea); + } + documentAreas[document.ID] = storageArea; + } + } + + public void Delete(Guid documentID) + { + lock (this) + { + if (documentAreas.ContainsKey(documentID)) + { + StorageArea storageArea = documentAreas[documentID]; + + documentAreas.Remove(documentID); + storageArea = storageAreas.Push(storageArea); + WriteStorageArea(storageArea); + } + } + } + + private StorageArea AppendStorageArea(int size) + { + CheckGranularity(ref size); + return new StorageArea(AppendOffset, size); + } + + private void WriteStorageArea(StorageArea storageArea) => WriteStorageArea(storageArea, new byte[0]); + private void WriteStorageArea(StorageArea storageArea,byte[] data) + { + AssertOpen(); + + if (data.Length > (storageArea.Size - 4)) + throw new ArgumentOutOfRangeException(nameof(data)); + + fileStream.Position = storageArea.Offset; + fileStream.WriteInteger(storageArea.Size); + fileStream.WriteBytes(data); + fileStream.WriteBytes(new byte[ storageArea.Size - 4 - data.Length]); + } + + private void CheckGranularity(ref int i) + { + i = (i + GranularityMask) & ~GranularityMask; + } + + + + } +} diff --git a/odb/ng/storage/FSStorageContainer.cs b/odb/ng/storage/FSStorageContainer.cs new file mode 100644 index 0000000..c71cc4b --- /dev/null +++ b/odb/ng/storage/FSStorageContainer.cs @@ -0,0 +1,97 @@ +// /** +// * File: FSStorage.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.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ln.types.odb.ng.storage +{ + public class FSStorageContainer : IStorageContainer,IDisposable + { + public string BasePath { get; } + + FileStream lockFile; + Dictionary storages = new Dictionary(); + + public FSStorageContainer(string basePath) + { + BasePath = basePath; + } + public bool IsOpen => lockFile != null; + + private void AssertOpen() + { + if (!IsOpen) + throw new IOException("FSSTorage not open"); + } + + public void Close() + { + lock (this) + { + AssertOpen(); + if (lockFile != null) + { + lockFile.Close(); + lockFile.Dispose(); + lockFile = null; + } + } + } + + public IStorage GetStorage(string storageName) + { + lock (this) + { + AssertOpen(); + + if (!storages.ContainsKey(storageName)) + storages.Add(storageName, new FSStorage(Path.Combine(BasePath, storageName))); + return storages[storageName]; + } + } + + public IEnumerable GetStorageNames() + { + lock (this) + { + AssertOpen(); + return storages.Keys; + } + } + + public void Open() + { + lock (this) + { + if (!IsOpen) + { + if (!Directory.Exists(BasePath)) + Directory.CreateDirectory(BasePath); + + lockFile = new FileStream(Path.Combine(BasePath, ".lock"), FileMode.CreateNew, FileAccess.ReadWrite, FileShare.None, 1024, FileOptions.DeleteOnClose); + + foreach (String storagePath in Directory.EnumerateDirectories(BasePath)) + { + FSStorage storage = new FSStorage(storagePath); + storages.Add(storage.StorageName, storage); + } + } + } + } + + public void Dispose() + { + if (IsOpen) + Close(); + } + } +} diff --git a/odb/ng/storage/StorageArea.cs b/odb/ng/storage/StorageArea.cs new file mode 100644 index 0000000..0a3c3ff --- /dev/null +++ b/odb/ng/storage/StorageArea.cs @@ -0,0 +1,38 @@ +// /** +// * File: StorageArea.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; +namespace ln.types.odb.ng.storage +{ + public class StorageArea + { + public int Offset { get; } + public int Size { get; set; } + + public int NextOffset => Offset + Size; + + public StorageArea(int offset,int size) + { + Offset = offset; + Size = size; + } + + public StorageArea Split(int splitSize) + { + if (splitSize >= Size) + throw new ArgumentOutOfRangeException(nameof(splitSize)); + + StorageArea splitArea = new StorageArea(Offset + Size - splitSize,splitSize); + Size -= splitSize; + + return splitArea; + } + + } +} diff --git a/odb/ng/storage/StorageAreaContainer.cs b/odb/ng/storage/StorageAreaContainer.cs new file mode 100644 index 0000000..d59c8b1 --- /dev/null +++ b/odb/ng/storage/StorageAreaContainer.cs @@ -0,0 +1,69 @@ +// /** +// * File: StorageAreaContainer.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 ln.types.btree; +namespace ln.types.odb.ng.storage +{ + public class StorageAreaContainer + { + public int SplitLimit { get; set; } = 32; + + MappingBTree storageAreas = new MappingBTree((value)=>value.Offset); + + public StorageAreaContainer() + { + } + + public StorageArea Push(StorageArea storageArea) + { + storageAreas.Add(storageArea); + + try + { + StorageArea previousStorageArea = storageAreas.Previous(storageArea); + if ((previousStorageArea != null) && (previousStorageArea.NextOffset == storageArea.Offset)) + { + previousStorageArea.Size += storageArea.Size; + storageAreas.Remove(storageArea); + storageArea = previousStorageArea; + } + } catch + { + } + + try + { + StorageArea nextStorageArea = storageAreas.Next(storageArea); + if ((nextStorageArea != null) && (storageArea.NextOffset == nextStorageArea.Offset)) + { + storageArea.Size += nextStorageArea.Size; + storageAreas.Remove(nextStorageArea); + } + } catch + { } + + return storageArea; + } + + public StorageArea Pop(int minSize) + { + foreach (StorageArea storageArea in storageAreas) + { + if (storageArea.Size >= minSize) + { + storageAreas.RemoveKey(storageArea.Offset); + return storageArea; + } + } + return null; + } + + } +} diff --git a/odb/values/ODBList.cs b/odb/values/ODBList.cs index 656c589..827b0c2 100644 --- a/odb/values/ODBList.cs +++ b/odb/values/ODBList.cs @@ -43,7 +43,14 @@ namespace ln.types.odb.values public int Count => items.Count; - public override object Value { + public override ODBValue Clone() + { + ODBList clone = new ODBList(); + clone.items.AddRange(this.items); + return clone; + } + + public override object Value { get { object[] a = new object[items.Count]; diff --git a/odb/values/ODBValue.cs b/odb/values/ODBValue.cs index 66dcb3e..f88b45c 100644 --- a/odb/values/ODBValue.cs +++ b/odb/values/ODBValue.cs @@ -94,7 +94,13 @@ namespace ln.types.odb.values public virtual ODBDocument AsDocument => (ODBDocument)this; - public virtual void Store(BinaryWriter storage) + public virtual ODBValue Clone() + { + return this; + } + + + public virtual void Store(BinaryWriter storage) { byte[] storageBytes = ToStorage(); @@ -221,5 +227,21 @@ namespace ln.types.odb.values { return a.CompareValueTo(b); } - } + + static ODBValue() + { + new ODBDocument(); + new ODBNull(); + new ODBStringValue(); + new ODBBool(); + new ODBList(); + new ODBInteger(); + new ODBUInteger(); + new ODBLong(); + new ODBULong(); + new ODBDouble(); + new ODBGuid(); + new ODBTypedValue(); + } + } }