This commit is contained in:
Michael Becker 2025-07-16 18:09:20 -04:00
parent 3bc0641c99
commit d21c8c9f6d
14 changed files with 677 additions and 28 deletions

View File

@ -10,6 +10,7 @@ using Mocha.Core;
using Mocha.Core.UI.Server;
using Mocha.Core.Logging;
using Mocha.Core.Logging.Loggers;
using Mocha.Core.Storage;
/// <summary>
/// Provides the entry point for a simple application which starts a Web server
@ -56,6 +57,19 @@ public class Program : WebApplication
Console.Error.WriteLine("oms: error: failed to write PID file; graceful control will be unavailable");
}
string mdtfile = "/etc/mocha/oms/20250708.mdt";
mdtfile = Path.Combine(new string[] { System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "mocha", DateTime.Now.ToString("yyyyMMddHHmmss") + ".mdt" });
string? workdir = Path.GetDirectoryName(mdtfile);
if (workdir != null)
{
if (!Directory.Exists(workdir))
{
Directory.CreateDirectory(workdir);
}
}
Oms.Storages.Add(new BinaryOmsStorage(mdtfile));
DefaultFileLogger logger = new DefaultFileLogger();
ConsoleLogger logger2 = new ConsoleLogger();
Log.Loggers.Add(logger);
@ -122,6 +136,8 @@ public class Program : WebApplication
UpdateMochafile();
Oms.ReloadStorages();
// now we can start the Web server
base.OnStartup(e);
}

View File

@ -149,6 +149,11 @@ public class ElementCommand : InstanceCommand
// POST http://localhost:4436/tenants/super/instances/2997$1/element
// relatedInstance=4170$1
if (oms.TryParseInstanceRef(context.Request.Form["relatedInstance"], out InstanceHandle h))
{
omsContext.TargetInstance = h;
}
Response r = oms.ProcessElement(omsContext, elem, context.Request.Form);
JsonObject obj = r.GetResponse(oms, omsContext);

View File

@ -55,11 +55,14 @@ namespace Mocha.Core
public static Guid ExtractSingleInstanceStringComponent { get; } = new Guid("{FCECCE4E-8D05-485A-AE34-B1B45E766661}");
public static Guid InstanceAttributeStringComponent { get; } = new Guid("{623565D5-5AEE-49ED-A5A9-0CFE670507BC}");
public static Guid Prompt { get; } = new Guid("{EC889225-416A-4F73-B8D1-2A42B37AF43E}");
/*
public static Guid TextPrompt { get; } = new Guid("{195DDDD7-0B74-4498-BF61-B0549FE05CF3}");
public static Guid ChoicePrompt { get; } = new Guid("{5597AEEB-922D-48AF-AE67-DF7D951C71DB}");
public static Guid InstancePrompt { get; } = new Guid("{F3ECBF1E-E732-4370-BE05-8FA7CC520F50}");
public static Guid BooleanPrompt { get; } = new Guid("{a7b49c03-c9ce-4a79-a4b2-e94fc8cd8b29}");
*/
public static Guid Method { get; } = new Guid("{D2813913-80B6-4DD6-9AD6-56D989169734}");

View File

@ -18,6 +18,7 @@
namespace Mocha.Core;
using System.Reflection;
using System.Runtime.Serialization;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Nodes;
@ -44,8 +45,12 @@ public abstract class Oms
public bool ValidateConstraints { get; protected set; } = true;
public OmsStorage.OmsStorageCollection Storages { get; }
public Oms()
{
Storages = new OmsStorage.OmsStorageCollection(this);
MethodImplementation[] methodImplementations = MBS.Core.Reflection.TypeLoader.GetAvailableTypes<MethodImplementation>(new System.Reflection.Assembly[] { Assembly.GetExecutingAssembly() });
foreach (MethodImplementation impl in methodImplementations)
{
@ -328,13 +333,22 @@ public abstract class Oms
protected abstract void AssignRelationshipInternal(InstanceHandle source, InstanceHandle relationship, IEnumerable<InstanceHandle> targets, DateTime effectiveDate);
public void AssignRelationship(IInstanceReference source, InstanceHandle relationship, IEnumerable<InstanceHandle> targets, DateTime? effectiveDate = null)
{
DateTime dt = effectiveDate.GetValueOrDefault(DateTime.Now);
ValidateConstraintsForRelationship(source.GetHandle(), relationship, targets);
AssignRelationshipInternal(source.GetHandle(), relationship, targets, effectiveDate.GetValueOrDefault(DateTime.Now));
AssignRelationshipInternal(source.GetHandle(), relationship, targets, dt);
if (!_InhibitStorage)
{
foreach (OmsStorage sto in Storages)
{
sto.WriteRelationship(this, source.GetHandle(), relationship, targets, dt);
}
}
}
public void AssignRelationship(IInstanceReference source, Relationship relationship, IEnumerable<InstanceHandle> targets, DateTime? effectiveDate = null)
{
ValidateConstraintsForRelationship(source.GetHandle(), relationship.GetHandle(), targets);
AssignRelationshipInternal(source.GetHandle(), relationship.GetHandle(), targets, effectiveDate.GetValueOrDefault(DateTime.Now));
AssignRelationship(source.GetHandle(), relationship.GetHandle(), targets, effectiveDate.GetValueOrDefault(DateTime.Now));
}
public void AssignRelationship(IInstanceReference source, InstanceHandle relationship, IInstanceReference target, DateTime? effectiveDate = null)
@ -360,6 +374,15 @@ public abstract class Oms
if (!ValidateConstraints)
return;
// toggle switches for the various constraints
bool enforceSingularRelationshipConstraint = true;
bool validateSourceAndDestinationClasses = true;
// this should possibly be moved outside of what's supposed to only be a "validate" routine
bool createSiblingRelationship = true;
if (enforceSingularRelationshipConstraint)
{
if (TryGetAttributeValue<bool>(relationship, GetInstance(KnownAttributeGuids.Boolean.Singular), out bool value))
{
if (value)
@ -371,7 +394,35 @@ public abstract class Oms
}
}
}
}
if (validateSourceAndDestinationClasses)
{
InstanceHandle ihSourceClass = GetRelatedInstance(relationship, GetInstance(KnownRelationshipGuids.Relationship__has_source__Class));
InstanceHandle ihDestinationClass = GetRelatedInstance(relationship, GetInstance(KnownRelationshipGuids.Relationship__has_destination__Class));
List<InstanceHandle> ignoreClasses = new List<InstanceHandle>();
ignoreClasses.Add(GetInstance(KnownInstanceGuids.Classes.Instance));
ignoreClasses.Add(GetInstance(KnownInstanceGuids.Classes.ExecutableReturningWorkData));
if (ihSourceClass != InstanceHandle.Empty)
{
if (!IsInstanceOf(source, ihSourceClass) && !ignoreClasses.Contains(ihSourceClass))
{
throw new InvalidOperationException(String.Format("Cannot assign relationship source class `{0}` to a type `{1}`", GetInstanceText(GetParentClass(source)), GetInstanceText(ihSourceClass)));
}
}
if (ihDestinationClass != InstanceHandle.Empty)
{
foreach (InstanceHandle target in targets)
{
if (!IsInstanceOf(target, ihDestinationClass) && !ignoreClasses.Contains(ihDestinationClass))
{
throw new InvalidOperationException(String.Format("Cannot assign relationship destination class `{0}` to a type `{1}`", GetInstanceText(GetParentClass(target)), GetInstanceText(ihDestinationClass)));
}
}
}
}
if (createSiblingRelationship)
{
InstanceHandle siblingRelationship = GetRelatedInstance(relationship, GetInstance(KnownRelationshipGuids.Relationship__has_sibling__Relationship));
if (siblingRelationship != InstanceHandle.Empty)
{
@ -381,6 +432,17 @@ public abstract class Oms
//* we use AssignRelationshipInternal here because calling the top-level function
//* would result in an infinite loop of sibling assignments
AssignRelationshipInternal(target, siblingRelationship, new InstanceHandle[] { source }, dt);
//? 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)
{
foreach (OmsStorage sto in Storages)
{
sto.WriteRelationship(this, target, siblingRelationship, new InstanceHandle[] { source }, dt);
}
}
}
}
}
}
@ -389,6 +451,13 @@ public abstract class Oms
public void ClearRelationship(InstanceHandle source, InstanceHandle relationship)
{
ClearRelationshipInternal(source, relationship);
if (!_InhibitStorage)
{
foreach (OmsStorage sto in Storages)
{
sto.ClearRelationship(this, source, relationship, DateTime.Now);
}
}
}
protected abstract bool TryGetInstanceInternal(Guid globalIdentifier, out InstanceHandle inst);
@ -527,12 +596,21 @@ public abstract class Oms
}
InstanceHandle ir = CreateInstance(guid);
SetParentClass(ir, ir_class.GetHandle());
int ct = CountInstances(ir_class.GetHandle());
InstanceKey pclass_key = GetInstanceKey(ir_class.GetHandle());
InstanceKey new_index = new InstanceKey(pclass_key.InstanceIndex, ct);
SetInstanceKey(ir, new_index);
DateTime dt = DateTime.Now;
if (!_InhibitStorage)
{
foreach (OmsStorage sto in Storages)
{
sto.CreateInstance(this, ir, ir_class.GetHandle(), dt);
}
}
SetParentClass(ir, ir_class.GetHandle());
return ir;
}
public T CreateInstanceOf<T>(IInstanceReference ir_class, Guid guid) where T : ConcreteInstanceWrapper
@ -744,6 +822,7 @@ public abstract class Oms
protected abstract void SetAttributeValueInternal(InstanceHandle source, InstanceHandle attribute, object value, DateTime effectiveDate);
public void SetAttributeValue(IInstanceReference source, InstanceHandle attribute, object value, DateTime? effectiveDate = null)
{
DateTime dt = effectiveDate.GetValueOrDefault(DateTime.Now);
InstanceHandle sh = source.GetHandle();
ValidateConstraintsForAttribute(sh, attribute, value);
@ -756,7 +835,15 @@ public abstract class Oms
_derivedData[sh][attribute] = value;
return;
}
SetAttributeValueInternal(sh, attribute, value, effectiveDate.GetValueOrDefault(DateTime.Now));
SetAttributeValueInternal(sh, attribute, value, dt);
if (!_InhibitStorage)
{
foreach (OmsStorage sto in Storages)
{
sto.WriteAttributeValue(this, source.GetHandle(), attribute, value, dt);
}
}
}
private void ValidateConstraintsForAttribute(InstanceHandle source, InstanceHandle attribute, object value)
@ -2450,4 +2537,15 @@ public abstract class Oms
}
return value;
}
private bool _InhibitStorage = false;
public void ReloadStorages()
{
_InhibitStorage = true;
foreach (OmsStorage sto in Storages)
{
sto.Load(this);
}
_InhibitStorage = false;
}
}

View File

@ -352,7 +352,14 @@ public class OmsMethodBuilder
{
InstanceHandle method = CreateMethodBase(Oms.GetInstance(KnownInstanceGuids.MethodClasses.BuildUIResponseMethod), forClassInstance, verb, name, accessModifier, isStatic);
InstanceHandle r_Build_UI_Response_Method__uses__Executable_returning_Element = Oms.GetInstance(KnownRelationshipGuids.Build_UI_Response_Method__uses__Executable_returning_Element);
if (usesExecutableReturningElement != InstanceHandle.Empty)
{
Oms.AssignRelationship(method, r_Build_UI_Response_Method__uses__Executable_returning_Element, usesExecutableReturningElement);
}
else
{
//? should we throw an exception here?
}
return new BuildUIResponseMethod(method);
}

View File

@ -0,0 +1,87 @@
// 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.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)
{
_parent = parent;
}
}
protected abstract void CreateInstanceInternal(Oms oms, InstanceHandle source, InstanceHandle parentClass, DateTime effectiveDate);
public void CreateInstance(Oms oms, InstanceHandle source, InstanceHandle parentClass, DateTime effectiveDate)
{
CreateInstanceInternal(oms, source, parentClass, effectiveDate);
if (AutoFlush) Flush();
}
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();
public void Close()
{
CloseInternal();
}
protected abstract void LoadInternal(Oms oms);
public void Load(Oms oms)
{
LoadInternal(oms);
}
}

View File

@ -0,0 +1,138 @@
// 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)
{
}
}

View File

@ -0,0 +1,147 @@
// 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")
{
}
}
}
}
}
}

View File

@ -0,0 +1,40 @@
// 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;
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)
{
Stream = stream;
}
protected override void FlushInternal()
{
Stream.Flush();
}
protected override void CloseInternal()
{
Stream.Close();
}
}

View File

@ -53,12 +53,21 @@ public abstract class OmsTestsBase
return oms;
}
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.";
@ -66,7 +75,7 @@ public abstract class OmsTestsBase
[SetUp]
public void Setup()
{
Oms = CreateOms();
ReloadOms();
// create the Test Class
InstanceHandle c_Class = Oms.GetInstance(KnownInstanceGuids.Classes.Class);

View File

@ -74,13 +74,16 @@ public class BasicTests : OmsTestsBase
InstanceHandle irTestClass2 = Oms.GetInstance(TEST_CLASS2_GUID);
Assert.That(irTestClass2, Is.Not.EqualTo(InstanceHandle.Empty));
InstanceHandle ihTestClassInstance = Oms.CreateInstanceOf(irTestClass);
InstanceHandle ihTestClassInstance2 = Oms.CreateInstanceOf(irTestClass2);
InstanceHandle irTestRelationship = Oms.GetInstance(TEST_REL_GUID);
Assert.That(irTestRelationship, Is.Not.EqualTo(InstanceHandle.Empty));
Oms.AssignRelationship(irTestClass, irTestRelationship, irTestClass2);
Oms.AssignRelationship(ihTestClassInstance, irTestRelationship, ihTestClassInstance2);
InstanceHandle irRelated = Oms.GetRelatedInstance(irTestClass, irTestRelationship);
Assert.That(irRelated, Is.EqualTo(irTestClass2));
InstanceHandle irRelated = Oms.GetRelatedInstance(ihTestClassInstance, irTestRelationship);
Assert.That(irRelated, Is.EqualTo(ihTestClassInstance2));
}
[Test]

View File

@ -86,4 +86,30 @@ public class RelationshipTests : OmsTestsBase
InstanceHandle ir = Oms.GetRelatedInstance(c_TestClass2Instance1, r_Test_Class_2__single_for__Test_Class);
Assert.That(ir, Is.EqualTo(c_TestClassInstance));
}
[Test]
public void CompatibleRelationshipAssignmentThrowsNothing()
{
InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class);
InstanceHandle ihSubClass = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute));
InstanceHandle ihRel = Oms.GetInstance(KnownRelationshipGuids.Class__has__Attribute);
Assert.That(delegate ()
{
Oms.AssignRelationship(ihClass, ihRel, ihSubClass);
}, Throws.Nothing);
}
[Test]
public void IncompatibleRelationshipAssignmentThrowsException()
{
InstanceHandle ihClass = Oms.GetInstance(KnownInstanceGuids.Classes.Class);
InstanceHandle ihSubClass = Oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute);
InstanceHandle ihRel = Oms.GetInstance(KnownRelationshipGuids.Class__valid_for__Work_Set);
Assert.That(delegate ()
{
Oms.AssignRelationship(ihClass, ihRel, ihSubClass);
}, Throws.InvalidOperationException);
}
}

View File

@ -0,0 +1,70 @@
// 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 Mocha.Core.Storage;
using Mocha.Testing;
namespace Mocha.Core.Tests.Storage;
public class JsonStorageTests : OmsTestsBase
{
[Test(Author = "Michael Becker", Description = "Verifies that the create instance, assign attribute, and assign relationship operations save to and load from storage.")]
public void BasicTests()
{
MemoryStream ms = new MemoryStream();
JsonOmsStorage json = new JsonOmsStorage(ms);
Oms.Storages.Add(json);
InstanceHandle ihTestClass = Oms.GetInstance(TEST_CLASS_GUID);
InstanceHandle ihTestClass2 = Oms.GetInstance(TEST_CLASS2_GUID);
InstanceHandle ihRel = Oms.GetInstance(TEST_REL_GUID);
InstanceHandle test = Oms.CreateInstanceOf(ihTestClass, new Guid("1e9b7101-13dc-4d97-9ae4-570a07bd3545"));
InstanceHandle test2 = Oms.CreateInstanceOf(ihTestClass2, new Guid("eea0defa-e93d-4b7e-bbe5-c3d654791335"));
Oms.SetAttributeValue(test, Oms.GetInstance(KnownAttributeGuids.Text.Name), "PICADILLY");
// check that it worked
string v = Oms.GetAttributeValue<string>(test, Oms.GetInstance(KnownAttributeGuids.Text.Name));
Assert.That(v, Is.EqualTo("PICADILLY"));
Oms.AssignRelationship(test, ihRel, test2);
json.Close();
byte[] data = ms.ToArray();
System.IO.File.WriteAllBytes("/tmp/test.json", data);
// reload the OMS from scratch
ReloadOms();
ms = new MemoryStream(data);
json = new JsonOmsStorage(ms);
// don't load the save data yet, check to make sure nothing's changed on the OMS
Oms.TryGetInstance(new Guid("1e9b7101-13dc-4d97-9ae4-570a07bd3545"), out test);
Assert.That(test, Is.EqualTo(InstanceHandle.Empty));
// now, load the save data
Oms.Storages.Add(json);
Oms.ReloadStorages();
// 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));
}
}