From 52b1012fd1a61b18e0e875b766323e41e761128f Mon Sep 17 00:00:00 2001 From: Michael Becker Date: Tue, 29 Oct 2024 17:03:24 -0400 Subject: [PATCH] update examples and fix major bug in library loader --- mocha-common | 2 +- mocha-dotnet-new | 27 +-- mocha-dotnet.sln | 7 + .../examples/mocha-dotnet-new/Program.cs | 59 +++++ .../Mocha.Oms.Server/Mocha.Oms.Server.csproj | 17 ++ .../src/app/Mocha.Oms.Server/Program.cs | 218 ++++++++++++++++++ .../src/lib/Mocha.Core/InstanceKey.cs | 14 ++ mocha-dotnet/src/lib/Mocha.Core/Library.cs | 3 + mocha-dotnet/src/lib/Mocha.Core/Oms.cs | 83 ++++++- mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs | 3 + .../OmsImplementations/MemoryOms.cs | 86 +++++-- .../src/lib/Mocha.Core/OmsStackTrace.cs | 56 +++++ .../McxMiniPlugin.cs | 15 ++ .../tests/Mocha.Core.Tests/BasicTests.cs | 18 ++ 14 files changed, 567 insertions(+), 41 deletions(-) create mode 100644 mocha-dotnet/examples/mocha-dotnet-new/Program.cs create mode 100644 mocha-dotnet/src/app/Mocha.Oms.Server/Mocha.Oms.Server.csproj create mode 100644 mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs create mode 100644 mocha-dotnet/src/lib/Mocha.Core/OmsStackTrace.cs diff --git a/mocha-common b/mocha-common index a40a8b3..8a98974 160000 --- a/mocha-common +++ b/mocha-common @@ -1 +1 @@ -Subproject commit a40a8b3685a377ab183eb0c906acf8c806b4e215 +Subproject commit 8a989742ca32c5ef46dbee115675f8605fd033c1 diff --git a/mocha-dotnet-new b/mocha-dotnet-new index 4bcfcee..15d32ab 100755 --- a/mocha-dotnet-new +++ b/mocha-dotnet-new @@ -16,7 +16,6 @@ ln -s $MOCHA_MOCHA_LIBRARIES_PATH system # since we cannot 'dotnet add reference', do it manually - it still works FILE=*.csproj - ROOT_NAMESPACE=MochaTest1 echo """ @@ -46,21 +45,15 @@ echo """ """ > $FILE -FILE=Program.cs +MOCHA_SHARE=/usr/share/mocha +cp $MOCHA_SHARE/examples/mocha-dotnet-new/Program.cs Program.cs -echo """ -// See https://get.mochapowered.com/mocha-dotnet-new for more information - -Mocha.Core.OmsImplementations.EmbeddedMiniOms oms = new Mocha.Core.OmsImplementations.EmbeddedMiniOms(); -oms.SystemLibraryResourceName = null; // \"mocha_test.resources.net.alcetech.Mocha.System.mcl\"; - -oms.Initialize(); - -Mocha.Core.LibraryHandle lh = oms.LoadLibrary(\"system/net.alcetech.Mocha.System.mcl\"); -oms.AddLibraryReference(lh); - -Mocha.Core.InstanceHandle ih = oms.GetInstance(Mocha.Core.KnownInstanceGuids.Classes.Class); - -Console.WriteLine(\"Hello, World! The \`Class\` GID is {0}\", oms.GetGlobalIdentifier(ih)); -""" > $FILE +echo "" +echo "Next steps:" +echo "" +echo "1. Edit the Program.cs file to make changes, or open the folder in" +echo " your favorite IDE." +echo "" +echo "2. Type 'dotnet run' to see the program in action." +echo "" diff --git a/mocha-dotnet.sln b/mocha-dotnet.sln index 22cded5..7186d3e 100644 --- a/mocha-dotnet.sln +++ b/mocha-dotnet.sln @@ -41,6 +41,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{B024 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Plugins.Libraries.McxMini", "mocha-dotnet\src\plugins\Mocha.Plugins.Libraries.McxMini\Mocha.Plugins.Libraries.McxMini.csproj", "{6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Oms.Server", "mocha-dotnet\src\app\Mocha.Oms.Server\Mocha.Oms.Server.csproj", "{07828A24-B888-4464-A158-F77AB61F6AD9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -71,6 +73,10 @@ Global {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Debug|Any CPU.Build.0 = Debug|Any CPU {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Release|Any CPU.ActiveCfg = Release|Any CPU {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF}.Release|Any CPU.Build.0 = Release|Any CPU + {07828A24-B888-4464-A158-F77AB61F6AD9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {07828A24-B888-4464-A158-F77AB61F6AD9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {07828A24-B888-4464-A158-F77AB61F6AD9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {07828A24-B888-4464-A158-F77AB61F6AD9}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -92,6 +98,7 @@ Global {20B7D199-322C-4942-85FE-46B90C75E92A} = {27C300F5-5172-4225-A6F7-3503B9007DD8} {B024FD23-7084-4DDF-A185-D58BEE7A006F} = {66EB3261-A473-41C7-8D40-E1B4DC8ED4B3} {6005DB73-30D8-4398-8B1F-5E23C0F2FFBF} = {B024FD23-7084-4DDF-A185-D58BEE7A006F} + {07828A24-B888-4464-A158-F77AB61F6AD9} = {11486802-8136-4958-8B32-FC34630B0306} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D28A9CF8-0235-4F8F-865F-C460BDCAE16D} diff --git a/mocha-dotnet/examples/mocha-dotnet-new/Program.cs b/mocha-dotnet/examples/mocha-dotnet-new/Program.cs new file mode 100644 index 0000000..694e073 --- /dev/null +++ b/mocha-dotnet/examples/mocha-dotnet-new/Program.cs @@ -0,0 +1,59 @@ +// See https://get.mochapowered.com/mocha-dotnet-new for more information + +using Mocha.Core; +using Mocha.Core.Oop; +using Mocha.Core.Oop.Methods; + +Mocha.Core.OmsImplementations.EmbeddedMiniOms oms = new Mocha.Core.OmsImplementations.EmbeddedMiniOms(); +oms.SystemLibraryResourceName = null; + +// Initialize the OMS +oms.Initialize(); + +// Load the System library +Mocha.Core.LibraryHandle lh = oms.LoadLibrary("system/net.alcetech.Mocha.System.mcl"); + +// Add the System library reference to the default tenant ('super') +oms.AddLibraryReference(lh); + +// ******** Add your own application code here... ******** + +// This makes sure that the `Class` class is defined. The class Class is the class of all the classes. +Mocha.Core.InstanceHandle clsClass = oms.GetInstance(Mocha.Core.KnownInstanceGuids.Classes.Class); +Console.WriteLine("Hello, World! The `Class` GID is {0}", oms.GetGlobalIdentifier(clsClass)); + +// This creates a System Attribute Routine that returns the string 'Hello world!'. +// To use it, define a Get Attribute by System Routine method that uses this System Attribute Routine. +SystemAttributeRoutine srHelloWorld = oms.SystemRoutineBuilder.CreateSystemAttributeRoutine(new Guid("{139eb04a-3da9-4b8a-bc9a-96bff240f013}"), delegate (Oms oms, OmsContext ctx) +{ + return "Hello from a System Attribute Routine! ZQ stack trace: " + ctx.StackTrace.ToString(); +}); + +// Define a Get Attribute by System Routine method that uses this System Attribute Routine. +// This Method is identified in UI as `Class@get Hello World(GAS)*S(public)` +Mocha.Core.InstanceHandle attValue = oms.GetInstance(Mocha.Core.KnownAttributeGuids.Text.Value); +MethodReturningAttribute mHello = oms.MethodBuilder.CreateGetAttributeBySystemRoutineMethod +( + clsClass, + "get", + "Hello World", + AccessModifier.Public, + true, + attValue, + srHelloWorld +); + +// Method Bindings allow us to separate method calls and parameter assignments from method definitions. +// A single Method can use parameters which are passed by Method Bindings to change what objects the Method acts upon. +ReturnAttributeMethodBinding mbHello = mHello.CreateMethodBinding(oms); + +// An OmsContext is necessary for executing methods. The context stores information about global variables (Work Data) +// as well as the ZQ call stack trace to aid in debugging. +OmsContext ctx = oms.CreateContext(); + +// ExecuteReturningAttributeValue is a convenience method that calls Execute on the given Method Binding, looks up the +// value stored in the returned Work Data, and returns that value. You can specify a type parameter to ensure that the +// returned value is of the appropriate type. Text Attribute returns string, Boolean Attribute returns bool, +// Numeric Attribute returns decimal, and Date Attribute returns System.DateTime +string value = oms.ExecuteReturningAttributeValue(ctx, mbHello); +Console.WriteLine(value); diff --git a/mocha-dotnet/src/app/Mocha.Oms.Server/Mocha.Oms.Server.csproj b/mocha-dotnet/src/app/Mocha.Oms.Server/Mocha.Oms.Server.csproj new file mode 100644 index 0000000..3c2342c --- /dev/null +++ b/mocha-dotnet/src/app/Mocha.Oms.Server/Mocha.Oms.Server.csproj @@ -0,0 +1,17 @@ + + + + + + + + + + + Exe + net8.0 + enable + enable + + + diff --git a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs new file mode 100644 index 0000000..451efb4 --- /dev/null +++ b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs @@ -0,0 +1,218 @@ +namespace Mocha.Oms.Server; + +using Mocha.Core; + +using MBS.Web; +using System; +using Mocha.Core.OmsImplementations; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.Json.Nodes; + +public class Program : WebApplication +{ + protected override int DefaultPort => 14678; + + public Program() + { + ShortName = "mocha-oms-server"; + } + + private LibraryHandle l_System, l_Web; + + private Oms oms; + + protected override void OnStartup(EventArgs e) + { + // this all has to be done before the Web server is started... + oms = new MemoryOms(); + + TenantHandle th = oms.CreateTenant("super"); + oms.SelectTenant(th); + + LibraryHandle lh = oms.LoadLibrary("/home/beckermj/Documents/Projects/mochapowered/mocha-dotnet/mocha-common/mocha-common/output/net.alcetech.Mocha.System.mcl"); + oms.AddLibraryReference(lh); + + LibraryHandle lh2 = oms.LoadLibrary("/home/beckermj/Documents/Projects/mochapowered/mocha-dotnet/mocha-common/mocha-common/output/net.alcetech.Mocha.Web.mcl"); + oms.AddLibraryReference(lh); + + // now we can start the Web server + base.OnStartup(e); + } + + protected override void OnProcessRequest(WebServerProcessRequestEventArgs e) + { + base.OnProcessRequest(e); + + StreamWriter sw = new StreamWriter(e.Context.Response.Stream); + if (e.Context.Request.Path == "/robots.txt") + { + + } + else if (e.Context.Request.PathParts.Length > 1) + { + if (e.Context.Request.PathParts[1] == "instances") + { + if (e.Context.Request.PathParts.Length > 2) + { + if (e.Context.Request.PathParts[2] == "create") + { + // usage: + // POST /instances/create + // + // parentClassIid=1$1&title=My+New+Class + if (e.Context.Request.Form.ContainsKey("parentClassIid")) + { + string pclass = e.Context.Request.Form["parentClassIid"]; + InstanceKey key = InstanceKey.Parse(pclass); + + if (oms.TryGetInstance(key, out InstanceHandle ihParentClass)) + { + InstanceHandle ihRet = oms.CreateInstanceOf(ihParentClass); + + if (e.Context.Request.Form.ContainsKey("title")) + { + oms.SetAttributeValue(ihRet, oms.GetInstance(KnownAttributeGuids.Text.Name), e.Context.Request.Form["title"]); + } + + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + obj.Add("result", JsonValue.Create("success")); + obj.Add("iid", JsonValue.Create(oms.GetInstanceKey(ihRet).ToString())); + sw.Write(obj.ToJsonString()); + } + else + { + e.Context.Response.ResponseCode = 422; + e.Context.Response.ResponseText = "Unprocessable Content"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + obj.Add("result", JsonValue.Create("failure")); + obj.Add("message", JsonValue.Create("parent class instance does not exist")); + sw.Write(obj.ToJsonString()); + } + } + } + else if (e.Context.Request.PathParts[2] == "search") + { + string query = ""; + if (e.Context.Request.Query.ContainsKey("q") && e.Context.Request.Query["q"].Count > 0) + { + query = e.Context.Request.Query["q"][0]; + } + + string parentClassIid = null; + InstanceHandle ihParentClass = InstanceHandle.Empty; + if (e.Context.Request.Query.ContainsKey("parentClassIid") && e.Context.Request.Query["parentClassIid"].Count > 0) + { + parentClassIid = e.Context.Request.Query["parentClassIid"][0]; + if (InstanceKey.TryParse(parentClassIid, out InstanceKey ikParentClass)) + { + ihParentClass = oms.GetInstance(ikParentClass); + } + } + + IEnumerable ihs = null; + if (ihParentClass != InstanceHandle.Empty) + { + ihs = oms.GetInstancesOf(ihParentClass); + } + else + { + ihs = oms.GetInstances(); + } + + if (!String.IsNullOrEmpty(query)) + { + List list = new List(); + foreach (InstanceHandle ih in ihs) + { + string text = oms.GetInstanceText(ih); + if (text.ToLower().Contains(query)) + { + list.Add(ih); + } + } + ihs = list; + } + + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + + JsonArray objInstances = InstancesToJson(ihs); + obj.Add("instances", objInstances); + + sw.Write(obj.ToJsonString()); + } + } + else + { + e.Context.Response.ResponseCode = 200; + e.Context.Response.ResponseText = "OK"; + e.Context.Response.ContentType = "application/json"; + + JsonObject obj = new JsonObject(); + + Stopwatch watch = new Stopwatch(); + watch.Start(); + + IEnumerable ihs = oms.GetInstances(); + + watch.Stop(); + obj.Add("time", JsonValue.Create(watch.ElapsedMilliseconds.ToString())); + + JsonArray objInstances = InstancesToJson(ihs); + obj.Add("instances", objInstances); + + sw.Write(obj.ToJsonString()); + } + } + } + else + { + e.Context.Response.ResponseCode = 404; + e.Context.Response.ResponseText = "Not Found"; + e.Context.Response.ContentType = "text/html"; + + sw.WriteLine("

Not Found

"); + sw.WriteLine("

" + e.Context.Request.Path + "

"); + } + sw.Flush(); + sw.Close(); + } + + private JsonArray InstancesToJson(IEnumerable ihs) + { + JsonArray array = new JsonArray(); + foreach (InstanceHandle ih in ihs) + { + InstanceHandle parentClass = oms.GetParentClass(ih); + + JsonObject objInstance = new JsonObject(); + objInstance.Add("iid", JsonValue.Create(oms.GetInstanceKey(ih).ToString())); + objInstance.Add("text", JsonValue.Create(oms.GetInstanceText(ih))); + + JsonObject objParentClass = new JsonObject(); + objParentClass.Add("iid", JsonValue.Create(oms.GetInstanceKey(parentClass).ToString())); + objParentClass.Add("text", JsonValue.Create(oms.GetInstanceText(parentClass))); + objInstance.Add("parentClass", objParentClass); + + array.Add(objInstance); + } + return array; + } + + public static int Main(string[] args) + { + return (new Program()).Start(); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs b/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs index 1307ac8..4d9c5c8 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs @@ -52,6 +52,20 @@ namespace Mocha.Core { return new InstanceKey(instanceKey); } + public static bool TryParse(string instanceKey, out InstanceKey value) + { + try + { + value = InstanceKey.Parse(instanceKey); + return true; + } + catch + { + } + + value = InstanceKey.Empty; + return false; + } public InstanceKey(string instanceKey) { diff --git a/mocha-dotnet/src/lib/Mocha.Core/Library.cs b/mocha-dotnet/src/lib/Mocha.Core/Library.cs index 2b9b457..891fe1a 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Library.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Library.cs @@ -11,7 +11,10 @@ public class Library { } + public Guid GlobalIdentifier { get; set; } = Guid.Empty; + public LibraryInstance.LibraryInstanceCollection Instances { get; } = new LibraryInstance.LibraryInstanceCollection(); public List Attributes { get; } = new List(); public List Relationships { get; } = new List(); + public List LibraryReferences { get; } = new List(); } diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs index f405314..b1a8574 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs @@ -344,6 +344,11 @@ public abstract class Oms InstanceHandle ir = CreateInstance(guid); SetParentClass(ir, ir_class); + + int ct = CountInstances(ir_class); + InstanceKey pclass_key = GetInstanceKey(ir_class); + InstanceKey new_index = new InstanceKey(pclass_key.InstanceIndex, ct); + SetInstanceKey(ir, new_index); return ir; } @@ -381,10 +386,12 @@ public abstract class Oms ClearRelationship(ir, GetInstance(KnownRelationshipGuids.Instance__for__Class)); AssignRelationship(ir, GetInstance(KnownRelationshipGuids.Instance__for__Class), ir_parent); - AssignRelationship(ir_parent, GetInstance(KnownRelationshipGuids.Class__has__Instance), ir); + + // FIXME: remove in future release; sibling relationships are now properly handled in ValidateConstraintsForRelationship + // AssignRelationship(ir_parent, GetInstance(KnownRelationshipGuids.Class__has__Instance), ir); int ct = CountInstances(ir_parent); - SetInstanceKey(ir, new InstanceKey(GetInstanceKey(ir_parent).InstanceIndex, ct)); + SetInstanceKey(ir, new InstanceKey(GetInstanceKey(ir_parent).InstanceIndex, ct - 1)); } public int CountInstances(InstanceHandle ir_parent) @@ -395,7 +402,8 @@ public abstract class Oms public InstanceHandle GetParentClass(InstanceHandle ir) { - IEnumerable irs = GetRelatedInstances(ir, GetInstance(KnownRelationshipGuids.Instance__for__Class)); + InstanceHandle relInstance__for__Class = GetInstance(KnownRelationshipGuids.Instance__for__Class); + IEnumerable irs = GetRelatedInstances(ir, relInstance__for__Class); if (irs.Count() > 0) { return irs.First(); @@ -794,6 +802,10 @@ public abstract class Oms InstanceHandle parentClass = GetParentClass(methodOrMethodBinding); Guid parentClassId = GetGlobalIdentifier(parentClass); + context.StackTrace.Push(methodOrMethodBinding); + + InstanceHandle? retval = null; + if (IsInstanceOf(methodOrMethodBinding, GetInstance(KnownInstanceGuids.Classes.MethodBinding))) /* if (IsInstanceOf(methodOrMethodBinding, GetInstance(KnownInstanceGuids.Classes.ReturnAttributeMethodBinding)) @@ -813,7 +825,7 @@ public abstract class Oms } context.SetWorkData(assignsToParm, assignsFromWorkData); } - return ExecuteMethodBinding(context, methodOrMethodBinding); + retval = ExecuteMethodBinding(context, methodOrMethodBinding); } else { @@ -825,10 +837,18 @@ public abstract class Oms InstanceHandle pclassInstance = GetParentClass(hh); context.SetWorkData(pclassInstance, hh); } - return methodImplementations[parentClassId].Execute(this, context, methodOrMethodBinding); + retval = methodImplementations[parentClassId].Execute(this, context, methodOrMethodBinding); } } - throw new NotImplementedException(String.Format("method implementation not found for method class {0}", GetGlobalIdentifier(parentClass))); + if (retval == null) + { + throw new NotImplementedException(String.Format("method implementation not found for method class {0}", GetGlobalIdentifier(parentClass))); + } + else + { + context.StackTrace.Pop(); + return retval.Value; + } } private InstanceHandle ExecuteMethodBinding(OmsContext context, InstanceHandle methodBinding) @@ -982,7 +1002,33 @@ public abstract class Oms return sb.ToString(); } - public Dictionary _systemRoutinesByInstance = new Dictionary(); + private OmsSystemRoutineBuilder _systemRoutineBuilder = null; + public OmsSystemRoutineBuilder SystemRoutineBuilder + { + get + { + if (_systemRoutineBuilder == null) + { + _systemRoutineBuilder = new OmsSystemRoutineBuilder(this); + } + return _systemRoutineBuilder; + } + } + + private OmsMethodBuilder _methodBuilder = null; + public OmsMethodBuilder MethodBuilder + { + get + { + if (_methodBuilder == null) + { + _methodBuilder = new OmsMethodBuilder(this); + } + return _methodBuilder; + } + } + + private Dictionary _systemRoutinesByInstance = new Dictionary(); internal void RegisterSystemRoutine(Guid globalIdentifier, SystemRoutine routine) { _systemRoutinesByInstance[globalIdentifier] = routine; @@ -1158,6 +1204,20 @@ public abstract class Oms } return LibraryHandle.Empty; } + private Dictionary _librariesGuids = new Dictionary(); + /// + /// Gets the for the library with the given Global Identifier. + /// + /// + /// + public LibraryHandle GetLibrary(Guid globalIdentifier) + { + if (_librariesGuids.ContainsKey(globalIdentifier)) + { + return _librariesGuids[globalIdentifier]; + } + return LibraryHandle.Empty; + } /// /// Loads the specified library file into memory. To add it to the tenant, @@ -1214,7 +1274,6 @@ public abstract class Oms lib.Link(); InitializeLibrary(lh, lib); - return lh; } @@ -1227,6 +1286,7 @@ public abstract class Oms /// public LibraryHandle LoadLibrary(string filename) { + Console.WriteLine("oms: LoadLibrary '{0}'", filename); // some examples: // .mcl compiled binary library file (fastest?) // /path/to/directory containing a bunch of XML / JSON / YAML files @@ -1295,6 +1355,7 @@ public abstract class Oms private void InitializeLibrary(LibraryHandle lh, Library lib) { InitializeLibraryInternal(lh, lib); + _librariesGuids[lib.GlobalIdentifier] = lh; } public string? GetTenantName(TenantHandle tenant) @@ -1325,4 +1386,10 @@ public abstract class Oms } return false; } + + protected abstract IEnumerable GetInstancesInternal(); + public IEnumerable GetInstances() + { + return GetInstancesInternal(); + } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs index 5cd3319..02f2d04 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsContext.cs @@ -24,9 +24,12 @@ namespace Mocha.Core; public class OmsContext { public Oms Oms { get; } + public OmsStackTrace StackTrace { get; } + internal OmsContext(Oms oms) { Oms = oms; + StackTrace = new OmsStackTrace(this); } private Dictionary _WorkData = new Dictionary(); diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs index ae6b17c..2cc28ed 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/MemoryOms.cs @@ -63,6 +63,15 @@ public class MemoryOms : Oms { return _keysByInstance[instance]; } + foreach (LibraryHandle lh in _libraryReferences) + { + _Tenant lib = oms._libraries[lh]; + InstanceKey ik = lib.GetInstanceKey(instance); + if (ik != InstanceKey.Empty) + { + return ik; + } + } return InstanceKey.Empty; } @@ -97,6 +106,19 @@ public class MemoryOms : Oms ih = _instancesByKey[ik]; return true; } + foreach (LibraryHandle lh in _libraryReferences) + { + if (oms._libraries.ContainsKey(lh)) + { + if (oms._libraries[lh].TryGetInstance(ik, out ih)) + { + // LibraryInst_g g = new LibraryInst_g() { InstanceHandle = ih, SourceLibrary = lh, GlobalIdentifier = globalIdentifier }; + // _libraries[lh]._libraryInst_gs[globalIdentifier] = g; + // _libraries[lh]._libraryInst_is[ih] = g; + return true; + } + } + } ih = InstanceHandle.Empty; return false; } @@ -151,11 +173,21 @@ public class MemoryOms : Oms } public IReadOnlyCollection GetRelatedInstances(InstanceHandle source, InstanceHandle relationship, DateTime effectiveDate) { + List list2 = new List(); + + foreach (LibraryHandle lh in _libraryReferences) + { + if (oms._libraries.ContainsKey(lh)) + { + IReadOnlyCollection insts2 = oms._libraries[lh].GetRelatedInstances(source, relationship, effectiveDate); + list2.AddRange(insts2); + } + } + if (_Relationships.ContainsKey(source)) { if (_Relationships[source].ContainsKey(relationship)) { - List list2 = new List(); List list = _Relationships[source][relationship]; foreach (RelationshipValue val in list) { @@ -164,22 +196,9 @@ public class MemoryOms : Oms list2.AddRange(val.TargetInstances); } } - return list2.ToArray(); } } - - foreach (LibraryHandle lh in _libraryReferences) - { - if (oms._libraries.ContainsKey(lh)) - { - IReadOnlyCollection insts2 = oms._libraries[lh].GetRelatedInstances(source, relationship, effectiveDate); - if (insts2.Count > 0) - { - return insts2; - } - } - } - return new InstanceHandle[0]; + return list2; } public bool HasAttributeValue(InstanceHandle source, InstanceHandle attribute, DateTime effectiveDate) @@ -271,6 +290,18 @@ public class MemoryOms : Oms if (_libraryReferences.Contains(library)) _libraryReferences.Remove(library); } + + public IEnumerable GetInstances() + { + List list = new List(); + foreach (LibraryHandle lh in _libraryReferences) + { + IEnumerable ihs2 = oms._libraries[lh].GetInstances(); + list.AddRange(ihs2); + } + list.AddRange(_instancesByGuid.Values); + return list; + } } private Dictionary _tenantData = new Dictionary(); @@ -363,9 +394,21 @@ public class MemoryOms : Oms protected override void InitializeLibraryInternal(LibraryHandle lh, Library data) { _Tenant lib = new _Tenant(this); + + // hack + _Tenant old = _CurrentTenantData; + _CurrentTenantData = lib; + + foreach (Guid iid in data.LibraryReferences) + { + lib.AddLibraryReference(GetLibrary(iid)); + } foreach (LibraryInstance inst in data.Instances) { + // Console.WriteLine("library init: created instance '{0}'", inst.InstanceGuid.ToString("B")); + InstanceHandle ih = lib.CreateInstance(inst.InstanceGuid); + SetInstanceKey(ih, inst.InstanceKey); // LibraryInst_g lig = new LibraryInst_g() { GlobalIdentifier = inst.InstanceGuid, InstanceHandle = ih, SourceLibrary = lh }; // _libraryInst_is[ih] = lig; @@ -376,19 +419,27 @@ public class MemoryOms : Oms if (lib.TryGetInstance(att.SourceInstanceGuid, out InstanceHandle src) && lib.TryGetInstance(att.AttributeInstanceGuid, out InstanceHandle dst)) { + // Console.WriteLine("library init: set attribute value {0} . {1} = '{2}'", att.SourceInstanceGuid.ToString("B"), att.AttributeInstanceGuid.ToString("B"), att.Value); + lib.SetAttributeValue(src, dst, att.Value, DateTime.Now); } } foreach (LibraryRelationship rel in data.Relationships) { + // here's the prroblem: rel.SourceInstance succeeds, rel.RelationshipInstance failss because it's defined on the other library if (lib.TryGetInstance(rel.SourceInstanceGuid, out InstanceHandle src) && lib.TryGetInstance(rel.RelationshipInstanceGuid, out InstanceHandle dst) && lib.TryGetInstance(rel.TargetInstanceGuid, out InstanceHandle tgt)) { + // Console.WriteLine("library init: assign relationship {0} . {1} = {2}", rel.SourceInstanceGuid.ToString("B"), rel.RelationshipInstanceGuid.ToString("B"), rel.TargetInstanceGuid.ToString("B")); + lib.AssignRelationship(src, dst, new InstanceHandle[] { tgt }, DateTime.Now); } } _libraries[lh] = lib; + + // hack + _CurrentTenantData = old; } private struct LibraryInst_g @@ -478,4 +529,9 @@ public class MemoryOms : Oms _CurrentTenantData.SetAttributeValue(source, attribute, value, effectiveDate); } + protected override IEnumerable GetInstancesInternal() + { + return _CurrentTenantData.GetInstances(); + } + } diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsStackTrace.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsStackTrace.cs new file mode 100644 index 0000000..c746c42 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsStackTrace.cs @@ -0,0 +1,56 @@ +// Copyright (C) 2024 Michael Becker +// +// 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 . + +using System.Text; + +namespace Mocha.Core; + +public class OmsStackTrace +{ + private Stack stack = new Stack(); + + private OmsContext parent; + internal OmsStackTrace(OmsContext parent) + { + this.parent = parent; + } + + public void Push(InstanceHandle ih) + { + stack.Push(ih); + } + public InstanceHandle Pop() + { + return stack.Pop(); + } + + public override string ToString() + { + StringBuilder sb = new StringBuilder(); + + int i = 0; + foreach (InstanceHandle ih in stack) + { + sb.Append(parent.Oms.GetInstanceKey(ih)); + if (i < stack.Count - 1) + { + sb.Append(':'); + } + } + return sb.ToString(); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs index ee13564..2d76865 100644 --- a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs +++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs @@ -54,7 +54,9 @@ public class McxMiniLibraryPlugin : LibraryPlugin McxMiniFlags flags = (McxMiniFlags)r.ReadInt32(); int sectionsCount = r.ReadInt32(); + Guid libraryGuid = r.ReadGuid(); + library.GlobalIdentifier = libraryGuid; Dictionary sections = new Dictionary(); for (int i = 0; i < sectionsCount; i++) @@ -86,11 +88,17 @@ public class McxMiniLibraryPlugin : LibraryPlugin McxSection attributesSection = sections["Attributes"]; McxSection relationshipsSection = sections["Relationships"]; McxSection stringTableSection = sections["StringTable"]; + McxSection resourcesSection = new McxSection(); if (sections.ContainsKey("Resources")) { resourcesSection = sections["Resources"]; } + McxSection libraryRefsSection = new McxSection(); + if (sections.ContainsKey("LibraryRefs")) + { + libraryRefsSection = sections["LibraryRefs"]; + } if (guidsSection.Count != instancesSection.Count) { @@ -161,6 +169,13 @@ public class McxMiniLibraryPlugin : LibraryPlugin library.Attributes.Add(new LibraryAttribute(attributes[i].SourceInstanceGuid, attributes[i].AttributeInstanceGuid, stringTable[attributes[i].ValueIndex])); } + r.BaseStream.Seek(libraryRefsSection.Offset, SeekOrigin.Begin); + for (int i = 0; i < libraryRefsSection.Count; i++) + { + Guid guid = r.ReadGuid(); + library.LibraryReferences.Add(guid); + } + if (resourcesSection.Length > 0) { diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/BasicTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/BasicTests.cs index c1220bb..bc5a478 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/BasicTests.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/BasicTests.cs @@ -149,5 +149,23 @@ public class BasicTests : OmsTestsBase InstanceHandle ihTextAttribute2 = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute); Assert.That(ihTextAttribute2, Is.EqualTo(ihTextAttribute)); } + + [Test] + public void Class_GetInstance_returns_same_InstanceHandle_before_and_after_AddLibraryReference() + { + // remove the Web reference just for this test + // Oms.RemoveLibraryReference(lhWeb); + + InstanceHandle ihTextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.Class); + + // load the other library, but do not reference it yet + LibraryHandle lhWeb = Oms.LoadLibrary(typeof(LibraryLoaderTests), "Mocha.Core.Tests.Resources.net.alcetech.Mocha.Web.mcl"); + + // add back the reference to the other library + Oms.AddLibraryReference(lhWeb); + + InstanceHandle ihTextAttribute2 = Oms.GetInstance(KnownInstanceGuids.Classes.Class); + Assert.That(ihTextAttribute2, Is.EqualTo(ihTextAttribute)); + } } \ No newline at end of file