fix and improve persistence / storage functionality complete with unit tests
This commit is contained in:
parent
0c7abc9a13
commit
39cdecd9f4
@ -18,7 +18,6 @@
|
||||
namespace Mocha.Core;
|
||||
|
||||
using System.Reflection;
|
||||
using System.Runtime.Serialization;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
@ -340,11 +339,16 @@ public abstract class Oms
|
||||
ValidateConstraintsForRelationship(source.GetHandle(), relationship, targets);
|
||||
AssignRelationshipInternal(source.GetHandle(), relationship, targets, dt);
|
||||
|
||||
if (!_InhibitStorage)
|
||||
if (!InhibitStorageProcessing)
|
||||
{
|
||||
List<InstanceReference> list = new List<InstanceReference>();
|
||||
foreach (InstanceHandle ih in targets)
|
||||
{
|
||||
list.Add(new InstanceReference(GetGlobalIdentifier(ih)));
|
||||
}
|
||||
foreach (OmsStorage sto in Storages)
|
||||
{
|
||||
sto.WriteRelationship(this, source.GetHandle(), relationship, targets, dt);
|
||||
sto.WriteRelationship(new InstanceReference(GetGlobalIdentifier(source)), new InstanceReference(GetGlobalIdentifier(relationship)), list, dt, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -447,11 +451,11 @@ public abstract class Oms
|
||||
|
||||
//? this also means that we need to manually loop through storages and update the siblings
|
||||
//? if I had a nickel for every time I called AssignRelationshipInternal,... I'd have two nickels
|
||||
if (!_InhibitStorage)
|
||||
if (!InhibitStorageProcessing)
|
||||
{
|
||||
foreach (OmsStorage sto in Storages)
|
||||
{
|
||||
sto.WriteRelationship(this, target, siblingRelationship, new InstanceHandle[] { source }, dt);
|
||||
sto.WriteRelationship(new InstanceReference(GetGlobalIdentifier(target)), new InstanceReference(GetGlobalIdentifier(siblingRelationship)), new InstanceReference[] { new InstanceReference(GetGlobalIdentifier(source)) }, dt, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -463,11 +467,11 @@ public abstract class Oms
|
||||
public void ClearRelationship(InstanceHandle source, InstanceHandle relationship)
|
||||
{
|
||||
ClearRelationshipInternal(source, relationship);
|
||||
if (!_InhibitStorage)
|
||||
if (!InhibitStorageProcessing)
|
||||
{
|
||||
foreach (OmsStorage sto in Storages)
|
||||
{
|
||||
sto.ClearRelationship(this, source, relationship, DateTime.Now);
|
||||
sto.ClearRelationship(new InstanceReference(GetGlobalIdentifier(source)), new InstanceReference(GetGlobalIdentifier(relationship)), DateTime.Now);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -492,6 +496,15 @@ public abstract class Oms
|
||||
throw new KeyNotFoundException(String.Format("inst not found: {0}", globalIdentifier));
|
||||
}
|
||||
|
||||
public InstanceHandle GetInstance(InstanceReference ir)
|
||||
{
|
||||
if (ir.GlobalIdentifier != Guid.Empty)
|
||||
{
|
||||
return GetInstance(ir.GlobalIdentifier);
|
||||
}
|
||||
return GetInstance(ir.InstanceKey);
|
||||
}
|
||||
|
||||
private Dictionary<string, InstanceHandle> _derivedInstances = new Dictionary<string, InstanceHandle>();
|
||||
public InstanceHandle GetInstance(InstanceKey ik)
|
||||
{
|
||||
@ -620,11 +633,11 @@ public abstract class Oms
|
||||
*/
|
||||
|
||||
DateTime dt = DateTime.Now;
|
||||
if (!_InhibitStorage)
|
||||
if (!InhibitStorageProcessing)
|
||||
{
|
||||
foreach (OmsStorage sto in Storages)
|
||||
{
|
||||
sto.CreateInstance(this, ir, ir_class.GetHandle(), dt);
|
||||
sto.CreateInstance(new InstanceReference(guid), dt);
|
||||
}
|
||||
}
|
||||
SetParentClass(ir, ir_class.GetHandle());
|
||||
@ -856,11 +869,11 @@ public abstract class Oms
|
||||
Log.WriteLine("oms: setting attribute value {0} . {1} = '{2}'", sh, attribute, value);
|
||||
SetAttributeValueInternal(sh, attribute, value, dt);
|
||||
|
||||
if (!_InhibitStorage)
|
||||
if (!InhibitStorageProcessing)
|
||||
{
|
||||
foreach (OmsStorage sto in Storages)
|
||||
{
|
||||
sto.WriteAttributeValue(this, source.GetHandle(), attribute, value, dt);
|
||||
sto.WriteAttributeValue(new InstanceReference(GetGlobalIdentifier(source.GetHandle())), new InstanceReference(GetGlobalIdentifier(attribute)), value, dt);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2605,14 +2618,28 @@ public abstract class Oms
|
||||
return value;
|
||||
}
|
||||
|
||||
private bool _InhibitStorage = false;
|
||||
internal bool InhibitStorageProcessing { get; set; } = false;
|
||||
public void ReloadStorages()
|
||||
{
|
||||
_InhibitStorage = true;
|
||||
InhibitStorageProcessing = true;
|
||||
foreach (OmsStorage sto in Storages)
|
||||
{
|
||||
sto.Load(this);
|
||||
}
|
||||
_InhibitStorage = false;
|
||||
InhibitStorageProcessing = false;
|
||||
}
|
||||
|
||||
internal InstanceHandle UnsafeCreateInstance(InstanceReference ir)
|
||||
{
|
||||
return UnsafeCreateInstance(ir.GlobalIdentifier, ir.InstanceKey);
|
||||
}
|
||||
internal InstanceHandle UnsafeCreateInstance(Guid guid, InstanceKey ik)
|
||||
{
|
||||
InstanceHandle ih = CreateInstance(guid);
|
||||
if (ik != InstanceKey.Empty)
|
||||
{
|
||||
SetInstanceKey(ih, ik);
|
||||
}
|
||||
return ih;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of Mocha.NET.
|
||||
//
|
||||
@ -15,73 +15,108 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System.Net.Sockets;
|
||||
using System.Runtime.Serialization;
|
||||
|
||||
namespace Mocha.Core;
|
||||
|
||||
public abstract class OmsStorage
|
||||
{
|
||||
public class OmsStorageCollection : System.Collections.ObjectModel.Collection<OmsStorage>
|
||||
{
|
||||
private Oms _parent;
|
||||
public OmsStorageCollection(Oms parent)
|
||||
public Oms Oms { get; private set; }
|
||||
public OmsStorageCollection(Oms oms)
|
||||
{
|
||||
_parent = parent;
|
||||
Oms = oms;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void CreateInstanceInternal(Oms oms, InstanceHandle source, InstanceHandle parentClass, DateTime effectiveDate);
|
||||
public void CreateInstance(Oms oms, InstanceHandle source, InstanceHandle parentClass, DateTime effectiveDate)
|
||||
private bool _ReadOnly = false;
|
||||
public bool ReadOnly { get { return _ReadOnly; } }
|
||||
|
||||
protected OmsStorage(bool readOnly)
|
||||
{
|
||||
CreateInstanceInternal(oms, source, parentClass, effectiveDate);
|
||||
if (AutoFlush) Flush();
|
||||
_ReadOnly = readOnly;
|
||||
}
|
||||
|
||||
protected abstract void WriteAttributeValueInternal(Oms oms, InstanceHandle source, InstanceHandle attribute, object value, DateTime effectiveDate);
|
||||
public void WriteAttributeValue(Oms oms, InstanceHandle source, InstanceHandle attribute, object value, DateTime effectiveDate)
|
||||
{
|
||||
WriteAttributeValueInternal(oms, source, attribute, value, effectiveDate);
|
||||
if (AutoFlush) Flush();
|
||||
}
|
||||
|
||||
protected abstract void WriteRelationshipInternal(Oms oms, InstanceHandle source, InstanceHandle relationship, IEnumerable<InstanceHandle> targets, DateTime effectiveDate);
|
||||
public void WriteRelationship(Oms oms, InstanceHandle source, InstanceHandle relationship, IEnumerable<InstanceHandle> targets, DateTime effectiveDate)
|
||||
{
|
||||
WriteRelationshipInternal(oms, source, relationship, targets, effectiveDate);
|
||||
if (AutoFlush) Flush();
|
||||
}
|
||||
|
||||
protected abstract void ClearRelationshipInternal(Oms oms, InstanceHandle source, InstanceHandle relationship, DateTime effectiveDate);
|
||||
public void ClearRelationship(Oms oms, InstanceHandle source, InstanceHandle relationship, DateTime effectiveDate)
|
||||
{
|
||||
ClearRelationshipInternal(oms, source, relationship, effectiveDate);
|
||||
if (AutoFlush) Flush();
|
||||
}
|
||||
|
||||
protected abstract void OpenInternal();
|
||||
public void Open()
|
||||
{
|
||||
OpenInternal();
|
||||
}
|
||||
|
||||
public bool AutoFlush { get; set; } = true;
|
||||
|
||||
protected abstract void FlushInternal();
|
||||
public void Flush()
|
||||
{
|
||||
FlushInternal();
|
||||
}
|
||||
protected abstract void CloseInternal();
|
||||
protected virtual void CloseInternal() { }
|
||||
public void Close()
|
||||
{
|
||||
CloseInternal();
|
||||
}
|
||||
|
||||
protected virtual void ResolveDependenciesInternal(Oms oms)
|
||||
{
|
||||
}
|
||||
internal void ResolveDependencies(Oms oms)
|
||||
{
|
||||
ResolveDependenciesInternal(oms);
|
||||
}
|
||||
|
||||
protected abstract void CreateInstanceInternal(InstanceReference instance, DateTime effectiveDate);
|
||||
public void CreateInstance(InstanceReference instance, DateTime effectiveDate)
|
||||
{
|
||||
if (ReadOnly)
|
||||
return;
|
||||
|
||||
CreateInstanceInternal(instance, effectiveDate);
|
||||
if (AutoFlush)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void WriteAttributeValueInternal(InstanceReference sourceInstance, InstanceReference attributeInstance, object value, DateTime effectiveDate);
|
||||
public void WriteAttributeValue(InstanceReference sourceInstance, InstanceReference attributeInstance, object value, DateTime effectiveDate)
|
||||
{
|
||||
if (ReadOnly)
|
||||
return;
|
||||
|
||||
WriteAttributeValueInternal(sourceInstance, attributeInstance, value, effectiveDate);
|
||||
if (AutoFlush)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void ClearRelationshipInternal(InstanceReference sourceInstance, InstanceReference relationshipInstance, DateTime effectiveDate);
|
||||
public void ClearRelationship(InstanceReference sourceInstance, InstanceReference relationshipInstance, DateTime effectiveDate)
|
||||
{
|
||||
if (ReadOnly)
|
||||
return;
|
||||
|
||||
ClearRelationshipInternal(sourceInstance, relationshipInstance, effectiveDate);
|
||||
if (AutoFlush)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void WriteRelationshipInternal(InstanceReference sourceInstance, InstanceReference attributeInstance, IEnumerable<InstanceReference> targetInstances, DateTime effectiveDate, bool remove);
|
||||
public void WriteRelationship(InstanceReference sourceInstance, InstanceReference attributeInstance, IEnumerable<InstanceReference> targetInstances, DateTime effectiveDate, bool remove)
|
||||
{
|
||||
if (ReadOnly)
|
||||
return;
|
||||
|
||||
WriteRelationshipInternal(sourceInstance, attributeInstance, targetInstances, effectiveDate, remove);
|
||||
if (AutoFlush)
|
||||
{
|
||||
Flush();
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void LoadInternal(Oms oms);
|
||||
public void Load(Oms oms)
|
||||
{
|
||||
oms.InhibitStorageProcessing = true;
|
||||
LoadInternal(oms);
|
||||
oms.InhibitStorageProcessing = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool AutoFlush { get; set; } = true;
|
||||
|
||||
protected virtual void FlushInternal()
|
||||
{
|
||||
}
|
||||
public void Flush()
|
||||
{
|
||||
FlushInternal();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,214 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of Mocha.NET.
|
||||
//
|
||||
// Mocha.NET is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Mocha.NET is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Mocha.Core.Storage.Binary;
|
||||
|
||||
public class BinaryOmsStorage : OmsStorage
|
||||
{
|
||||
private Stream _Stream;
|
||||
public Stream Stream { get { return _Stream; } }
|
||||
|
||||
private BinaryWriter _Writer;
|
||||
private BinaryWriter Writer { get { return _Writer; } }
|
||||
|
||||
private BinaryReader _Reader;
|
||||
private BinaryReader Reader { get { return _Reader; } }
|
||||
|
||||
public BinaryOmsStorage(Stream stream, bool readOnly = false) : base(readOnly)
|
||||
{
|
||||
_Stream = stream;
|
||||
if (!readOnly)
|
||||
{
|
||||
_Writer = new BinaryWriter(stream);
|
||||
}
|
||||
_Reader = new BinaryReader(stream);
|
||||
}
|
||||
|
||||
private void WriteData(BinaryWriter writer, object value)
|
||||
{
|
||||
if (value is string)
|
||||
{
|
||||
writer.Write((int)BinaryOmsStorageAttributeType.Text);
|
||||
writer.Write((string)value);
|
||||
return;
|
||||
}
|
||||
else if (value is bool)
|
||||
{
|
||||
writer.Write((int)BinaryOmsStorageAttributeType.Boolean);
|
||||
writer.Write((bool)value);
|
||||
return;
|
||||
}
|
||||
else if (value is decimal)
|
||||
{
|
||||
writer.Write((int)BinaryOmsStorageAttributeType.Numeric);
|
||||
writer.Write((decimal)value);
|
||||
return;
|
||||
}
|
||||
else if (value is DateTime)
|
||||
{
|
||||
writer.Write((int)BinaryOmsStorageAttributeType.Date);
|
||||
writer.Write(((DateTime)value).ToBinary());
|
||||
return;
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
|
||||
private object ReadData(BinaryReader reader)
|
||||
{
|
||||
BinaryOmsStorageAttributeType type = (BinaryOmsStorageAttributeType)reader.ReadInt32();
|
||||
if (type == BinaryOmsStorageAttributeType.Text)
|
||||
{
|
||||
return reader.ReadString();
|
||||
}
|
||||
else if (type == BinaryOmsStorageAttributeType.Boolean)
|
||||
{
|
||||
return reader.ReadBoolean();
|
||||
}
|
||||
else if (type == BinaryOmsStorageAttributeType.Numeric)
|
||||
{
|
||||
return reader.ReadDecimal();
|
||||
}
|
||||
else if (type == BinaryOmsStorageAttributeType.Date)
|
||||
{
|
||||
return DateTime.FromBinary(reader.ReadInt64());
|
||||
}
|
||||
throw new InvalidOperationException();
|
||||
}
|
||||
protected override void CreateInstanceInternal(InstanceReference sourceInstance, DateTime effectiveDate)
|
||||
{
|
||||
Writer.Write((byte)BinaryOmsStorageOpCode.CreateInstance);
|
||||
WriteInstance(sourceInstance);
|
||||
Writer.Write(effectiveDate.ToBinary());
|
||||
}
|
||||
|
||||
protected override void WriteAttributeValueInternal(InstanceReference sourceInstance, InstanceReference attributeInstance, object value, DateTime effectiveDate)
|
||||
{
|
||||
Writer.Write((byte)BinaryOmsStorageOpCode.AssignAttribute);
|
||||
WriteInstance(sourceInstance);
|
||||
WriteInstance(attributeInstance);
|
||||
Writer.Write(effectiveDate.ToBinary());
|
||||
|
||||
WriteData(Writer, value);
|
||||
}
|
||||
|
||||
private void WriteInstance(InstanceReference instanceReference)
|
||||
{
|
||||
WriteInstanceKey(instanceReference.InstanceKey);
|
||||
WriteGuid(instanceReference.GlobalIdentifier);
|
||||
}
|
||||
|
||||
protected override void ClearRelationshipInternal(InstanceReference sourceInstance, InstanceReference relationshipInstance, DateTime effectiveDate)
|
||||
{
|
||||
_Writer.Write((byte)BinaryOmsStorageOpCode.ClearRelationship);
|
||||
WriteInstance(sourceInstance);
|
||||
WriteInstance(relationshipInstance);
|
||||
Writer.Write(effectiveDate.ToBinary());
|
||||
}
|
||||
|
||||
protected override void WriteRelationshipInternal(InstanceReference sourceInstance, InstanceReference relationshipInstance, IEnumerable<InstanceReference> targetInstances, DateTime effectiveDate, bool remove)
|
||||
{
|
||||
_Writer.Write((byte)BinaryOmsStorageOpCode.AssignRelationship);
|
||||
WriteInstance(sourceInstance);
|
||||
WriteInstance(relationshipInstance);
|
||||
Writer.Write(effectiveDate.ToBinary());
|
||||
|
||||
_Writer.Write(targetInstances.Count());
|
||||
foreach (InstanceReference ir in targetInstances)
|
||||
{
|
||||
WriteInstance(ir);
|
||||
}
|
||||
}
|
||||
|
||||
private void WriteInstanceKey(InstanceKey instanceKey)
|
||||
{
|
||||
Writer.Write(instanceKey.ClassIndex);
|
||||
Writer.Write(instanceKey.InstanceIndex);
|
||||
}
|
||||
private void WriteGuid(Guid guid)
|
||||
{
|
||||
Writer.Write(guid.ToByteArray());
|
||||
}
|
||||
protected InstanceKey ReadInstanceKey()
|
||||
{
|
||||
int classIndex = Reader.ReadInt32();
|
||||
int instanceIndex = Reader.ReadInt32();
|
||||
return new InstanceKey(classIndex, instanceIndex);
|
||||
}
|
||||
protected Guid ReadGuid()
|
||||
{
|
||||
byte[] data = Reader.ReadBytes(16);
|
||||
return new Guid(data);
|
||||
}
|
||||
|
||||
protected virtual void ApplyOperation(Oms oms, BinaryOmsStorageOpCode opcode)
|
||||
{
|
||||
if (opcode == BinaryOmsStorageOpCode.CreateInstance)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
else if (opcode == BinaryOmsStorageOpCode.AssignAttribute)
|
||||
{
|
||||
InstanceReference sourceInstance = ReadInstance();
|
||||
InstanceReference attributeInstance = ReadInstance();
|
||||
|
||||
DateTime effectiveDate = DateTime.FromBinary(Reader.ReadInt64());
|
||||
object value = ReadData(Reader);
|
||||
|
||||
oms.SetAttributeValue(sourceInstance.GetHandle(oms), attributeInstance.GetHandle(oms), value, effectiveDate);
|
||||
}
|
||||
else if (opcode == BinaryOmsStorageOpCode.AssignRelationship)
|
||||
{
|
||||
InstanceReference sourceInstance = ReadInstance();
|
||||
InstanceReference relationshipInstance = ReadInstance();
|
||||
DateTime effectiveDate = DateTime.FromBinary(Reader.ReadInt64());
|
||||
|
||||
int instanceCount = Reader.ReadInt32();
|
||||
InstanceReference[] iks = new InstanceReference[instanceCount];
|
||||
for (int i = 0; i < instanceCount; i++)
|
||||
{
|
||||
InstanceReference ik = ReadInstance();
|
||||
iks[i] = ik;
|
||||
}
|
||||
oms.AssignRelationship(sourceInstance.GetHandle(oms), relationshipInstance.GetHandle(oms), InstanceReference.GetHandles(iks, oms), effectiveDate);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void LoadInternal(Oms oms)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
BinaryOmsStorageOpCode opcode = (BinaryOmsStorageOpCode)Reader.ReadByte();
|
||||
if (opcode == BinaryOmsStorageOpCode.Invalid) return;
|
||||
|
||||
ApplyOperation(oms, opcode);
|
||||
}
|
||||
catch (EndOfStreamException ex)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected InstanceReference ReadInstance()
|
||||
{
|
||||
InstanceKey sourceInstanceKey = ReadInstanceKey();
|
||||
Guid sourceInstanceGuid = ReadGuid();
|
||||
return new InstanceReference(sourceInstanceKey, sourceInstanceGuid);
|
||||
}
|
||||
}
|
||||
@ -15,9 +15,16 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using Mocha.Testing;
|
||||
namespace Mocha.Core.Storage.Binary;
|
||||
|
||||
public class PersistenceTests : OmsTestsBase
|
||||
public enum BinaryOmsStorageAttributeType : int
|
||||
{
|
||||
|
||||
Text = 4,
|
||||
Boolean = 5,
|
||||
Numeric = 20,
|
||||
Date = 21,
|
||||
XML = 22,
|
||||
Currency = 99,
|
||||
File = 822,
|
||||
RichText = 2737
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of Mocha.NET.
|
||||
//
|
||||
// Mocha.NET is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Mocha.NET is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Mocha.Core.Storage.Binary;
|
||||
|
||||
public enum BinaryOmsStorageOpCode : byte
|
||||
{
|
||||
Invalid = 0x00,
|
||||
|
||||
CreateInstance = 0xC1,
|
||||
DeleteInstance = 0xC0,
|
||||
|
||||
AssignAttribute = 0xA1,
|
||||
ClearAttribute = 0xA0,
|
||||
|
||||
AssignRelationship = 0xB1,
|
||||
RemoveRelationship = 0xB2,
|
||||
ClearRelationship = 0xB0
|
||||
}
|
||||
@ -1,138 +0,0 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of Mocha.NET.
|
||||
//
|
||||
// Mocha.NET is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Mocha.NET is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Data;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
|
||||
namespace Mocha.Core.Storage;
|
||||
|
||||
public class BinaryOmsStorage : OmsStorage
|
||||
{
|
||||
public Stream Stream { get; }
|
||||
|
||||
public BinaryWriter Writer { get; }
|
||||
|
||||
public BinaryOmsStorage(string path)
|
||||
{
|
||||
Stream = System.IO.File.Open(path, FileMode.Create, FileAccess.Write, FileShare.Read);
|
||||
Writer = new BinaryWriter(Stream);
|
||||
|
||||
Open();
|
||||
}
|
||||
public BinaryOmsStorage(Stream stream)
|
||||
{
|
||||
Stream = stream;
|
||||
Writer = new BinaryWriter(Stream);
|
||||
|
||||
Open();
|
||||
}
|
||||
|
||||
protected override void OpenInternal()
|
||||
{
|
||||
Writer.Write(new char[] { 'M', 'c', 'J', '!' });
|
||||
Writer.Write(1.0f);
|
||||
}
|
||||
|
||||
protected override void CreateInstanceInternal(Oms oms, InstanceHandle source, InstanceHandle parentClass, DateTime effectiveDate)
|
||||
{
|
||||
Guid sourceInstanceGuid = oms.GetGlobalIdentifier(source);
|
||||
Guid classInstanceGuid = oms.GetGlobalIdentifier(parentClass);
|
||||
|
||||
Writer.Write((byte)0xC1);
|
||||
Writer.Write(sourceInstanceGuid.ToByteArray());
|
||||
Writer.Write(classInstanceGuid.ToByteArray());
|
||||
|
||||
InstanceKey ik = oms.GetInstanceKey(source);
|
||||
Writer.Write(ik.ClassIndex);
|
||||
Writer.Write(ik.InstanceIndex);
|
||||
|
||||
Writer.Write(effectiveDate.ToBinary());
|
||||
Writer.Write(DateTime.Now.ToBinary());
|
||||
}
|
||||
|
||||
protected override void WriteAttributeValueInternal(Oms oms, InstanceHandle source, InstanceHandle attribute, object value, DateTime effectiveDate)
|
||||
{
|
||||
Guid sourceInstanceGuid = oms.GetGlobalIdentifier(source);
|
||||
Guid attributeInstanceGuid = oms.GetGlobalIdentifier(attribute);
|
||||
|
||||
Writer.Write((byte)0xA1);
|
||||
Writer.Write(sourceInstanceGuid.ToByteArray());
|
||||
Writer.Write(attributeInstanceGuid.ToByteArray());
|
||||
WriteBinaryValue(Writer, value);
|
||||
|
||||
Writer.Write(effectiveDate.ToBinary());
|
||||
Writer.Write(DateTime.Now.ToBinary());
|
||||
}
|
||||
|
||||
private void WriteBinaryValue(BinaryWriter writer, object value)
|
||||
{
|
||||
if (value is string s)
|
||||
{
|
||||
writer.Write((int)1);
|
||||
writer.Write(s);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WriteRelationshipInternal(Oms oms, InstanceHandle source, InstanceHandle relationship, IEnumerable<InstanceHandle> targets, DateTime effectiveDate)
|
||||
{
|
||||
Guid sourceInstanceGuid = oms.GetGlobalIdentifier(source);
|
||||
Guid relationshipInstanceGuid = oms.GetGlobalIdentifier(relationship);
|
||||
|
||||
Writer.Write((byte)0xB1);
|
||||
Writer.Write(sourceInstanceGuid.ToByteArray());
|
||||
Writer.Write(relationshipInstanceGuid.ToByteArray());
|
||||
|
||||
Writer.Write((int)targets.Count());
|
||||
foreach (InstanceHandle target in targets)
|
||||
{
|
||||
Writer.Write(oms.GetGlobalIdentifier(target).ToByteArray());
|
||||
}
|
||||
|
||||
Writer.Write(effectiveDate.ToBinary());
|
||||
Writer.Write(DateTime.Now.ToBinary());
|
||||
}
|
||||
|
||||
protected override void ClearRelationshipInternal(Oms oms, InstanceHandle source, InstanceHandle relationship, DateTime effectiveDate)
|
||||
{
|
||||
Guid sourceInstanceGuid = oms.GetGlobalIdentifier(source);
|
||||
Guid relationshipInstanceGuid = oms.GetGlobalIdentifier(relationship);
|
||||
|
||||
Writer.Write((byte)0xB0);
|
||||
Writer.Write(sourceInstanceGuid.ToByteArray());
|
||||
Writer.Write(relationshipInstanceGuid.ToByteArray());
|
||||
|
||||
Writer.Write(effectiveDate.ToBinary());
|
||||
Writer.Write(DateTime.Now.ToBinary());
|
||||
}
|
||||
|
||||
protected override void FlushInternal()
|
||||
{
|
||||
Writer.Flush();
|
||||
}
|
||||
|
||||
protected override void CloseInternal()
|
||||
{
|
||||
Writer.Flush();
|
||||
Stream.Close();
|
||||
}
|
||||
|
||||
protected override void LoadInternal(Oms oms)
|
||||
{
|
||||
}
|
||||
|
||||
}
|
||||
25
mocha-dotnet/src/lib/Mocha.Core/Storage/CustomDefinition.cs
Normal file
25
mocha-dotnet/src/lib/Mocha.Core/Storage/CustomDefinition.cs
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of mochalite-dotnet.
|
||||
//
|
||||
// mochalite-dotnet is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// mochalite-dotnet is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with mochalite-dotnet. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Mocha.Core.Storage;
|
||||
|
||||
public class CustomDefinition
|
||||
{
|
||||
public InstanceReference ClassInstance { get; set; } = null;
|
||||
public Dictionary<string, InstanceReference> Attributes { get; } = new Dictionary<string, InstanceReference>();
|
||||
public Dictionary<string, InstanceReference> Relationships { get; } = new Dictionary<string, InstanceReference>();
|
||||
}
|
||||
342
mocha-dotnet/src/lib/Mocha.Core/Storage/Json/JsonOmsStorage.cs
Normal file
342
mocha-dotnet/src/lib/Mocha.Core/Storage/Json/JsonOmsStorage.cs
Normal file
@ -0,0 +1,342 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of mochalite-dotnet.
|
||||
//
|
||||
// mochalite-dotnet is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// mochalite-dotnet is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with mochalite-dotnet. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
using System.Data;
|
||||
using System.Globalization;
|
||||
using System.IO.Pipes;
|
||||
using System.Linq.Expressions;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Threading.Tasks.Dataflow;
|
||||
using Mocha.Core;
|
||||
|
||||
|
||||
namespace Mocha.Core.Storage.Json;
|
||||
|
||||
public class JsonOmsStorage : OmsStorage
|
||||
{
|
||||
private Stream _Stream;
|
||||
public Stream Stream { get { return _Stream; } }
|
||||
|
||||
public List<JsonObject> UnresolvedCustomDefinitions { get; } = new List<JsonObject>();
|
||||
|
||||
protected JsonObject? MakeInstanceObj(InstanceReference instance)
|
||||
{
|
||||
if (instance.InstanceKey == InstanceKey.Empty && instance.GlobalIdentifier == Guid.Empty)
|
||||
return null;
|
||||
|
||||
JsonObject o = new JsonObject();
|
||||
if (instance.InstanceKey != InstanceKey.Empty)
|
||||
{
|
||||
o.Add("instanceKey", instance.InstanceKey.ToString());
|
||||
}
|
||||
if (instance.GlobalIdentifier != Guid.Empty)
|
||||
{
|
||||
o.Add("globalIdentifier", instance.GlobalIdentifier.ToString());
|
||||
}
|
||||
return o;
|
||||
}
|
||||
|
||||
public JsonOmsStorage(Stream stream, bool readOnly = false) : base(readOnly)
|
||||
{
|
||||
_Stream = stream;
|
||||
|
||||
_obj.Add("type", "journal");
|
||||
_obj.Add("items", _ary);
|
||||
|
||||
|
||||
}
|
||||
|
||||
public static JsonOmsStorage FromPath(string path)
|
||||
{
|
||||
string[] jsonfiles = System.IO.Directory.GetFiles(path, "*.json", SearchOption.AllDirectories);
|
||||
StringBuilder content = new StringBuilder();
|
||||
content.Append("{\"type\": \"journal\", \"items\": [");
|
||||
for (int i = 0; i < jsonfiles.Length; i++)
|
||||
{
|
||||
content.Append(System.IO.File.ReadAllText(jsonfiles[i]));
|
||||
if (i < jsonfiles.Length - 1)
|
||||
{
|
||||
content.Append(',');
|
||||
}
|
||||
}
|
||||
content.Append("]}");
|
||||
MemoryStream ms = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(content.ToString()));
|
||||
return new JsonOmsStorage(ms, true);
|
||||
}
|
||||
|
||||
private JsonArray _ary = new JsonArray();
|
||||
private JsonObject _obj = new JsonObject();
|
||||
|
||||
protected override void FlushInternal()
|
||||
{
|
||||
Stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
string text = _obj.ToJsonString();
|
||||
byte[] data = System.Text.Encoding.UTF8.GetBytes(text);
|
||||
Stream.Write(data, 0, data.Length);
|
||||
}
|
||||
|
||||
protected override void ClearRelationshipInternal(InstanceReference sourceInstance, InstanceReference relationshipInstance, DateTime effectiveDate)
|
||||
{
|
||||
JsonObject o = new JsonObject();
|
||||
o.Add("type", "clearRelationship");
|
||||
o.Add("sourceInstance", MakeInstanceObj(sourceInstance));
|
||||
o.Add("relationshipInstance", MakeInstanceObj(relationshipInstance));
|
||||
o.Add("effectiveDate", effectiveDate.ToString("O"));
|
||||
_ary.Add(o);
|
||||
}
|
||||
|
||||
protected override void CreateInstanceInternal(InstanceReference instance, DateTime effectiveDate)
|
||||
{
|
||||
JsonObject o = new JsonObject();
|
||||
o.Add("type", "createInstance");
|
||||
o.Add("instanceKey", instance.InstanceKey.ToString());
|
||||
o.Add("globalIdentifier", instance.GlobalIdentifier.ToString());
|
||||
o.Add("effectiveDate", effectiveDate.ToString("O"));
|
||||
_ary.Add(o);
|
||||
}
|
||||
|
||||
protected override void WriteAttributeValueInternal(InstanceReference sourceInstance, InstanceReference attributeInstance, object value, DateTime effectiveDate)
|
||||
{
|
||||
JsonObject o = new JsonObject();
|
||||
o.Add("type", "assignAttribute");
|
||||
o.Add("sourceInstance", MakeInstanceObj(sourceInstance));
|
||||
o.Add("attributeInstance", MakeInstanceObj(attributeInstance));
|
||||
o.Add("value", JsonValue.Create(value));
|
||||
o.Add("effectiveDate", effectiveDate.ToString("O"));
|
||||
_ary.Add(o);
|
||||
}
|
||||
|
||||
protected override void WriteRelationshipInternal(InstanceReference sourceInstance, InstanceReference relationshipInstance, IEnumerable<InstanceReference> targetInstances, DateTime effectiveDate, bool remove)
|
||||
{
|
||||
JsonObject o = new JsonObject();
|
||||
o.Add("type", "assignRelationship");
|
||||
o.Add("sourceInstance", MakeInstanceObj(sourceInstance));
|
||||
o.Add("relationshipInstance", MakeInstanceObj(relationshipInstance));
|
||||
|
||||
JsonArray ary = new JsonArray();
|
||||
foreach (InstanceReference inst in targetInstances)
|
||||
{
|
||||
ary.Add(MakeInstanceObj(inst));
|
||||
}
|
||||
o.Add("targetInstances", ary);
|
||||
|
||||
o.Add("effectiveDate", effectiveDate.ToString("O"));
|
||||
_ary.Add(o);
|
||||
}
|
||||
|
||||
protected override void LoadInternal(Oms oms)
|
||||
{
|
||||
StreamReader sr = new StreamReader(Stream);
|
||||
string text = sr.ReadToEnd();
|
||||
JsonObject? obj = (JsonObject?)JsonNode.Parse(text);
|
||||
LoadObject(obj, oms);
|
||||
}
|
||||
|
||||
private Dictionary<string, CustomDefinition> customDefinitions = new Dictionary<string, CustomDefinition>();
|
||||
|
||||
private void LoadObject(JsonObject? obj, Oms oms)
|
||||
{
|
||||
if (obj == null) return;
|
||||
|
||||
if (obj.ContainsKey("type"))
|
||||
{
|
||||
JsonNode? n = obj["type"];
|
||||
if (n != null)
|
||||
{
|
||||
string tagName = n.GetValue<string>();
|
||||
if (tagName.Equals("journal"))
|
||||
{
|
||||
JsonArray? ary = obj["items"] as JsonArray;
|
||||
if (ary != null)
|
||||
{
|
||||
foreach (JsonNode? n2 in ary)
|
||||
{
|
||||
if (n2 is JsonObject o2)
|
||||
{
|
||||
LoadObject(o2, oms);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (tagName.Equals("createInstance"))
|
||||
{
|
||||
InstanceReference? ir = ParseInstanceReference(obj);
|
||||
InstanceHandle ih = oms.UnsafeCreateInstance(ir);
|
||||
}
|
||||
else if (tagName.Equals("clearRelationship"))
|
||||
{
|
||||
oms.ClearRelationship(oms.GetInstance(ParseInstanceReference(obj["sourceInstance"] as JsonObject)), oms.GetInstance(ParseInstanceReference(obj["relationshipInstance"] as JsonObject)));
|
||||
}
|
||||
else if (tagName.Equals("assignRelationship"))
|
||||
{
|
||||
List<InstanceHandle> tgts = new List<InstanceHandle>();
|
||||
foreach (JsonNode n1 in (obj["targetInstances"] as JsonArray))
|
||||
{
|
||||
tgts.Add(oms.GetInstance(ParseInstanceReference(n1 as JsonObject)));
|
||||
}
|
||||
oms.AssignRelationship(oms.GetInstance(ParseInstanceReference(obj["sourceInstance"] as JsonObject)), oms.GetInstance(ParseInstanceReference(obj["relationshipInstance"] as JsonObject)), tgts);
|
||||
}
|
||||
else if (tagName.Equals("assignAttribute"))
|
||||
{
|
||||
object? value = obj["value"].GetValue<string>();
|
||||
DateTime effectiveDate = DateTime.Parse(obj["effectiveDate"].GetValue<string>());
|
||||
oms.SetAttributeValue(oms.GetInstance(ParseInstanceReference(obj["sourceInstance"] as JsonObject)), oms.GetInstance(ParseInstanceReference(obj["attributeInstance"] as JsonObject)), value, effectiveDate);
|
||||
}
|
||||
else if (tagName.Equals("instance"))
|
||||
{
|
||||
CustomDefinition? cd = null;
|
||||
if (obj.ContainsKey("instanceKey"))
|
||||
{
|
||||
InstanceKey inst = InstanceKey.Parse(obj["instanceKey"].GetValue<string>());
|
||||
if (obj.ContainsKey("customTagName"))
|
||||
{
|
||||
cd = new CustomDefinition();
|
||||
cd.ClassInstance = new InstanceReference(inst);
|
||||
customDefinitions[obj["customTagName"].GetValue<string>()] = cd;
|
||||
}
|
||||
|
||||
if (obj.ContainsKey("attributes"))
|
||||
{
|
||||
JsonArray ary = obj["attributes"].AsArray();
|
||||
foreach (JsonObject? node in ary)
|
||||
{
|
||||
if (node == null) continue;
|
||||
|
||||
if (node.ContainsKey("instanceKey"))
|
||||
{
|
||||
if (node.ContainsKey("value"))
|
||||
{
|
||||
InstanceKey att = InstanceKey.Parse(node["instanceKey"].GetValue<string>());
|
||||
if (node["value"].GetValueKind() == System.Text.Json.JsonValueKind.String)
|
||||
{
|
||||
oms.SetAttributeValue(oms.GetInstance(inst), oms.GetInstance(att), node["value"].GetValue<string>());
|
||||
}
|
||||
}
|
||||
if (cd != null && node.ContainsKey("customTagName"))
|
||||
{
|
||||
cd.Attributes[node["customTagName"].GetValue<string>()] = new InstanceReference(InstanceKey.Parse(node["instanceKey"].GetValue<string>()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (obj.ContainsKey("relationships"))
|
||||
{
|
||||
JsonArray ary = obj["relationships"].AsArray();
|
||||
foreach (JsonObject? node in ary)
|
||||
{
|
||||
if (node == null) continue;
|
||||
|
||||
if (node.ContainsKey("relationshipInstanceKey"))
|
||||
{
|
||||
InstanceKey rel = InstanceKey.Parse(node["relationshipInstanceKey"].GetValue<string>());
|
||||
if (cd != null && node.ContainsKey("customTagName"))
|
||||
{
|
||||
cd.Attributes[node["customTagName"].GetValue<string>()] = new InstanceReference(InstanceKey.Parse(node["relationshipInstanceKey"].GetValue<string>()));
|
||||
}
|
||||
if (node.ContainsKey("targetInstances"))
|
||||
{
|
||||
JsonArray ary1 = node["targetInstances"].AsArray();
|
||||
List<InstanceHandle> irs = new List<InstanceHandle>();
|
||||
foreach (JsonObject? node1 in ary1)
|
||||
{
|
||||
InstanceReference ir = ParseInstanceReference(node1);
|
||||
if (ir != null)
|
||||
{
|
||||
irs.Add(oms.GetInstance(ir));
|
||||
}
|
||||
}
|
||||
|
||||
oms.AssignRelationship(oms.GetInstance(inst), oms.GetInstance(rel), irs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (customDefinitions.ContainsKey(tagName))
|
||||
{
|
||||
CreateCustomDefinition(oms, obj, customDefinitions[tagName]);
|
||||
}
|
||||
else
|
||||
{
|
||||
UnresolvedCustomDefinitions.Add(obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected override void ResolveDependenciesInternal(Oms oms)
|
||||
{
|
||||
base.ResolveDependenciesInternal(oms);
|
||||
|
||||
foreach (JsonObject obj in UnresolvedCustomDefinitions)
|
||||
{
|
||||
if (obj.ContainsKey("type"))
|
||||
{
|
||||
JsonNode? n = obj["type"];
|
||||
if (n != null)
|
||||
{
|
||||
string tagName = n.GetValue<string>();
|
||||
if (customDefinitions.ContainsKey(tagName))
|
||||
{
|
||||
CreateCustomDefinition(oms, obj, customDefinitions[tagName]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void CreateCustomDefinition(Oms oms, JsonObject obj, CustomDefinition cd)
|
||||
{
|
||||
InstanceHandle inst = oms.CreateInstanceOf(oms.GetInstance(cd.ClassInstance));
|
||||
foreach (KeyValuePair<string, InstanceReference> kvp in cd.Attributes)
|
||||
{
|
||||
if (obj.ContainsKey(kvp.Key))
|
||||
{
|
||||
JsonNode n1 = obj[kvp.Key];
|
||||
oms.SetAttributeValue(inst, oms.GetInstance(kvp.Value), n1.GetValue<string>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private InstanceReference? ParseInstanceReference(JsonObject? obj)
|
||||
{
|
||||
if (obj == null)
|
||||
return null;
|
||||
|
||||
InstanceKey instanceKey = InstanceKey.Empty;
|
||||
Guid globalIdentifier = Guid.Empty;
|
||||
|
||||
if (obj.ContainsKey("instanceKey"))
|
||||
{
|
||||
instanceKey = InstanceKey.Parse(obj["instanceKey"].GetValue<string>());
|
||||
}
|
||||
if (obj.ContainsKey("globalIdentifier"))
|
||||
{
|
||||
globalIdentifier = Guid.Parse(obj["globalIdentifier"].GetValue<string>());
|
||||
}
|
||||
return new InstanceReference(instanceKey, globalIdentifier);
|
||||
}
|
||||
}
|
||||
@ -1,147 +0,0 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of Mocha.NET.
|
||||
//
|
||||
// Mocha.NET is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Mocha.NET is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using Mocha.Core.Oop.Methods;
|
||||
|
||||
namespace Mocha.Core.Storage;
|
||||
|
||||
public class JsonOmsStorage : StreamOmsStorage
|
||||
{
|
||||
public JsonOmsStorage(string path) : base(path) { }
|
||||
public JsonOmsStorage(Stream stream) : base(stream) { }
|
||||
|
||||
private JsonArray PrimaryArray { get; } = new JsonArray();
|
||||
|
||||
protected override void OpenInternal()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void CreateInstanceInternal(Oms oms, InstanceHandle source, InstanceHandle parentClass, DateTime effectiveDate)
|
||||
{
|
||||
Guid sourceInstanceGuid = oms.GetGlobalIdentifier(source);
|
||||
Guid classInstanceGuid = oms.GetGlobalIdentifier(parentClass); // calling oms.GetParentClass(source) here is unreliable
|
||||
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.Add("type", "createInstance");
|
||||
obj.Add("globalIdentifier", sourceInstanceGuid.ToString("B"));
|
||||
obj.Add("parentClassGlobalIdentifier", classInstanceGuid.ToString("B"));
|
||||
obj.Add("iid", oms.GetInstanceKey(source).ToString());
|
||||
obj.Add("edate", effectiveDate);
|
||||
obj.Add("cdate", DateTime.Now);
|
||||
|
||||
PrimaryArray.Add(obj);
|
||||
}
|
||||
|
||||
protected override void WriteAttributeValueInternal(Oms oms, InstanceHandle source, InstanceHandle attribute, object value, DateTime effectiveDate)
|
||||
{
|
||||
Guid sourceInstanceGuid = oms.GetGlobalIdentifier(source);
|
||||
Guid attributeInstanceGuid = oms.GetGlobalIdentifier(attribute);
|
||||
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.Add("type", "assignAttribute");
|
||||
obj.Add("src", sourceInstanceGuid.ToString("B"));
|
||||
obj.Add("att", attributeInstanceGuid.ToString("B"));
|
||||
obj.Add("value", JsonValue.Create(value));
|
||||
obj.Add("edate", effectiveDate);
|
||||
obj.Add("cdate", DateTime.Now);
|
||||
|
||||
PrimaryArray.Add(obj);
|
||||
}
|
||||
|
||||
protected override void WriteRelationshipInternal(Oms oms, InstanceHandle source, InstanceHandle relationship, IEnumerable<InstanceHandle> targets, DateTime effectiveDate)
|
||||
{
|
||||
Guid sourceInstanceGuid = oms.GetGlobalIdentifier(source);
|
||||
Guid relationshipInstanceGuid = oms.GetGlobalIdentifier(relationship);
|
||||
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.Add("type", "assignRelationship");
|
||||
obj.Add("src", sourceInstanceGuid.ToString("B"));
|
||||
obj.Add("rel", relationshipInstanceGuid.ToString("B"));
|
||||
|
||||
JsonArray ary = new JsonArray();
|
||||
foreach (InstanceHandle target in targets)
|
||||
{
|
||||
ary.Add(oms.GetGlobalIdentifier(target).ToString("B"));
|
||||
}
|
||||
|
||||
obj.Add("tgts", ary);
|
||||
obj.Add("edate", effectiveDate);
|
||||
obj.Add("cdate", DateTime.Now);
|
||||
PrimaryArray.Add(obj);
|
||||
}
|
||||
|
||||
protected override void ClearRelationshipInternal(Oms oms, InstanceHandle source, InstanceHandle relationship, DateTime effectiveDate)
|
||||
{
|
||||
Guid sourceInstanceGuid = oms.GetGlobalIdentifier(source);
|
||||
Guid relationshipInstanceGuid = oms.GetGlobalIdentifier(relationship);
|
||||
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.Add("type", "clearRelationship");
|
||||
obj.Add("src", sourceInstanceGuid.ToString("B"));
|
||||
obj.Add("rel", relationshipInstanceGuid.ToString("B"));
|
||||
obj.Add("edate", effectiveDate);
|
||||
obj.Add("cdate", DateTime.Now);
|
||||
PrimaryArray.Add(obj);
|
||||
}
|
||||
|
||||
protected override void CloseInternal()
|
||||
{
|
||||
JsonObject obj = new JsonObject();
|
||||
obj.Add("blocks", PrimaryArray);
|
||||
|
||||
StreamWriter sw = new StreamWriter(Stream);
|
||||
sw.Write(obj.ToString());
|
||||
sw.Flush();
|
||||
|
||||
base.CloseInternal();
|
||||
}
|
||||
|
||||
protected override void LoadInternal(Oms oms)
|
||||
{
|
||||
StreamReader sr = new StreamReader(Stream);
|
||||
string text = sr.ReadToEnd();
|
||||
|
||||
JsonObject? obj = (JsonObject?)JsonNode.Parse(text);
|
||||
if (obj != null)
|
||||
{
|
||||
if (obj.ContainsKey("blocks"))
|
||||
{
|
||||
foreach (JsonObject obj2 in obj["blocks"].AsArray())
|
||||
{
|
||||
string type = obj2["type"].AsValue().ToString();
|
||||
if (type == "createInstance")
|
||||
{
|
||||
Guid globalIdentifier = Guid.Parse(obj2["globalIdentifier"].ToString());
|
||||
Guid parentClassGlobalIdentifier = Guid.Parse(obj2["parentClassGlobalIdentifier"].ToString());
|
||||
InstanceKey iid = InstanceKey.Parse(obj2["iid"].ToString());
|
||||
|
||||
InstanceHandle ih = oms.CreateInstanceOf(oms.GetInstance(parentClassGlobalIdentifier), globalIdentifier);
|
||||
oms.SetInstanceKey(ih, iid);
|
||||
}
|
||||
else if (type == "assignAttribute")
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -22,8 +22,8 @@ public abstract class StreamOmsStorage : OmsStorage
|
||||
{
|
||||
public Stream Stream { get; }
|
||||
|
||||
public StreamOmsStorage(string path) : this(System.IO.File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read)) { }
|
||||
public StreamOmsStorage(Stream stream)
|
||||
public StreamOmsStorage(string path, bool readOnly) : this(System.IO.File.Open(path, FileMode.Create, FileAccess.ReadWrite, FileShare.Read), readOnly) { }
|
||||
public StreamOmsStorage(Stream stream, bool readOnly) : base(readOnly)
|
||||
{
|
||||
Stream = stream;
|
||||
}
|
||||
|
||||
@ -56,26 +56,6 @@ public abstract class OmsTestsBase
|
||||
public void ReloadOms()
|
||||
{
|
||||
Oms = CreateOms();
|
||||
}
|
||||
|
||||
protected readonly Guid TEST_CLASS_GUID = new Guid("{5821fc28-6411-4339-a7d2-56dc05591501}");
|
||||
protected readonly Guid TEST_CLASS2_GUID = new Guid("{6351ecb7-da5d-4029-ba3a-196a6dc980e9}");
|
||||
protected readonly Guid TEST_CLASS_INST_GUID = new Guid("{12b4bd54-dabd-4074-be50-ffd78115a85a}");
|
||||
protected readonly Guid TEST_ATTR_GUID = new Guid("{e4f314a4-f4b1-4cf1-95d8-7a927fb7c8a0}");
|
||||
protected readonly Guid TEST_ATTR_BOOL_GUID = new Guid("{b8dc887c-9964-4a66-94b1-6dcd00b196dc}");
|
||||
protected readonly Guid TEST_ATTR_NUM_GUID = new Guid("{62d633df-47ef-43b7-a108-b0b47f5212f6}");
|
||||
/// <summary>
|
||||
/// Test Class.has Test Class 2
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected readonly Guid TEST_REL_GUID = new Guid("{969d98bc-95e0-45a9-92e5-d70c62980c0e}");
|
||||
protected readonly Guid TEST_REL2_GUID = new Guid("{235f025a-90a6-43a6-9be1-0b32716eb95a}");
|
||||
protected readonly string TEST_ATTR_VALUE = "The quick brown fox jumps over the lazy dog.";
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
ReloadOms();
|
||||
|
||||
// create the Test Class
|
||||
InstanceHandle c_Class = Oms.GetInstance(KnownInstanceGuids.Classes.Class);
|
||||
@ -101,6 +81,26 @@ public abstract class OmsTestsBase
|
||||
|
||||
Relationship? irTestRelationship = Oms.CreateRelationship(irTestClass, "has", irTestClass2, TEST_REL_GUID, true, "for", TEST_REL2_GUID);
|
||||
Assert.That(irTestRelationship, Is.Not.Null);
|
||||
}
|
||||
|
||||
protected readonly Guid TEST_CLASS_GUID = new Guid("{5821fc28-6411-4339-a7d2-56dc05591501}");
|
||||
protected readonly Guid TEST_CLASS2_GUID = new Guid("{6351ecb7-da5d-4029-ba3a-196a6dc980e9}");
|
||||
protected readonly Guid TEST_CLASS_INST_GUID = new Guid("{12b4bd54-dabd-4074-be50-ffd78115a85a}");
|
||||
protected readonly Guid TEST_ATTR_GUID = new Guid("{e4f314a4-f4b1-4cf1-95d8-7a927fb7c8a0}");
|
||||
protected readonly Guid TEST_ATTR_BOOL_GUID = new Guid("{b8dc887c-9964-4a66-94b1-6dcd00b196dc}");
|
||||
protected readonly Guid TEST_ATTR_NUM_GUID = new Guid("{62d633df-47ef-43b7-a108-b0b47f5212f6}");
|
||||
/// <summary>
|
||||
/// Test Class.has Test Class 2
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
protected readonly Guid TEST_REL_GUID = new Guid("{969d98bc-95e0-45a9-92e5-d70c62980c0e}");
|
||||
protected readonly Guid TEST_REL2_GUID = new Guid("{235f025a-90a6-43a6-9be1-0b32716eb95a}");
|
||||
protected readonly string TEST_ATTR_VALUE = "The quick brown fox jumps over the lazy dog.";
|
||||
|
||||
[SetUp]
|
||||
public void Setup()
|
||||
{
|
||||
ReloadOms();
|
||||
|
||||
AfterSetup();
|
||||
}
|
||||
|
||||
@ -0,0 +1,58 @@
|
||||
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
|
||||
//
|
||||
// This file is part of Mocha.NET.
|
||||
//
|
||||
// Mocha.NET is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Mocha.NET is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
namespace Mocha.Core.Tests.Storage;
|
||||
|
||||
using Mocha.Core;
|
||||
using Mocha.Core.Storage;
|
||||
using Mocha.Core.Storage.Binary;
|
||||
using Mocha.Testing;
|
||||
|
||||
public class BinaryStorageTests : OmsTestsBase
|
||||
{
|
||||
[Test]
|
||||
public void BinaryOmsStorage_MemoryStream_Test()
|
||||
{
|
||||
System.IO.MemoryStream ms = new MemoryStream();
|
||||
Oms.Storages.Add(new BinaryOmsStorage(ms));
|
||||
|
||||
string value = Oms.GetAttributeValue<string>(Oms.GetInstance(KnownInstanceGuids.Classes.Class), Oms.GetInstance(KnownAttributeGuids.Text.TargetURL));
|
||||
Assert.That(value, Is.Null);
|
||||
|
||||
Oms.SetAttributeValue(Oms.GetInstance(KnownInstanceGuids.Classes.Class), Oms.GetInstance(KnownAttributeGuids.Text.TargetURL), "mahek_spamss");
|
||||
|
||||
value = Oms.GetAttributeValue<string>(Oms.GetInstance(KnownInstanceGuids.Classes.Class), Oms.GetInstance(KnownAttributeGuids.Text.TargetURL));
|
||||
Assert.That(value, Is.EqualTo("mahek_spamss"));
|
||||
|
||||
Oms.Storages[0].Flush();
|
||||
ms.Flush();
|
||||
|
||||
byte[] data = ms.ToArray();
|
||||
ReloadOms();
|
||||
|
||||
value = Oms.GetAttributeValue<string>(Oms.GetInstance(KnownInstanceGuids.Classes.Class), Oms.GetInstance(KnownAttributeGuids.Text.TargetURL));
|
||||
Assert.That(value, Is.Null);
|
||||
|
||||
ms = new MemoryStream(data);
|
||||
|
||||
Oms.Storages.Add(new BinaryOmsStorage(ms));
|
||||
Oms.ReloadStorages();
|
||||
|
||||
value = Oms.GetAttributeValue<string>(Oms.GetInstance(KnownInstanceGuids.Classes.Class), Oms.GetInstance(KnownAttributeGuids.Text.TargetURL));
|
||||
Assert.That(value, Is.EqualTo("mahek_spamss"));
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,7 @@
|
||||
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
using Mocha.Core.Storage;
|
||||
using Mocha.Core.Storage.Json;
|
||||
using Mocha.Testing;
|
||||
|
||||
namespace Mocha.Core.Tests.Storage;
|
||||
@ -66,5 +67,16 @@ public class JsonStorageTests : OmsTestsBase
|
||||
// and see if we have the newly-created instance
|
||||
Oms.TryGetInstance(new Guid("1e9b7101-13dc-4d97-9ae4-570a07bd3545"), out test);
|
||||
Assert.That(test, Is.Not.EqualTo(InstanceHandle.Empty));
|
||||
|
||||
// and check that the attribute was set correctly
|
||||
v = Oms.GetAttributeValue<string>(test, Oms.GetInstance(KnownAttributeGuids.Text.Name));
|
||||
Assert.That(v, Is.EqualTo("PICADILLY"));
|
||||
|
||||
// and check relationships were assigned properly
|
||||
IEnumerable<InstanceHandle> ihs = Oms.GetRelatedInstances(test, Oms.GetInstance(TEST_REL_GUID));
|
||||
|
||||
InstanceHandle ihC2 = Oms.GetInstance(TEST_CLASS2_GUID);
|
||||
InstanceHandle ihI2 = Oms.GetInstancesOf(ihC2).First();
|
||||
Assert.That(ihs.First(), Is.EqualTo(ihI2));
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user