diff --git a/.gitignore b/.gitignore index 8e1b336..b1d74b4 100644 --- a/.gitignore +++ b/.gitignore @@ -418,6 +418,10 @@ FodyWeavers.xsd # Mocha CUP output directory output +# Mocha compiled files +*.mcl +*.mcx + # Backups (made by Notepad++) *.bak diff --git a/mocha-common b/mocha-common index 94de4f0..47d874d 160000 --- a/mocha-common +++ b/mocha-common @@ -1 +1 @@ -Subproject commit 94de4f0a8e855543c0f1b0679aebf7caa2db56c9 +Subproject commit 47d874d77af1f29f7870ee6d885cbf6138b7651d diff --git a/mocha-dotnet.sln b/mocha-dotnet.sln index 1923f04..6437a58 100644 --- a/mocha-dotnet.sln +++ b/mocha-dotnet.sln @@ -55,6 +55,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Oms.Server.Tests", "m EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Testing", "mocha-dotnet\src\lib\Mocha.Testing\Mocha.Testing.csproj", "{5DE4A4AF-9B11-4844-B9C3-D93E17536267}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Compilers.XmlPreprocessor", "mocha-dotnet\src\app\Mocha.Compilers.XmlPreprocessor\Mocha.Compilers.XmlPreprocessor.csproj", "{EA82944B-9720-4126-898F-3E3EDDAE44F4}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Modeling", "mocha-dotnet\src\lib\Mocha.Modeling\Mocha.Modeling.csproj", "{E3DB95D7-9B56-4624-9961-50B0CDAFDC78}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Modeling.Tests", "mocha-dotnet\tests\Mocha.Modeling.Tests\Mocha.Modeling.Tests.csproj", "{B2A51995-3440-45C5-BB84-FF49A26CF00B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -113,6 +119,18 @@ Global {5DE4A4AF-9B11-4844-B9C3-D93E17536267}.Debug|Any CPU.Build.0 = Debug|Any CPU {5DE4A4AF-9B11-4844-B9C3-D93E17536267}.Release|Any CPU.ActiveCfg = Release|Any CPU {5DE4A4AF-9B11-4844-B9C3-D93E17536267}.Release|Any CPU.Build.0 = Release|Any CPU + {EA82944B-9720-4126-898F-3E3EDDAE44F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EA82944B-9720-4126-898F-3E3EDDAE44F4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EA82944B-9720-4126-898F-3E3EDDAE44F4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EA82944B-9720-4126-898F-3E3EDDAE44F4}.Release|Any CPU.Build.0 = Release|Any CPU + {E3DB95D7-9B56-4624-9961-50B0CDAFDC78}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E3DB95D7-9B56-4624-9961-50B0CDAFDC78}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E3DB95D7-9B56-4624-9961-50B0CDAFDC78}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E3DB95D7-9B56-4624-9961-50B0CDAFDC78}.Release|Any CPU.Build.0 = Release|Any CPU + {B2A51995-3440-45C5-BB84-FF49A26CF00B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B2A51995-3440-45C5-BB84-FF49A26CF00B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B2A51995-3440-45C5-BB84-FF49A26CF00B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B2A51995-3440-45C5-BB84-FF49A26CF00B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -141,6 +159,9 @@ Global {ADD7359E-0E3B-4435-B812-EC961C52DA2A} = {A2C401E9-FED4-43BA-A928-566239894CEE} {271CBF6A-07E4-46EB-A418-513E9FD89E36} = {27C300F5-5172-4225-A6F7-3503B9007DD8} {5DE4A4AF-9B11-4844-B9C3-D93E17536267} = {A2C401E9-FED4-43BA-A928-566239894CEE} + {EA82944B-9720-4126-898F-3E3EDDAE44F4} = {11486802-8136-4958-8B32-FC34630B0306} + {E3DB95D7-9B56-4624-9961-50B0CDAFDC78} = {A2C401E9-FED4-43BA-A928-566239894CEE} + {B2A51995-3440-45C5-BB84-FF49A26CF00B} = {27C300F5-5172-4225-A6F7-3503B9007DD8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D28A9CF8-0235-4F8F-865F-C460BDCAE16D} diff --git a/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Mocha.Compilers.XmlPreprocessor.csproj b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Mocha.Compilers.XmlPreprocessor.csproj new file mode 100644 index 0000000..8e9683e --- /dev/null +++ b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Mocha.Compilers.XmlPreprocessor.csproj @@ -0,0 +1,16 @@ + + + + Exe + net8.0 + enable + enable + + + + + + + + + diff --git a/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/ElementContent.cs b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/ElementContent.cs new file mode 100644 index 0000000..9930809 --- /dev/null +++ b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/ElementContent.cs @@ -0,0 +1,9 @@ +using System; + +namespace Mocha.Compilers.XmlPreprocessor.ObjectModel; + +public class ElementContent : Instance +{ + public ElementContent() : base() { } + public ElementContent(InstanceReference reference) : base(reference) { } +} diff --git a/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/Instance.cs b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/Instance.cs new file mode 100644 index 0000000..afe2b8b --- /dev/null +++ b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/Instance.cs @@ -0,0 +1,17 @@ +using System; + +namespace Mocha.Compilers.XmlPreprocessor.ObjectModel; + +public class Instance +{ + public InstanceReference? Reference { get; } + + public Instance() + { + Reference = null; + } + public Instance(InstanceReference reference) + { + Reference = reference; + } +} diff --git a/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/Task.cs b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/Task.cs new file mode 100644 index 0000000..7f6efb6 --- /dev/null +++ b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/ObjectModel/Task.cs @@ -0,0 +1,24 @@ +using System; + +namespace Mocha.Compilers.XmlPreprocessor.ObjectModel; + +public class Task : Instance +{ + public Task() : base() { } + public Task(InstanceReference reference) : base(reference) { } + + public class TaskSelection + { + public List ElementContents { get; } = new List(); + + } + public class TaskProcessing + { + public List ElementContents { get; } = new List(); + + } + + public TaskSelection Selection { get; } = new TaskSelection(); + public TaskProcessing Processing { get; } = new TaskProcessing(); + +} diff --git a/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Parser.cs b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Parser.cs new file mode 100644 index 0000000..7d26b68 --- /dev/null +++ b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Parser.cs @@ -0,0 +1,81 @@ +// Copyright (C) 2025 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.Xml; +using Mocha.Compilers.XmlPreprocessor.ObjectModel; + +class Parser +{ + public Dictionary EntityDefinitions { get; } = new Dictionary(); + private string EvaluateEntityDefinitions(string value) + { + foreach (KeyValuePair kvp in EntityDefinitions) + { + value = value.Replace(String.Concat("@", kvp.Key, ";"), kvp.Value); + } + return value; + } + + public List Instances { get; } = new List(); + + public void Load(Stream st) + { + XmlDocument xd = new XmlDocument(); + xd.Load(st); + + if (xd["task"] is XmlNode x_task) + { + LoadTask(x_task); + } + } + + private void LoadTask(XmlNode x_task) + { + Mocha.Compilers.XmlPreprocessor.ObjectModel.Task task = new Mocha.Compilers.XmlPreprocessor.ObjectModel.Task(); + if (x_task["selection"]?["layout"] is XmlNode x_task_selection_layout) + { + List list = new List(); + + foreach (XmlNode xn in x_task_selection_layout) + { + if (xn.Name == "instance") + { + if (xn.Attributes?["globalIdentifier"] is XmlAttribute attrGlobalIdentifier) + { + task.Selection.ElementContents.Add(new ElementContent(new InstanceReference(Guid.Parse(EvaluateEntityDefinitions(attrGlobalIdentifier.Value))))); + } + } + } + } + if (x_task["processing"]?["layout"] is XmlNode x_task_processing_layout) + { + List list = new List(); + + foreach (XmlNode xn in x_task_processing_layout) + { + if (xn.Name == "instance") + { + if (xn.Attributes?["globalIdentifier"] is XmlAttribute attrGlobalIdentifier) + { + task.Processing.ElementContents.Add(new ElementContent(new InstanceReference(Guid.Parse(EvaluateEntityDefinitions(attrGlobalIdentifier.Value))))); + } + } + } + } + Instances.Add(task); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Program.cs b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Program.cs new file mode 100644 index 0000000..40f5ce6 --- /dev/null +++ b/mocha-dotnet/src/app/Mocha.Compilers.XmlPreprocessor/Program.cs @@ -0,0 +1,30 @@ +using System.Xml; +using Mocha.Core; +using Mocha.Plugins.Libraries.McxMini; + +namespace Mocha.Compilers.XmlPreprocesssor; + +public class Program +{ + + public static void Main(string[] args) + { + string filename = "/home/beckermj/Documents/Projects/mochapowered/mocha-dotnet/mocha-common/mocha-common/data/libraries/yaml/net.alcetech.Mocha.System/005-UserInterface/Tasks/WorkSet.xml"; + + System.IO.FileStream fs = System.IO.File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read); + + Parser p = new Parser(); + + Library libSystem = new Library(); + McxMiniLibraryPlugin plug = new McxMiniLibraryPlugin(); + plug.Load("/home/beckermj/Documents/Projects/mochapowered/mocha-dotnet/mocha-common/mocha-common/output/net.alcetech.Mocha.System.mcl", libSystem); + + // yay! + foreach (KeyValuePair kvp in libSystem.EntityDefinitions) + { + p.EntityDefinitions[kvp.Key] = kvp.Value; + } + + p.Load(fs); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs index 25549da..bd1b610 100644 --- a/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs +++ b/mocha-dotnet/src/app/Mocha.Oms.Server/Program.cs @@ -176,10 +176,16 @@ public class Program : WebApplication if (jo.ContainsKey("name")) { string name = jo["name"].GetValue(); - Log.WriteLine("oms-dotnet: Mochafile says create tenant '{0}'", name); + int count = 1; - TenantHandle th = Oms.CreateTenant(name); - Log.WriteLine("oms-dotnet: create tenant ok, handle is '{0}'", th); + if (jo.ContainsKey("count")) + { + count = jo["count"].GetValue(); + } + for (int i = 0; i < count; i++) + { + CreateTenant2(name); + } } } } @@ -189,6 +195,39 @@ public class Program : WebApplication return true; } + private void CreateTenant2(string name) + { + int ct = 0; + TenantHandle th = Oms.GetTenantByName(name); + if (th != TenantHandle.Empty) + { + while (true) + { + ct++; + string name2 = String.Concat(name, ct.ToString()); + Log.WriteLine("oms-dotnet: Mochafile says create tenant '{0}'", name2); + + th = Oms.GetTenantByName(name2); + if (th == TenantHandle.Empty) + { + th = Oms.CreateTenant(name2); + break; + } + } + } + else + { + Log.WriteLine("oms-dotnet: Mochafile says create tenant '{0}'", name); + th = Oms.CreateTenant(name); + } + + Oms.SelectTenant(th); + Oms.AddLibraryReference(Oms.GetLibrary("net.alcetech.Mocha.System")); + Oms.AddLibraryReference(Oms.GetLibrary("net.alcetech.Mocha.Web")); + + Log.WriteLine("oms-dotnet: create tenant ok, handle is '{0}'", th); + } + private bool TryLoadLibrary(Oms oms, string path, string fileName) { string fullyQualifiedFileName = System.IO.Path.Combine(new string[] { path, fileName }); diff --git a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs index 01d29e6..0a330b9 100644 --- a/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs +++ b/mocha-dotnet/src/lib/Mocha.Core.UI.Server/Commands/ElementCommand.cs @@ -120,7 +120,7 @@ public class ElementCommand : InstanceCommand } else if (oms.IsInstanceOf(ProcessingInstance, oms.GetInstance(KnownInstanceGuids.Classes.Task))) { - RespndWithTask(oms, sw, e.Context, ctx, ProcessingInstance); + RespondWithTask(oms, sw, e.Context, ctx, ProcessingInstance); } else { @@ -131,25 +131,43 @@ public class ElementCommand : InstanceCommand if (defaultTask != InstanceHandle.Empty) { ctx.InitiatingInstance = ProcessingInstance; - RespndWithTask(oms, sw, e.Context, ctx, defaultTask); + RespondWithTask(oms, sw, e.Context, ctx, defaultTask); } } } } - private void RespndWithTask(Oms oms, StreamWriter sw, WebContext context, OmsContext omsContext, InstanceHandle task) + private void RespondWithTask(Oms oms, StreamWriter sw, WebContext context, OmsContext omsContext, InstanceHandle task) { // fill out the initiating element and execute the task InstanceHandle elem = oms.GetRelatedInstance(task, oms.GetInstance(KnownRelationshipGuids.Task__has_initiating__Element)); omsContext.InitiatingTask = task; - Response r = oms.ProcessElement(omsContext, elem, null); - JsonObject obj = r.GetResponse(oms, omsContext); + if (context.Request.Method == "POST") + { + // !! FIXME !! this doesn't work + // POST http://localhost:4436/tenants/super/instances/2997$1/element + // relatedInstance=4170$1 - /* - - */ - - RespondWithJson(oms, sw, context, 200, "OK", obj); + Response r = oms.ProcessElement(omsContext, elem, context.Request.Form); + JsonObject obj = r.GetResponse(oms, omsContext); + + /* + + */ + + RespondWithJson(oms, sw, context, 200, "OK", obj); + } + else + { + Response r = oms.ProcessElement(omsContext, elem, null); + JsonObject obj = r.GetResponse(oms, omsContext); + + /* + + */ + + RespondWithJson(oms, sw, context, 200, "OK", obj); + } } } \ 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 2d9438d..7e7c986 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/InstanceKey.cs @@ -158,16 +158,11 @@ namespace Mocha.Core return String.Format("{0}${1}", ClassIndex, InstanceIndex); } - internal string GetDerivedDataString(Dictionary? derivedData = null) + internal static string GetDerivedDataString(Dictionary derivedData) { MemoryStream ms = new MemoryStream(); BinaryWriter bw = new BinaryWriter(ms); - if (derivedData == null) - { - derivedData = GetDerivedData(); - } - foreach (KeyValuePair kvp in derivedData) { if (kvp.Value is string) diff --git a/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs b/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs index 13a34db..9556856 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/KnownInstanceGuids.cs @@ -224,7 +224,10 @@ namespace Mocha.Core public static class MethodClasses { public static Guid ControlTransactionMethod { get; } = new Guid("{BF73CEAC-F972-46E8-BFA1-7807F39F1B91}"); // 8 - // PU - Process Updates Method - 11 + /// + /// PU - Process Updates Method - 11 + /// + public static Guid ProcessUpdatesMethod { get; } = new Guid("{f042c68d-8187-4dd6-93fa-7119bb949a16}"); public static Guid GetAttributeMethod { get; } = new Guid("{c3ecf8c9-597f-417b-ad65-fae0401719c6}"); // 12 // Perform System Routine Method - 13 public static Guid GetReferencedAttributeMethod { get; } = new Guid("{9205c54e-921a-484c-9be2-3d3deb877474}"); // 18 diff --git a/mocha-dotnet/src/lib/Mocha.Core/KnownRelationshipGuids.cs b/mocha-dotnet/src/lib/Mocha.Core/KnownRelationshipGuids.cs index 964aff1..e90c8a7 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/KnownRelationshipGuids.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/KnownRelationshipGuids.cs @@ -435,6 +435,9 @@ namespace Mocha.Core public static Guid Domain__secures__Route { get; } = new Guid("{95725379-7b45-4e0c-a04e-b06eead84c60}"); public static Guid Element__processed_by__Process_Related_Updates_Method { get; } = new Guid("{ed9c836a-04a4-4505-8953-3c567e841c66}"); public static Guid Process_Related_Updates_Method__uses__Executable_for_PUMB { get; } = new Guid("{50e1f14a-d6e5-4c71-b7ab-1755442728dc}"); + + public static Guid Process_Updates_Method__has__PUM_Process { get; } = new Guid("{0ffaf08e-dbb5-49d0-8de9-d253ce6cbe7c}"); + public static Guid PUM_Process__invokes__Execute_Update_Method_Binding { get; } = new Guid("{d3e83c17-fd38-46a0-a055-66281eabe9b0}"); public static Guid BEM_Process__uses_loop__Executable_returning_Instance_Set { get; } = new Guid("{0fb2b538-eacb-418a-b7d8-43a584b85952}"); diff --git a/mocha-dotnet/src/lib/Mocha.Core/Library.cs b/mocha-dotnet/src/lib/Mocha.Core/Library.cs index 891fe1a..345f3d2 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Library.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Library.cs @@ -7,9 +7,9 @@ public class Library /// Applies all attribute and relationship assignments once all instances have been /// accounted for. /// - public void Link() - { - } + public void Link() + { + } public Guid GlobalIdentifier { get; set; } = Guid.Empty; @@ -17,4 +17,5 @@ public class Library public List Attributes { get; } = new List(); public List Relationships { get; } = new List(); public List LibraryReferences { get; } = new List(); + public Dictionary EntityDefinitions { get; } = new Dictionary(); } diff --git a/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessUpdatesMethodImplementation.cs b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessUpdatesMethodImplementation.cs new file mode 100644 index 0000000..7cb1881 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/MethodImplementations/ProcessUpdatesMethodImplementation.cs @@ -0,0 +1,40 @@ +// 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 . + +namespace Mocha.Core.MethodImplementations; + +public class ProcessUpdatesMethodImplementation : MethodImplementation +{ + public override Guid MethodClassGuid => KnownInstanceGuids.MethodClasses.ProcessUpdatesMethod; + protected override InstanceHandle ExecuteInternal(Oms oms, OmsContext context, InstanceHandle method) + { + InstanceHandle forClass = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Method__for__Class)); + + InstanceHandle hasPUMProcess = oms.GetRelatedInstance(method, oms.GetInstance(KnownRelationshipGuids.Process_Updates_Method__has__PUM_Process)); + + IEnumerable invokesExecuteUpdateMethodBindings = oms.GetRelatedInstances(hasPUMProcess, oms.GetInstance(KnownRelationshipGuids.PUM_Process__invokes__Execute_Update_Method_Binding)); + // InstanceHandle invokesProcessRelatedUpdatesMethod = oms.GetRelatedInstance(hasPUMProcess, oms.GetInstance(KnownRelationshipGuids.PUM_Process__invokes__Process_Related_Updates_Method)); + Console.Error.WriteLine("---> has {0} EUMBs", invokesExecuteUpdateMethodBindings.Count()); + + foreach (InstanceHandle eumb in invokesExecuteUpdateMethodBindings) + { + Console.Error.WriteLine("---> executing EUMB {0}", eumb.GlobalIdentifier); + oms.Execute(context, eumb) ; + } + return InstanceHandle.Empty; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs index ab33b6b..820e580 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs @@ -131,6 +131,14 @@ public abstract class Oms } return (decimal)r.NextDouble(); }); + RegisterSystemUpdateRoutine(new Guid("256327f3-6f21-403d-8c58-0eabdbf10a6a"), delegate (Oms oms, OmsContext context) + { + // create instance + // but how do we get the InstanceHandle back? + // originally implemented create instance as a GSS - Get Instance Set by System Routine + // which would do the create and then return the newly-created instance in a Work Set + // via Method Binding, then use the Work Set in further method calls + }); RegisterSystemUpdateRoutine(KnownInstanceGuids.SystemUpdateRoutines.LoginUser, delegate (Oms oms, OmsContext context) { Console.Error.WriteLine("oms: executing system update routine {0}", KnownInstanceGuids.SystemUpdateRoutines.LoginUser); @@ -683,11 +691,18 @@ public abstract class Oms public bool IsDerivedInstance(IInstanceReference source) { // !!! we cannot use GetAttributeValue here !!! - InstanceHandle pclass = GetParentClass(source); - object? o = GetAttributeValueInternal(pclass, GetInstance(KnownAttributeGuids.Boolean.Derived), DateTime.Now); - if (o is bool b && b) + if (TryGetInstance(KnownAttributeGuids.Boolean.Derived, out InstanceHandle ihDerived)) { - return true; + InstanceHandle pclass = GetParentClass(source); + object? o = GetAttributeValueInternal(pclass, GetInstance(KnownAttributeGuids.Boolean.Derived), DateTime.Now); + if (o is bool b && b) + { + return true; + } + } + else + { + Console.Error.WriteLine("warning: Boolean Attribute `Derived` doesn't exist; assuming instance is not derived"); } return false; } @@ -1240,7 +1255,8 @@ public abstract class Oms } if (retval == null) { - throw new NotImplementedException(String.Format("method implementation not found for method class {0}", GetGlobalIdentifier(parentClass))); + return InstanceHandle.Empty; + //throw new NotImplementedException(String.Format("method implementation not found for method class {0}", GetGlobalIdentifier(parentClass))); } else { @@ -1514,7 +1530,7 @@ public abstract class Oms { return ee.ReturnValue; } - + object? value = sr.Execute(this, context); OnSystemRoutineExecuted(new SystemRoutineExecutedEventArgs(this, context, sr, value)); @@ -2096,7 +2112,7 @@ public abstract class Oms { targetURL = targetURLAttr; } - + if (targetURL != null) { return new RedirectResponse(targetURL); @@ -2126,6 +2142,42 @@ public abstract class Oms */ return FailureResponse.NotImplemented; } + + bool use_hack = false; + if (use_hack) + { + // if we get to here, let's just fall back and hardcode whatever + + // HACK + InstanceHandle ih = CreateInstanceOf(GetInstance(KnownInstanceGuids.Classes.Class)); + + foreach (InstanceHandle ec in elementContents) + { + string ecid = GetInstanceKey(ec).ToString(); + InstanceHandle relinst = GetRelatedInstance(ec, GetInstance(KnownRelationshipGuids.Element_Content__has__Instance)); + if (relinst != InstanceHandle.Empty) + { + if (IsInstanceOf(relinst, GetInstance(KnownInstanceGuids.Classes.Attribute))) + { + SetAttributeValue(ih, relinst, form[ecid]); + } + else if (IsInstanceOf(relinst, GetInstance(KnownInstanceGuids.Classes.Relationship))) + { + if (TryParseInstanceRef(form[ecid], out InstanceHandle ihr)) + { + AssignRelationship(ih, relinst, ihr); + } + else + { + Console.Error.WriteLine("unable to parse instance ref '{0}'", form[ecid]); + } + } + } + } + + // HACK + return new RedirectResponse(String.Format("~/d/inst/{0}.htmld", GetInstanceKey(ih))); + } } else { @@ -2136,7 +2188,7 @@ public abstract class Oms { return FailureResponse.UnexpectedFailure; } - + return new PageResponse(delegate (Oms oms, OmsContext ctx) { JsonObject obj3 = widget.ToJSONObject(ctx); @@ -2144,7 +2196,7 @@ public abstract class Oms JsonObject obj2 = new JsonObject(); obj2.Add("widget", JsonValue.Create("root")); obj2.Add("body", widget.ToJSONObject(ctx)); - + JsonObject objTitle = new JsonObject(); // hack if (String.IsNullOrEmpty(title) && ctx.InitiatingTask != InstanceHandle.Empty) @@ -2161,7 +2213,7 @@ public abstract class Oms return obj; }); } - return FailureResponse.UnexpectedFailure; + return null; // FailureResponse.UnexpectedFailure; } private Response? ProcessElementContent(OmsContext context, InstanceHandle elementContent, IDictionary form, string fqecidPrefix) @@ -2352,4 +2404,50 @@ public abstract class Oms } throw new FormatException(); } + + public object? Evaluate(OmsContext context, InstanceHandle workDataInstance, InstanceHandle sourceInstance, object? defaultValue = null) + { + object? value = defaultValue; + if (IsInstanceOf(workDataInstance, GetInstance(KnownInstanceGuids.Classes.TextAttribute))) + { + if (sourceInstance != InstanceHandle.Empty) + { + // first check our target instance for the attribute. this MAY be empty + value = GetAttributeValue(sourceInstance, workDataInstance); + } + } + else if (IsInstanceOf(workDataInstance, GetInstance(KnownInstanceGuids.Classes.BooleanAttribute))) + { + if (sourceInstance != InstanceHandle.Empty) + { + // first check our target instance for the attribute. this MAY be empty + value = GetAttributeValue(sourceInstance, workDataInstance); + } + } + else if (IsInstanceOf(workDataInstance, GetInstance(KnownInstanceGuids.Classes.NumericAttribute))) + { + if (sourceInstance != InstanceHandle.Empty) + { + // first check our target instance for the attribute. this MAY be empty + value = GetAttributeValue(sourceInstance, workDataInstance); + } + } + else if (IsInstanceOf(workDataInstance, GetInstance(KnownInstanceGuids.Classes.DateAttribute))) + { + if (sourceInstance != InstanceHandle.Empty) + { + // first check our target instance for the attribute. this MAY be empty + value = GetAttributeValue(sourceInstance, workDataInstance); + } + } + else + { + context.SetWorkData(GetInstance(KnownInstanceGuids.WorkSets.TaskRelatedInstance), context.InitiatingInstance); + + InstanceHandle workData = Execute(context, workDataInstance); + object? val = context.GetWorkData(workData); + value = val?.ToString(); + } + return value; + } } diff --git a/mocha-dotnet/src/lib/Mocha.Core/Transaction.cs b/mocha-dotnet/src/lib/Mocha.Core/Transaction.cs index 6c9bdea..0616e55 100755 --- a/mocha-dotnet/src/lib/Mocha.Core/Transaction.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Transaction.cs @@ -15,26 +15,6 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -// -// Transaction.cs -// -// Author: -// Michael Becker -// -// Copyright (c) 2021 Mike Becker's Software -// -// This program 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. -// -// This program 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 this program. If not, see . using System; using System.Collections; using System.Collections.Generic; diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/CheckBox.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/CheckBox.cs new file mode 100644 index 0000000..b61d29f --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/CheckBox.cs @@ -0,0 +1,54 @@ +// 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.Json.Nodes; + +namespace Mocha.Core.UI; + +public class CheckBox : Widget +{ + protected override string GetWidgetName() + { + return "checkbox"; + } + public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.BooleanAttribute }; + + protected override void RenderJSONInternal(OmsContext context, JsonObject obj) + { + string? value = null; + + object? val = OMS.Evaluate(context, ParentInstance, TargetInstance); + if (val != null) + { + value = val.ToString(); + } + + if (value == null) + { + // no value specified for the target instance, so get default value of the Text Attribute + value = OMS.GetAttributeValue(ParentInstance, OMS.GetInstance(KnownAttributeGuids.Text.Value)); + } + + + if (value != null) + { + // if we finally have a value, use it + obj.Add("value", value); + } + } +} diff --git a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Text.cs b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Text.cs index 488e4e2..4954600 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Text.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/UI/Widgets/Text.cs @@ -34,29 +34,18 @@ public class Text : Widget } return "text"; } - public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.TextAttribute, KnownInstanceGuids.Classes.ExecutableReturningAttribute }; + public override Guid[] SupportedClassGuids => new Guid[] { KnownInstanceGuids.Classes.TextAttribute }; protected override void RenderJSONInternal(OmsContext context, JsonObject obj) { string? value = null; - if (OMS.IsInstanceOf(ParentInstance, OMS.GetInstance(KnownInstanceGuids.Classes.TextAttribute))) + object? val = OMS.Evaluate(context, ParentInstance, TargetInstance); + if (val != null) { - if (TargetInstance != InstanceHandle.Empty) - { - // first check our target instance for the attribute. this MAY be empty - value = OMS.GetAttributeValue(TargetInstance, ParentInstance); - } + value = val.ToString(); } - else - { - context.SetWorkData(OMS.GetInstance(KnownInstanceGuids.WorkSets.TaskRelatedInstance), context.InitiatingInstance); - - InstanceHandle workData = OMS.Execute(context, ParentInstance); - object? val = context.GetWorkData(workData); - value = val?.ToString(); - } - + if (value == null) { // no value specified for the target instance, so get default value of the Text Attribute diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/AccessModifier.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/AccessModifier.cs new file mode 100644 index 0000000..2897d5f --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/AccessModifier.cs @@ -0,0 +1,28 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public enum AccessModifier +{ + None, + Public, + Protected, + Internal, + ProtectedInternal, + Private +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Class.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Class.cs new file mode 100644 index 0000000..f8098d6 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Class.cs @@ -0,0 +1,28 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +using System.Collections.Generic; + +public class Class : ICodeType, IClassMember +{ + public AccessModifier AccessModifier { get; set; } = AccessModifier.None; + public string? Name { get; set; } = ""; + public ObjectReference? InheritsClass { get; set; } = null; + public List Items { get; } = new List(); +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/CodeGenerator.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/CodeGenerator.cs new file mode 100644 index 0000000..916e1f1 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/CodeGenerator.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +using System.Text; + +public abstract class CodeGenerator +{ + protected abstract string GenerateCodeInternal(ICodeItem item); + public string GenerateCode(ICodeItem item) + { + return GenerateCodeInternal(item); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Document.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Document.cs new file mode 100644 index 0000000..393519c --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Document.cs @@ -0,0 +1,25 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +using System.Collections.Generic; + +public class Document +{ + public List Classes { get; } = new List(); +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/CreateInstance.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/CreateInstance.cs new file mode 100644 index 0000000..5afebd7 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/CreateInstance.cs @@ -0,0 +1,30 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public class CreateInstance : IMethodMember +{ + public ObjectReference ObjectReference { get; set; } + public List ParameterValues { get; } = new List(); + + public CreateInstance(IEnumerable objectNames, IEnumerable parameterValues) + { + ObjectReference = new ObjectReference(objectNames); + ParameterValues.AddRange(parameterValues); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/MethodCall.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/MethodCall.cs new file mode 100644 index 0000000..e2d1afc --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/MethodCall.cs @@ -0,0 +1,41 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration.Expressions; + +public class MethodCall : IMethodMember +{ + public ObjectReference ObjectName { get; set; } + public string MethodName { get; set; } + public List Parameters { get; } = new List(); + public IEnumerable? GenericParameters { get; set; } = null; + + public MethodCall(IEnumerable objectNames, string methodName, IEnumerable? parmValues = null) + { + ObjectName = new ObjectReference(objectNames); + MethodName = methodName; + if (parmValues != null) + { + foreach (object parmValue in parmValues) + { + MethodParameter parm = new MethodParameter(); + parm.Value = parmValue; + Parameters.Add(parm); + } + } + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/MethodParameter.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/MethodParameter.cs new file mode 100644 index 0000000..f8a88ba --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/MethodParameter.cs @@ -0,0 +1,31 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public class MethodParameter +{ + public MethodParameter(string? name = null, ObjectReference? dataType = null) + { + Name = name; + DataType = dataType; + } + + public ObjectReference? DataType { get; set; } = null; + public string? Name { get; set; } = null; + public object? Value { get; set; } = null; +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/ObjectReference.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/ObjectReference.cs new file mode 100644 index 0000000..8ec526d --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/ObjectReference.cs @@ -0,0 +1,28 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public class ObjectReference +{ + public IEnumerable ObjectNames { get; set; } = null; + + public ObjectReference(IEnumerable objectNames) + { + ObjectNames = objectNames; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/PropertyAssignment.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/PropertyAssignment.cs new file mode 100644 index 0000000..974c65e --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/PropertyAssignment.cs @@ -0,0 +1,30 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public class PropertyAssignment : IMethodMember +{ + public ObjectReference ObjectReference { get; } + public object? Value { get; set; } = null; + + public PropertyAssignment(ObjectReference objectReference, object value) + { + ObjectReference = objectReference; + Value = value; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/Return.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/Return.cs new file mode 100644 index 0000000..21d1a2f --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Expressions/Return.cs @@ -0,0 +1,27 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration.Expressions; + +public class Return : IMethodMember +{ + public object? Value { get; set; } + public Return(object? value) + { + Value = value; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Generators/CSharpCodeGenerator.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Generators/CSharpCodeGenerator.cs new file mode 100644 index 0000000..97f90eb --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Generators/CSharpCodeGenerator.cs @@ -0,0 +1,261 @@ +// Copyright (C) 2025 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.Linq.Expressions; +using System.Reflection; +using System.Text; +using MBS.Core.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Mocha.Core; +using Mocha.Core.Oop; +using Mocha.Modeling.CodeGeneration.Expressions; + +namespace Mocha.Modeling.CodeGeneration.Generators; + +public class CSharpCodeGenerator : DotNetCodeGenerator +{ + public string GenerateCode(AccessModifier accessModifier) + { + switch (accessModifier) + { + case AccessModifier.Internal: return "internal"; + case AccessModifier.Protected: return "protected"; + case AccessModifier.ProtectedInternal: return "protected internal"; + case AccessModifier.Public: return "public"; + case AccessModifier.Private: return "private"; + } + return ""; + } + protected override string GenerateCodeInternal(ICodeItem item) + { + StringBuilder sb = new StringBuilder(); + if (item is Namespace ns) + { + sb.Append("namespace "); + sb.Append(BuildObjectReference(ns.Names)); + sb.Append(" { "); + foreach (ICodeItem subItem in ns.Items) + { + sb.Append(GenerateCode(subItem)); + } + sb.Append(" } "); + } + else if (item is Class c) + { + sb.Append(GenerateCode(c.AccessModifier)); + sb.Append("class "); + sb.Append(c.Name); + if (c.InheritsClass != null) + { + sb.Append(" : "); + sb.Append(BuildObjectReference(c.InheritsClass.ObjectNames)); + } + sb.Append(" { "); + foreach (IClassMember subItem in c.Items) + { + sb.AppendLine(GenerateCode(subItem)); + } + sb.Append(" }"); + } + else if (item is Property p) + { + sb.Append(GenerateCode(p.AccessModifier)); + sb.Append(' '); + sb.Append(GenerateCode(p.ReturnType)); + sb.Append(' '); + sb.Append(p.Name); + sb.Append(" { "); + if (p.Value != null || p.UseAutoProperty) + { + sb.Append("get;"); + if (!p.ReadOnly) + { + sb.Append(" set;"); + } + } + else + { + if (p.GetMethod != null) + { + sb.Append("get { "); + foreach (IMethodMember m in p.GetMethod.Items) + { + sb.Append(GenerateCode(m)); + sb.Append("; "); + } + sb.Append(" } "); + } + if (p.SetMethod != null) + { + sb.Append("set { "); + foreach (IMethodMember m in p.SetMethod.Items) + { + sb.Append(GenerateCode(m)); + sb.Append("; "); + } + sb.Append(" } "); + } + } + sb.Append(" } "); + if (p.Value != null || (p.UseAutoProperty && p.ReadOnly)) + { + sb.Append("= "); + sb.Append(ExpressionOrLiteral(p.Value)); + sb.Append(";"); + } + } + else if (item is Method m) + { + + } + else if (item is CreateInstance ci) + { + sb.Append("new "); + sb.Append(BuildObjectReference(ci.ObjectReference.ObjectNames)); + sb.Append("("); + sb.Append(BuildParameterList(ci.ParameterValues)); + sb.Append(")"); + } + else if (item is Return r) + { + sb.Append("return "); + sb.Append(ExpressionOrLiteral(r.Value)); + } + else if (item is MethodCall mc) + { + sb.Append(BuildObjectReference(mc.ObjectName.ObjectNames)); + sb.Append('.'); + sb.Append(mc.MethodName); + if (mc.GenericParameters != null) + { + sb.Append('<'); + sb.Append(BuildParameterList(mc.GenericParameters)); + sb.Append('>'); + } + sb.Append('('); + sb.Append(BuildParameterList(mc.Parameters)); + sb.Append(')'); + } + return sb.ToString(); + } + + public Assembly CompileScript(string script, IEnumerable? additionalDLLs = null) + { + SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(script); + return CompileScript(syntaxTree, additionalDLLs); + } + + protected override Compilation CreateCompilation(string assemblyName, SyntaxTree syntaxTree, MetadataReference[] references, OutputKind outputKind) + { + CSharpCompilation compilation = CSharpCompilation.Create( + assemblyName, + syntaxTrees: new[] { syntaxTree }, + references: references, + options: new CSharpCompilationOptions(outputKind)); + return compilation; + } + + protected override string ExpressionOrLiteral(object? value) + { + if (value == null) + { + return "null"; + } + else if (value is string) + { + return "\"" + (string)value + "\""; + } + else if (value is ICodeItem) + { + return GenerateCode((ICodeItem)value); + } + else if (value is MethodCall mc) + { + return ExpressionOrLiteral(mc.ObjectName) + "." + mc.MethodName + "(" + BuildParameterList(mc.Parameters) + ")"; + } + else if (value is ObjectReference orr) + { + return BuildObjectReference(orr.ObjectNames); + } + return value.ToString() ?? ""; + } + + protected override string NamedParameterSeparator => ": "; + + protected ObjectReference ParseObjectReference(string value) + { + string[] parts = value.Split(new char[] { '.' }); + return new ObjectReference(parts); + } + + protected Property GenerateProperty(PropertyInfo pi) + { + Property pp = new Property(pi.Name, ParseObjectReference(SafeTypeName(pi.PropertyType))); + pp.AccessModifier = AccessModifier.Public; + if (pi.PropertyType.IsSubclassOfGeneric(typeof(IList<>))) + { + pp.GetMethod = new Method(new IMethodMember[] + { + new Return(null) + }); + + if (pi.GetSetMethod() != null) + { + pp.SetMethod = new Method(new IMethodMember[] + { + + }); + } + } + else if (pi.PropertyType == typeof(string)) + { + pp.GetMethod = new Method(new IMethodMember[] + { + new Return(new MethodCall(new string[] { "this", "Oms" }, "GetAttributeValue", new object[] + { + new MethodCall(new string[] { "this", "Oms" }, "GetInstance", new object[] { new ObjectReference(new string[] { "this", "GlobalIdentifier" })}), + new MethodCall(new string[] { "this", "Oms" }, "GetInstance", new object[] { new CreateInstance(new string[] { "System", "Guid" }, new object[] { "{9153A637-992E-4712-ADF2-B03F0D9EDEA6}" })}) + }) { GenericParameters = [ new ObjectReference(["string"]) ]}) + }); + // GetAttributeValue(InstanceKey source, InstanceHandle attribute, T defaultValue = default(T), DateTime? effectiveDate = null) + + if (pi.GetSetMethod() != null) + { + pp.SetMethod = new Method(); + } + } + else + { + // generate dynamic getters and setters for GetRelatedInstance and AssignRelationship + pp.GetMethod = new Method(new IMethodMember[] + { + new Return(null) + }); + + if (pi.GetSetMethod() != null) + { + pp.SetMethod = new Method(new IMethodMember[] + { + + }); + } + } + return pp; + } + +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Generators/DotNetCodeGenerator.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Generators/DotNetCodeGenerator.cs new file mode 100644 index 0000000..0281b3f --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Generators/DotNetCodeGenerator.cs @@ -0,0 +1,160 @@ +// Copyright (C) 2025 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.Reflection; +using System.Text; +using MBS.Core.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Emit; +using Mocha.Core; + +namespace Mocha.Modeling.CodeGeneration.Generators; + +public abstract class DotNetCodeGenerator : CodeGenerator +{ + public string GenerateCode(ObjectReference objectReference) + { + return BuildObjectReference(objectReference.ObjectNames); + } + protected string? SafeTypeName(Type type) + { + if (type.IsSubclassOfGeneric(typeof(IList<>))) + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + sb.Append("System.Collections.Generic.IList<"); + Type[] genericArguments = type.GetGenericArguments(); + for (int i = 0; i < genericArguments.Length; i++) + { + sb.Append(SafeTypeName(genericArguments[i])); + if (i < genericArguments.Length - 1) + { + sb.Append(", "); + } + } + sb.Append(">"); + return sb.ToString(); + } + return type.FullName?.Replace("+", "."); + } + + protected abstract Compilation CreateCompilation(string assemblyName, SyntaxTree syntaxTree, MetadataReference[] references, OutputKind outputKind); + + protected virtual string NamedParameterSeparator { get; } = ": "; + + + protected virtual string BuildParameterList(IList parameters) + { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < parameters.Count; i++) + { + if (parameters[i].Name != null) + { + sb.Append(parameters[i].Name + NamedParameterSeparator); + } + sb.Append(ExpressionOrLiteral(parameters[i].Value)); + if (i < parameters.Count - 1) + { + sb.Append(", "); + } + } + return sb.ToString(); + } + + protected virtual string BuildParameterList(IEnumerable parameters) + { + StringBuilder sb = new StringBuilder(); + int i = 0; + foreach (object parm in parameters) + { + sb.Append(ExpressionOrLiteral(parm)); + if (i < parameters.Count() - 1) + { + sb.Append(", "); + } + i++; + } + return sb.ToString(); + } + + protected abstract string ExpressionOrLiteral(object? value); + + protected virtual string BuildObjectReference(IEnumerable objectNames) + { + StringBuilder sb = new StringBuilder(); + int i = 0; + foreach (string objectName in objectNames) + { + sb.Append(objectName); + if (i < objectNames.Count() - 1) + { + sb.Append('.'); + } + i++; + } + return sb.ToString(); + } + + protected Assembly CompileScript(SyntaxTree syntaxTree, IEnumerable? additionalDLLs = null) + { + // thanks https://stackoverflow.com/questions/66672329 + // use "mytest.dll" if you want, random works well enough + string assemblyName = System.IO.Path.GetRandomFileName(); + List dlls = new List { + typeof(object).Assembly.Location, + typeof(object).Assembly.Location.Replace("System.Private.CoreLib.", "System.Runtime."), + typeof(Oms).Assembly.Location, + typeof(IOmsDatabase).Assembly.Location + }; + + if (additionalDLLs != null) + { + dlls.AddRange(additionalDLLs); + } + + + MetadataReference[] references = dlls.Distinct().Select(x => MetadataReference.CreateFromFile(x)).ToArray(); + + Compilation compilation = CreateCompilation(assemblyName, syntaxTree, references, OutputKind.DynamicallyLinkedLibrary); + + // Now we actually compile the script, this includes some very crude error handling, just to show you can + using (var ms = new MemoryStream()) + { + EmitResult result = compilation.Emit(ms); + + if (!result.Success) + { + IEnumerable failures = result.Diagnostics.Where(diagnostic => + diagnostic.IsWarningAsError || + diagnostic.Severity == DiagnosticSeverity.Error); + + List errors = new List(); + foreach (Diagnostic diagnostic in failures) + { + //errors.AddDistinct(String.Format("{0} : {1}", diagnostic.Id, diagnostic.Location, diagnostic.GetMessage())); + errors.Add(diagnostic.ToString()); + } + + throw new ApplicationException("Compilation Errors: " + String.Join(Environment.NewLine, errors)); + } + else + { + ms.Seek(0, SeekOrigin.Begin); + return Assembly.Load(ms.ToArray()); + } + } + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/IClassMember.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/IClassMember.cs new file mode 100644 index 0000000..aa50a10 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/IClassMember.cs @@ -0,0 +1,23 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public interface IClassMember : ICodeItem +{ + string? Name { get; set; } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeExpression.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeExpression.cs new file mode 100644 index 0000000..e374a22 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeExpression.cs @@ -0,0 +1,22 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public interface ICodeExpression +{ +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeItem.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeItem.cs new file mode 100644 index 0000000..d38fdaa --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeItem.cs @@ -0,0 +1,22 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public interface ICodeItem +{ +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeType.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeType.cs new file mode 100644 index 0000000..e4c9b34 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/ICodeType.cs @@ -0,0 +1,24 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public interface ICodeType : ICodeItem +{ + public AccessModifier AccessModifier { get; set; } + public string? Name { get; set; } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/IMethodMember.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/IMethodMember.cs new file mode 100644 index 0000000..20d5060 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/IMethodMember.cs @@ -0,0 +1,22 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public interface IMethodMember : ICodeItem +{ +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Method.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Method.cs new file mode 100644 index 0000000..b92653f --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Method.cs @@ -0,0 +1,43 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public class Method : IClassMember +{ + public string? Name { get; set; } = null; + public List Items { get; } = new List(); + public List Parameters { get; } = new List(); + + public Method(IEnumerable? items = null) : this(null, items) + { + + } + public Method(string name, IEnumerable? items = null) + { + Name = name; + if (items != null) + { + Items.AddRange(items); + } + } + public Method(string name, IEnumerable parms, IEnumerable? items = null) + : this(name, items) + { + Parameters.AddRange(parms); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Namespace.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Namespace.cs new file mode 100644 index 0000000..4c02e32 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Namespace.cs @@ -0,0 +1,29 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public class Namespace : ICodeItem +{ + public string[] Names { get; set; } + public List Items { get; } = new List(); + + public Namespace(string[] names) + { + Names = names; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Property.cs b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Property.cs new file mode 100644 index 0000000..f30b9b4 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/CodeGeneration/Property.cs @@ -0,0 +1,37 @@ +// Copyright (C) 2025 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 . + +namespace Mocha.Modeling.CodeGeneration; + +public class Property : IClassMember +{ + public Property(string name, ObjectReference returnType, object? value = null) + { + Name = name; + ReturnType = returnType; + Value = value; + } + + public AccessModifier AccessModifier { get; set; } = AccessModifier.None; + public string Name { get; set; } = ""; + public ObjectReference ReturnType { get; set; } + public Method? GetMethod { get; set; } + public Method? SetMethod { get; set; } + public object? Value { get; set; } + public bool UseAutoProperty { get; set; } = false; + public bool ReadOnly { get; set; } = false; +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/ImplicitAttributes/TextAttribute.cs b/mocha-dotnet/src/lib/Mocha.Modeling/ImplicitAttributes/TextAttribute.cs similarity index 89% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/ImplicitAttributes/TextAttribute.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/ImplicitAttributes/TextAttribute.cs index 6c0c71c..328360d 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/ImplicitAttributes/TextAttribute.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/ImplicitAttributes/TextAttribute.cs @@ -1,4 +1,6 @@ -namespace Mocha.Core.Modeling.ImplicitAttributes; +namespace Mocha.Modeling.ImplicitAttributes; + +using Mocha.Core; public class TextAttribute { diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/Mocha.Modeling.csproj b/mocha-dotnet/src/lib/Mocha.Modeling/Mocha.Modeling.csproj new file mode 100644 index 0000000..77be9e0 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/Mocha.Modeling.csproj @@ -0,0 +1,19 @@ + + + + net8.0 + enable + enable + + + + + + + + + + + + + diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/ObjectFactory.cs b/mocha-dotnet/src/lib/Mocha.Modeling/ObjectFactory.cs new file mode 100644 index 0000000..b9f5640 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/ObjectFactory.cs @@ -0,0 +1,24 @@ +// Copyright (C) 2025 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 . + + +namespace Mocha.Modeling; + +public abstract class ObjectFactory +{ + +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsClass.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsClass.cs similarity index 92% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsClass.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsClass.cs index 8f8a658..4e41f0a 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsClass.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsClass.cs @@ -20,11 +20,20 @@ using System.Diagnostics; using System.Reflection; using MBS.Core.Reflection; -namespace Mocha.Core.Modeling; +using Mocha.Core; + +namespace Mocha.Modeling; + +public interface IOmsItem { } + +public interface IOmsClass : IOmsItem +{ + Guid GlobalIdentifier { get; } +} public class OmsClass { - internal OmsDatabase omsdb { get; private set; } + internal IOmsDatabase omsdb { get; private set; } private Guid _GlobalIdentifier = Guid.Empty; public Guid GlobalIdentifier { @@ -34,7 +43,7 @@ public class OmsClass } } private Dictionary _attributeValuesTemp = new Dictionary(); - + protected object GetAttributeValue(object defaultValue = null) { StackTrace st = new StackTrace(); @@ -107,8 +116,8 @@ public class OmsClass throw new InvalidOperationException(); } - internal void Initialize(OmsDatabase omsdb, Guid localInstanceGuid) - { + internal void Initialize(IOmsDatabase omsdb, Guid localInstanceGuid) + { this.omsdb = omsdb; this._GlobalIdentifier = localInstanceGuid; @@ -120,5 +129,5 @@ public class OmsClass omsdb.Oms.SetAttributeValue(inst, omsdb.Oms.GetInstance(kvp.Key), kvp.Value); } } - } + } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/OmsDatabase.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsDatabase.cs new file mode 100644 index 0000000..5fa13e0 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsDatabase.cs @@ -0,0 +1,89 @@ +// 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.Globalization; +using System.Reflection; +using MBS.Core.Reflection; +using Microsoft.VisualBasic; +using Mocha.Core; + +namespace Mocha.Modeling; + +public interface IOmsDatabase : IOmsItem +{ + Oms Oms { get; } + void Initialize(Oms oms); +} +public class OmsDatabase : IOmsDatabase +{ + public Oms Oms { get; private set; } + + private Type _Type = null; + public void Initialize(Oms oms) + { + OmsDatabaseExtensions.Initialize(this, oms); + } + + public static string GetNameForClass(Type t) + { + if (!t.IsAssignableTo(typeof(IOmsClass))) + { + throw new InvalidOperationException("Must implement IOmsClass"); + } + + object[] attrs = t.GetCustomAttributes(false); + foreach (object attr in attrs) + { + if (attr is OmsNameAttribute) + { + return ((OmsNameAttribute)attr).Name; + } + } + return ""; + } + + public static Guid GetGlobalIdentifierForClass(Type t) + { + if (!t.IsAssignableTo(typeof(IOmsClass))) + { + throw new InvalidOperationException("Must be subclass of OmsClass"); + } + + object[] attrs = t.GetCustomAttributes(false); + foreach (object attr in attrs) + { + if (attr is OmsGlobalIdentifierAttribute) + { + return ((OmsGlobalIdentifierAttribute)attr).GlobalIdentifier; + } + } + return Guid.Empty; + } + + public static Guid GetGlobalIdentifierForProperty(PropertyInfo pi) + { + object[] attrs = pi.GetCustomAttributes(false); + foreach (object attr in attrs) + { + if (attr is OmsGlobalIdentifierAttribute) + { + return ((OmsGlobalIdentifierAttribute)attr).GlobalIdentifier; + } + } + return Guid.Empty; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsDatabase.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsDatabaseExtensions.cs similarity index 50% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsDatabase.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsDatabaseExtensions.cs index 4a4f9bc..a0cc302 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsDatabase.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsDatabaseExtensions.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Michael Becker +// Copyright (C) 2025 Michael Becker // // This file is part of Mocha.NET. // @@ -15,22 +15,18 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -using System.Globalization; +namespace Mocha.Modeling; + +using Mocha.Core; using System.Reflection; using MBS.Core.Reflection; -using Mocha.Core.Modeling.PropertyImplementations; -namespace Mocha.Core.Modeling; - -public class OmsDatabase +public static class OmsDatabaseExtensions { - public Oms Oms { get; private set; } - - private Type _Type = null; - public void Initialize(Oms oms) + public static void Initialize(this IOmsDatabase db, Oms oms) { - Oms = oms; - _Type = this.GetType(); + // db.Oms = oms; + Type _Type = db.GetType(); // First create all the classes we've defined in the database. // These are nested types of the class derived from OmsDatabase. @@ -53,7 +49,7 @@ public class OmsDatabase if (!t.IsSubclassOf(typeof(OmsClass))) continue; - CreateClass(t); + db.CreateClass(t); } foreach (Type t in nestedTypes) @@ -64,7 +60,7 @@ public class OmsDatabase if (!t.IsSubclassOf(typeof(OmsClass))) continue; - SetupClassProperties(t); + db.SetupClassProperties(t); } // Now that our schema is set up, go through OmsDatabase public properties @@ -82,7 +78,7 @@ public class OmsDatabase throw new InvalidOperationException("Generic type argument of collection type must inherit from OmsClass"); } - fi.SetValue(this, OmsInstanceList.CreateGeneric(this, genericTypeArg)); + fi.SetValue(db, OmsInstanceList.CreateGeneric(db, genericTypeArg)); } else if (fi.FieldType.IsSubclassOf(typeof(OmsClass))) { @@ -90,12 +86,26 @@ public class OmsDatabase } else { - + } } } - private void CreateClass(Type t) + public static T CreateInstance(this IOmsDatabase db) where T : IOmsClass + { + Type t = typeof(T); + Guid globalIdentifier = OmsDatabase.GetGlobalIdentifierForClass(t); + InstanceHandle ir_class = db.Oms.GetInstance(globalIdentifier); + InstanceHandle ih = db.Oms.CreateInstanceOf(ir_class); + Guid id = db.Oms.GetGlobalIdentifier(ih); + + OmsObjectFactory factory = new OmsObjectFactory(); + T obj = factory.CreateInstance(db.Oms, id); + + return obj; + } + + private static void CreateClass(this IOmsDatabase db, Type t) { string className = t.Name; Guid globalIdentifier = Guid.Empty; @@ -117,39 +127,21 @@ public class OmsDatabase { globalIdentifier = Guid.NewGuid(); } - + Console.WriteLine("OmsDatabase: got type {0} (class `{1}` {2})", t.Name, className, globalIdentifier.ToString("B")); - Oms.CreateClass(className, globalIdentifier); + db.Oms.CreateClass(className, globalIdentifier); } - private void SetupClassProperties(Type t) + private static void SetupClassProperties(this IOmsDatabase db, Type t) { PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance); foreach (PropertyInfo property in properties) { - SetupClassProperty(t, property); + db.SetupClassProperty(t, property); } } - public static Guid GetGlobalIdentifierForClass(Type t) - { - if (!t.IsSubclassOf(typeof(OmsClass))) - { - throw new InvalidOperationException("Must be subclass of OmsClass"); - } - - object[] attrs = t.GetCustomAttributes(false); - foreach (object attr in attrs) - { - if (attr is OmsGlobalIdentifierAttribute) - { - return ((OmsGlobalIdentifierAttribute)attr).GlobalIdentifier; - } - } - return Guid.Empty; - } - - private void SetupClassProperty(Type t, PropertyInfo pi) + private static void SetupClassProperty(this IOmsDatabase db, Type t, PropertyInfo pi) { if (pi.PropertyType == typeof(Guid) && pi.Name == "GlobalIdentifier") { @@ -171,24 +163,24 @@ public class OmsDatabase globalIdentifier = Guid.NewGuid(); } - Guid myGuid = GetGlobalIdentifierForClass(t); - InstanceHandle sourceClassInstance = Oms.GetInstance(myGuid); + Guid myGuid = OmsDatabase.GetGlobalIdentifierForClass(t); + InstanceHandle sourceClassInstance = db.Oms.GetInstance(myGuid); InstanceHandle propertyInstance = InstanceHandle.Empty; if (pi.PropertyType.IsSubclassOfGeneric(typeof(ICollection<>))) { Type arg = pi.PropertyType.GenericTypeArguments[0]; - Guid guid = GetGlobalIdentifierForClass(arg); - InstanceHandle relationshipDestinationClassInstance = Oms.GetInstance(guid); + Guid guid = OmsDatabase.GetGlobalIdentifierForClass(arg); + InstanceHandle relationshipDestinationClassInstance = db.Oms.GetInstance(guid); - CreateRelationship(out propertyInstance, globalIdentifier, sourceClassInstance, relationshipDestinationClassInstance, false); + db.CreateRelationship(out propertyInstance, globalIdentifier, sourceClassInstance, relationshipDestinationClassInstance, false); } else if (pi.PropertyType.IsSubclassOf(typeof(OmsClass))) { - Guid guid = GetGlobalIdentifierForClass(pi.PropertyType); - InstanceHandle relationshipDestinationClassInstance = Oms.GetInstance(guid); + Guid guid = OmsDatabase.GetGlobalIdentifierForClass(pi.PropertyType); + InstanceHandle relationshipDestinationClassInstance = db.Oms.GetInstance(guid); - CreateRelationship(out propertyInstance, globalIdentifier, sourceClassInstance, relationshipDestinationClassInstance, true); + db.CreateRelationship(out propertyInstance, globalIdentifier, sourceClassInstance, relationshipDestinationClassInstance, true); } else { @@ -196,23 +188,23 @@ public class OmsDatabase if (pi.PropertyType == typeof(string)) { // Text Attribute - CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute), pi.Name); + db.CreateAttribute(out propertyInstance, db.Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute), pi.Name); } else if (pi.PropertyType == typeof(bool)) { // Boolean Attribute - CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute), pi.Name); + db.CreateAttribute(out propertyInstance, db.Oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute), pi.Name); } else if (pi.PropertyType == typeof(decimal) || pi.PropertyType == typeof(int)) { // Numeric Attribute - CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.NumericAttribute), pi.Name); + db.CreateAttribute(out propertyInstance, db.Oms.GetInstance(KnownInstanceGuids.Classes.NumericAttribute), pi.Name); } else if (pi.PropertyType == typeof(DateTime)) { // Date Attribute - CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.DateAttribute), pi.Name); + db.CreateAttribute(out propertyInstance, db.Oms.GetInstance(KnownInstanceGuids.Classes.DateAttribute), pi.Name); } else { @@ -220,51 +212,53 @@ public class OmsDatabase } } - ApplyNativeAttributes(propertyInstance, propertyAttrs); + db.ApplyNativeAttributes(propertyInstance, propertyAttrs); } - private void CreateAttribute(out InstanceHandle propertyInstance, InstanceHandle attributeClassInstance, string name) + private static void CreateAttribute(this IOmsDatabase db, out InstanceHandle propertyInstance, InstanceHandle attributeClassInstance, string name) { - propertyInstance = Oms.CreateInstanceOf(attributeClassInstance); - Oms.SetAttributeValue(propertyInstance, Oms.GetInstance(KnownAttributeGuids.Text.Name), name); + propertyInstance = db.Oms.CreateInstanceOf(attributeClassInstance); + db.Oms.SetAttributeValue(propertyInstance, db.Oms.GetInstance(KnownAttributeGuids.Text.Name), name); } - private void CreateRelationship(out InstanceHandle relationshipInstance, Guid globalIdentifier, InstanceHandle sourceClassInstance, InstanceHandle relationshipDestinationClassInstance, bool relationshipSingular) + private static void CreateRelationship(this IOmsDatabase db, out InstanceHandle relationshipInstance, Guid globalIdentifier, InstanceHandle sourceClassInstance, InstanceHandle relationshipDestinationClassInstance, bool relationshipSingular) { - relationshipInstance = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.Relationship), globalIdentifier); - Oms.AssignRelationship(relationshipInstance, Oms.GetInstance(KnownRelationshipGuids.Relationship__has_source__Class), sourceClassInstance); - Oms.AssignRelationship(relationshipInstance, Oms.GetInstance(KnownRelationshipGuids.Relationship__has_destination__Class), relationshipDestinationClassInstance); - Oms.SetAttributeValue(relationshipInstance, Oms.GetInstance(KnownAttributeGuids.Boolean.Singular), relationshipSingular); + relationshipInstance = db.Oms.CreateInstanceOf(db.Oms.GetInstance(KnownInstanceGuids.Classes.Relationship), globalIdentifier); + db.Oms.AssignRelationship(relationshipInstance, db.Oms.GetInstance(KnownRelationshipGuids.Relationship__has_source__Class), sourceClassInstance); + db.Oms.AssignRelationship(relationshipInstance, db.Oms.GetInstance(KnownRelationshipGuids.Relationship__has_destination__Class), relationshipDestinationClassInstance); + db.Oms.SetAttributeValue(relationshipInstance, db.Oms.GetInstance(KnownAttributeGuids.Boolean.Singular), relationshipSingular); } - private void ApplyNativeAttributes(InstanceHandle inst, object[] propertyAttrs) + private static void ApplyNativeAttributes(this IOmsDatabase db, InstanceHandle inst, object[] propertyAttrs) { foreach (object attr in propertyAttrs) { if (attr is OmsNativeAttribute nativeAttr) { - InstanceHandle attributeInstance = Oms.GetInstance(nativeAttr.AttributeGlobalIdentifier); - Oms.SetAttributeValue(inst, attributeInstance, nativeAttr.Value); + InstanceHandle attributeInstance = db.Oms.GetInstance(nativeAttr.AttributeGlobalIdentifier); + db.Oms.SetAttributeValue(inst, attributeInstance, nativeAttr.Value); } else if (attr is OmsNativeRelationship nativeRel) { - InstanceHandle relationshipInstance = Oms.GetInstance(nativeRel.RelationshipGlobalIdentifier); + InstanceHandle relationshipInstance = db.Oms.GetInstance(nativeRel.RelationshipGlobalIdentifier); List list = new List(); foreach (Guid guid in nativeRel.TargetInstances) { - InstanceHandle relinst = Oms.GetInstance(guid); + InstanceHandle relinst = db.Oms.GetInstance(guid); list.Add(relinst); } - Oms.AssignRelationship(inst, relationshipInstance, list.ToArray()); + db.Oms.AssignRelationship(inst, relationshipInstance, list.ToArray()); } } } - private Dictionary _classesByHandle = new Dictionary(); - private Dictionary _handlesByClass = new Dictionary(); - public OmsClass GetClass(InstanceHandle handle) + // FIXME ??? + private static Dictionary _classesByHandle = new Dictionary(); + private static Dictionary _handlesByClass = new Dictionary(); + + public static OmsClass GetClass(this IOmsDatabase db, InstanceHandle handle) { if (_classesByHandle.ContainsKey(handle)) { @@ -273,28 +267,15 @@ public class OmsDatabase return null; } - internal void CreateClass(OmsClass item, Guid localClassGuid, Guid localInstanceGuid) + internal static void CreateClass(this IOmsDatabase db, OmsClass item, Guid localClassGuid, Guid localInstanceGuid) { - InstanceHandle ih = Oms.CreateInstanceOf(Oms.GetInstance(localClassGuid), localInstanceGuid); - RegisterClass(item, ih); + InstanceHandle ih = db.Oms.CreateInstanceOf(db.Oms.GetInstance(localClassGuid), localInstanceGuid); + db.RegisterClass(item, ih); } - private void RegisterClass(OmsClass item, InstanceHandle ih) + private static void RegisterClass(this IOmsDatabase db, OmsClass item, InstanceHandle ih) { _classesByHandle[ih] = item; _handlesByClass[item] = ih; } - - public static Guid GetGlobalIdentifierForProperty(PropertyInfo pi) - { - object[] attrs = pi.GetCustomAttributes(false); - foreach (object attr in attrs) - { - if (attr is OmsGlobalIdentifierAttribute) - { - return ((OmsGlobalIdentifierAttribute)attr).GlobalIdentifier; - } - } - return Guid.Empty; - } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsGlobalIdentifierAttribute.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsGlobalIdentifierAttribute.cs similarity index 96% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsGlobalIdentifierAttribute.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsGlobalIdentifierAttribute.cs index b2ef2e3..39c24f3 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsGlobalIdentifierAttribute.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsGlobalIdentifierAttribute.cs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; public class OmsGlobalIdentifierAttribute : Attribute { diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsInstanceList.cs similarity index 88% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsInstanceList.cs index eef5f29..9d44038 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsInstanceList.cs @@ -17,23 +17,25 @@ using System.Collections; -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; + +using Mocha.Core; public class OmsInstanceList { - internal static OmsInstanceList CreateGeneric(OmsDatabase omsdb, Type type) + internal static OmsInstanceList CreateGeneric(IOmsDatabase omsdb, Type type) { Guid gl = OmsDatabase.GetGlobalIdentifierForClass(type); - + Type t = typeof(OmsInstanceList<>); Type concrete = t.MakeGenericType(new Type[] { type }); - return (OmsInstanceList) concrete.Assembly.CreateInstance(concrete.FullName, false, System.Reflection.BindingFlags.Default, null, new object[] { omsdb, gl }, null, null); + return (OmsInstanceList)concrete.Assembly.CreateInstance(concrete.FullName, false, System.Reflection.BindingFlags.Default, null, new object[] { omsdb, gl }, null, null); } } public class OmsInstanceList : OmsInstanceList, IList where T : OmsClass { - private OmsDatabase omsdb; + private IOmsDatabase omsdb; private Guid classGuid; public class Enumerator : IEnumerator @@ -55,8 +57,8 @@ public class OmsInstanceList : OmsInstanceList, IList where T : OmsClass private IEnumerable _list; private IEnumerator _listEnumerator; - private OmsDatabase omsdb; - public Enumerator(OmsDatabase omsdb, IEnumerable list) + private IOmsDatabase omsdb; + public Enumerator(IOmsDatabase omsdb, IEnumerable list) { this.omsdb = omsdb; _list = list; @@ -79,7 +81,7 @@ public class OmsInstanceList : OmsInstanceList, IList where T : OmsClass } } - public OmsInstanceList(OmsDatabase omsdb, Guid classGuid) + public OmsInstanceList(IOmsDatabase omsdb, Guid classGuid) { this.omsdb = omsdb; this.classGuid = classGuid; diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNameAttribute.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsNameAttribute.cs similarity index 96% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNameAttribute.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsNameAttribute.cs index 7ff15f2..311db5d 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNameAttribute.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsNameAttribute.cs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; public class OmsNameAttribute : Attribute { diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNativeAttribute.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsNativeAttribute.cs similarity index 96% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNativeAttribute.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsNativeAttribute.cs index c808755..5b7a671 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNativeAttribute.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsNativeAttribute.cs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; public abstract class OmsNativeAttribute : Attribute { diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNativeRelationship.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsNativeRelationship.cs similarity index 97% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNativeRelationship.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsNativeRelationship.cs index fa290c7..d420a8e 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsNativeRelationship.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsNativeRelationship.cs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; public abstract class OmsNativeRelationship : Attribute { diff --git a/mocha-dotnet/src/lib/Mocha.Modeling/OmsObjectFactory.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsObjectFactory.cs new file mode 100644 index 0000000..146a00c --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsObjectFactory.cs @@ -0,0 +1,160 @@ +// Copyright (C) 2025 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.Reflection; +using System.Text; +using MBS.Core.Reflection; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Emit; +using Mocha.Core; +using Mocha.Core.Oop; +using Mocha.Modeling.CodeGeneration; +using Mocha.Modeling.CodeGeneration.Generators; + +namespace Mocha.Modeling; + +public class OmsObjectFactory : CSharpCodeGenerator where TOmsClass : IOmsItem +{ + public TOmsClass CreateDatabase(Oms oms) + { + string script = BuildScript(); + Assembly dynamicAssembly = CompileScript(script, [ typeof(TOmsClass).Assembly.Location ]); + + Type t = typeof(TOmsClass); + foreach (Type t2 in t.GetNestedTypes()) + { + CreateType(oms, t2); + } + + Type?[] tis; + try + { + tis = dynamicAssembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + tis = ex.Types; + } + + object? o = Activator.CreateInstance(tis[0]); + if ((o?.GetType().IsAssignableTo(typeof(TOmsClass))).GetValueOrDefault()) + { + TOmsClass db = (TOmsClass)o; + + return db; + } + throw new TypeInitializationException(typeof(TOmsClass).Name, null); + } + public TOmsClass CreateInstance(Oms oms, Guid globalIdentifier) + { + string script = BuildScript(globalIdentifier); + Assembly dynamicAssembly = CompileScript(script, [ typeof(TOmsClass).Assembly.Location ]); + + Type?[] tis; + try + { + tis = dynamicAssembly.GetTypes(); + } + catch (ReflectionTypeLoadException ex) + { + tis = ex.Types; + } + + object? o = Activator.CreateInstance(tis[0]); + tis[0].GetProperty("Oms", BindingFlags.Instance | BindingFlags.Public).SetValue(o, oms); + + if ((o?.GetType().IsAssignableTo(typeof(TOmsClass))).GetValueOrDefault()) + { + TOmsClass db = (TOmsClass)o; + + return db; + } + throw new TypeInitializationException(typeof(TOmsClass).Name, null); + } + + private void CreateType(Oms oms, Type t) + { + if (t.IsInterface) + { + string name = OmsDatabase.GetNameForClass(t); + Guid id = OmsDatabase.GetGlobalIdentifierForClass(t); + + oms.CreateClass(name, id); + } + } + + private string BuildScript(Guid? globalIdentifier = null) + { + Type t = typeof(TOmsClass); + + Namespace ns = new Namespace(new string[] { "Mocha", "Modeling", "Models", t.Name }); + + Mocha.Modeling.CodeGeneration.Class cl = GenerateClass(t, globalIdentifier); + ns.Items.Add(cl); + + return GenerateCode(ns); + } + + private Mocha.Modeling.CodeGeneration.Class GenerateDatabase(Type t) + { + Mocha.Modeling.CodeGeneration.Class cl = new CodeGeneration.Class(); + cl.Name = t.Name; + cl.InheritsClass = ParseObjectReference(SafeTypeName(t)); + + Type[] nts = t.GetNestedTypes(); + foreach (Type nt in nts) + { + cl.Items.Add(GenerateClass(nt)); + } + + foreach (PropertyInfo pi in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + cl.Items.Add(GenerateProperty(pi)); + } + + cl.Items.Add(new Property("Oms", new ObjectReference(new string[] { "Mocha", "Core", "Oms" }), null) { AccessModifier = CodeGeneration.AccessModifier.Public, UseAutoProperty = true, ReadOnly = false }); + cl.Items.Add(new CodeGeneration.Method("Initialize", new MethodParameter[] { new MethodParameter("oms", new ObjectReference(["Mocha", "Core", "Oms"]))}, new IMethodMember[] + { + new PropertyAssignment(new ObjectReference(["this", "Oms"]), "oms") + })); + + return cl; + } + + private Mocha.Modeling.CodeGeneration.Class GenerateClass(Type t, Guid? globalIdentifier = null) + { + Mocha.Modeling.CodeGeneration.Class cl = new CodeGeneration.Class(); + cl.Name = t.Name; + cl.InheritsClass = ParseObjectReference(SafeTypeName(t)); + + if (globalIdentifier == null) + { + globalIdentifier = OmsDatabase.GetGlobalIdentifierForClass(t); + } + + cl.Items.Add(new Property("GlobalIdentifier", new ObjectReference(new string[] { "System", "Guid" }), new CreateInstance(["System", "Guid"], [globalIdentifier.ToString()])) { AccessModifier = CodeGeneration.AccessModifier.Public }); + cl.Items.Add(new Property("Oms", new ObjectReference(new string[] { "Mocha", "Core", "Oms" }), null) { AccessModifier = CodeGeneration.AccessModifier.Public, UseAutoProperty = true, ReadOnly = false }); + + foreach (PropertyInfo pi in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + cl.Items.Add(GenerateProperty(pi)); + } + return cl; + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationship.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationship.cs similarity index 96% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationship.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationship.cs index 1c90a53..cff58bf 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationship.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationship.cs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; public class OmsRelationship where TSource : OmsClass where TDestination : OmsClass { diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTargetAttribute.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationshipTargetAttribute.cs similarity index 96% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTargetAttribute.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationshipTargetAttribute.cs index b3b837a..a378e9b 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTargetAttribute.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationshipTargetAttribute.cs @@ -15,13 +15,15 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; + +using Mocha.Core; public class OmsRelationshipTargetAttribute : OmsNativeRelationship { public OmsRelationshipTargetAttribute(string targetClassGlobalIdentifier) : base(KnownRelationshipGuids.Relationship__has_destination__Class, targetClassGlobalIdentifier) { } public OmsRelationshipTargetAttribute(Type targetClassType) : base(KnownRelationshipGuids.Relationship__has_destination__Class, null) { - + } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTypeAttribute.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationshipTypeAttribute.cs similarity index 95% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTypeAttribute.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationshipTypeAttribute.cs index 3cad9ae..97d256a 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsRelationshipTypeAttribute.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsRelationshipTypeAttribute.cs @@ -15,7 +15,9 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; + +using Mocha.Core; public class OmsRelationshipTypeAttribute : OmsNativeAttribute { diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsSIngular.cs b/mocha-dotnet/src/lib/Mocha.Modeling/OmsSIngular.cs similarity index 95% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsSIngular.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/OmsSIngular.cs index d07dc54..794b4eb 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsSIngular.cs +++ b/mocha-dotnet/src/lib/Mocha.Modeling/OmsSIngular.cs @@ -15,7 +15,9 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -namespace Mocha.Core.Modeling; +namespace Mocha.Modeling; + +using Mocha.Core; public class OmsSingularAttribute : OmsNativeAttribute { diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsNonsingularRelationshipProperty.cs b/mocha-dotnet/src/lib/Mocha.Modeling/PropertyImplementations/OmsNonsingularRelationshipProperty.cs similarity index 100% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsNonsingularRelationshipProperty.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/PropertyImplementations/OmsNonsingularRelationshipProperty.cs diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsRelationshipProperty.cs b/mocha-dotnet/src/lib/Mocha.Modeling/PropertyImplementations/OmsRelationshipProperty.cs similarity index 100% rename from mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsRelationshipProperty.cs rename to mocha-dotnet/src/lib/Mocha.Modeling/PropertyImplementations/OmsRelationshipProperty.cs diff --git a/mocha-dotnet/src/lib/Mocha.Testing/CoreOmsTestsBase.cs b/mocha-dotnet/src/lib/Mocha.Testing/CoreOmsTestsBase.cs new file mode 100644 index 0000000..6ad735a --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Testing/CoreOmsTestsBase.cs @@ -0,0 +1,12 @@ +using System; +using Mocha.Core; + +namespace Mocha.Testing; + +public class CoreOmsTestsBase : OmsTestsBase +{ + protected override Oms CreateOmsInternal() + { + return new EmbeddedCoreOms(); + } +} diff --git a/mocha-dotnet/src/lib/Mocha.Testing/EmbeddedCoreOms.cs b/mocha-dotnet/src/lib/Mocha.Testing/EmbeddedCoreOms.cs new file mode 100644 index 0000000..fe13cf2 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Testing/EmbeddedCoreOms.cs @@ -0,0 +1,37 @@ +using System; +using Mocha.Core; +using Mocha.Core.OmsImplementations; + +namespace Mocha.Testing; + +public class EmbeddedCoreOms : MemoryOms +{ + public string? SystemLibraryResourceName { get; set; } = "Mocha.Testing.Resources.net.alcetech.Mocha.Core.mcl"; + + protected override void OnTenantCreated(OmsTenantCreatedEventArgs e) + { + TenantHandle h = CurrentTenant; + + SelectTenant(e.Tenant); + if (SystemLibraryResourceName != null) + { + LibraryHandle lh = LoadLibrary(typeof(EmbeddedMiniOms), SystemLibraryResourceName); + AddLibraryReference(lh); + } + ReleaseTenant(); + + if (h != TenantHandle.Empty) + { + SelectTenant(h); + } + + base.OnTenantCreated(e); + } + + protected override void InitializeInternal() + { + base.InitializeInternal(); + + CreateTenant("super"); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Testing/Mocha.Testing.csproj b/mocha-dotnet/src/lib/Mocha.Testing/Mocha.Testing.csproj index 1f9d724..92f9e4c 100644 --- a/mocha-dotnet/src/lib/Mocha.Testing/Mocha.Testing.csproj +++ b/mocha-dotnet/src/lib/Mocha.Testing/Mocha.Testing.csproj @@ -18,6 +18,7 @@ + diff --git a/mocha-dotnet/src/lib/Mocha.Testing/OmsTestsBase.cs b/mocha-dotnet/src/lib/Mocha.Testing/OmsTestsBase.cs index 3591714..24fe1c1 100644 --- a/mocha-dotnet/src/lib/Mocha.Testing/OmsTestsBase.cs +++ b/mocha-dotnet/src/lib/Mocha.Testing/OmsTestsBase.cs @@ -31,9 +31,14 @@ public abstract class OmsTestsBase return "Mocha.Testing.Resources." + name; } - protected Oms CreateOms() + protected virtual Oms CreateOmsInternal() { - Oms oms = new EmbeddedMiniOms(); + return new EmbeddedMiniOms(); + } + + private Oms CreateOms() + { + Oms oms = CreateOmsInternal(); oms.TenantCreated += delegate (object sender, OmsTenantCreatedEventArgs e) { oms.PushTenant(e.Tenant); 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 9360e94..b3b66ad 100644 --- a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs +++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.McxMini/McxMiniPlugin.cs @@ -1,5 +1,6 @@ namespace Mocha.Plugins.Libraries.McxMini; +using System.Reflection.Metadata.Ecma335; using System.Runtime.InteropServices; using MBS.Core; using Mocha.Core; @@ -99,6 +100,11 @@ public class McxMiniLibraryPlugin : LibraryPlugin { libraryRefsSection = sections["LibraryRefs"]; } + McxSection defsSection = new McxSection(); + if (sections.ContainsKey("Defs")) + { + defsSection = sections["Defs"]; + } if (guidsSection.Count != instancesSection.Count) { @@ -176,6 +182,21 @@ public class McxMiniLibraryPlugin : LibraryPlugin library.LibraryReferences.Add(guid); } + r.BaseStream.Seek(defsSection.Offset, SeekOrigin.Begin); + for (int i = 0; i < defsSection.Count; i++) + { + string name = r.ReadNullTerminatedString(); + string value = r.ReadNullTerminatedString(); + if (!library.EntityDefinitions.ContainsKey(name)) + { + library.EntityDefinitions.Add(name, value); + } + else + { + Console.Error.WriteLine("duplicate entity definition for key '{0}'", name); + } + } + if (resourcesSection.Length > 0) { diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/Modeling/OmsModelingTestsBase.cs b/mocha-dotnet/tests/Mocha.Core.Tests/PersistenceTests.cs similarity index 65% rename from mocha-dotnet/tests/Mocha.Core.Tests/Modeling/OmsModelingTestsBase.cs rename to mocha-dotnet/tests/Mocha.Core.Tests/PersistenceTests.cs index 6f7ce8d..6941291 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/Modeling/OmsModelingTestsBase.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/PersistenceTests.cs @@ -1,4 +1,4 @@ -// Copyright (C) 2024 Michael Becker +// Copyright (C) 2025 Michael Becker // // This file is part of Mocha.NET. // @@ -15,21 +15,9 @@ // You should have received a copy of the GNU General Public License // along with Mocha.NET. If not, see . -using Mocha.Core.Modeling; using Mocha.Testing; -namespace Mocha.Core.Tests.Modeling; - -public abstract class OmsModelingTestsBase : OmsTestsBase +public class PersistenceTests : OmsTestsBase { - protected abstract OmsDatabase GetDatabase(); - - protected override void AfterSetup() - { - base.AfterSetup(); - - OmsDatabase db = GetDatabase(); - db.Initialize(Oms); - } - + } \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/TinyCoreTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/TinyCoreTests.cs new file mode 100644 index 0000000..8707233 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Core.Tests/TinyCoreTests.cs @@ -0,0 +1,92 @@ +// 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.Runtime.CompilerServices; +using MBS.Core.Collections; +using Mocha.Core.Oop; +using Mocha.Testing; + +namespace Mocha.Core.Tests; + +[TestFixture] +public class TinyCoreTests : CoreOmsTestsBase +{ + + [Test] + public void PrerequisitesDefined_Level1_Classes() + { + Oms.SelectTenant(Oms.GetTenantByName("super")); + + // level 1 - make sure that Class, Attribute, and Relationship exist + InstanceHandle c_Class = Oms.GetInstance(KnownInstanceGuids.Classes.Class); + Assert.That(c_Class, Is.Not.EqualTo(InstanceHandle.Empty)); + + InstanceHandle c_Attribute = Oms.GetInstance(KnownInstanceGuids.Classes.Attribute); + Assert.That(c_Attribute, Is.Not.EqualTo(InstanceHandle.Empty)); + + InstanceHandle c_Relationship = Oms.GetInstance(KnownInstanceGuids.Classes.Relationship); + Assert.That(c_Relationship, Is.Not.EqualTo(InstanceHandle.Empty)); + + // if we reach here, we should be able to read classes and relationships, but not much else + } + + [Test] + public void PrerequisitesDefined_Level2_DataTypes() + { + // level 2 - make sure that the primary data types Text, Boolean, Numeric, and Date exist + InstanceHandle c_TextAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute); + Assert.That(c_TextAttribute, Is.Not.EqualTo(InstanceHandle.Empty)); + + InstanceHandle c_BooleanAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute); + Assert.That(c_BooleanAttribute, Is.Not.EqualTo(InstanceHandle.Empty)); + + InstanceHandle c_NumericAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.NumericAttribute); + Assert.That(c_NumericAttribute, Is.Not.EqualTo(InstanceHandle.Empty)); + + InstanceHandle c_DateAttribute = Oms.GetInstance(KnownInstanceGuids.Classes.DateAttribute); + Assert.That(c_DateAttribute, Is.Not.EqualTo(InstanceHandle.Empty)); + + // if we've reached this point, we should be able to get/set attribute values on existing classes + // we may not be able to create new classes without at least level 3... + } + + [Test] + public void PrerequisitesDefined_Level3_ClassHierarchy() + { + // level 3 - ensure that we can see parent classes + InstanceHandle r_Class__has__Instance = Oms.GetInstance(KnownRelationshipGuids.Class__has__Instance); + InstanceHandle r_Instance__for__Class = Oms.GetInstance(KnownRelationshipGuids.Instance__for__Class); + Assert.That(r_Class__has__Instance, Is.Not.EqualTo(InstanceHandle.Empty)); + Assert.That(r_Instance__for__Class, Is.Not.EqualTo(InstanceHandle.Empty)); + + // now we can create classes and instances, because we have `Instance.for Class` relationships + // but we can't do much introspection on the classes and hierarchy layouts without at least level 4... + + // level 4 - enable a rich object-oriented class hierarchy + InstanceHandle r_Class__has_sub__Class = Oms.GetInstance(KnownRelationshipGuids.Class__has_sub__Class); + InstanceHandle r_Class__has_super__Class = Oms.GetInstance(KnownRelationshipGuids.Class__has_super__Class); + Assert.That(r_Class__has_sub__Class, Is.Not.EqualTo(InstanceHandle.Empty)); + Assert.That(r_Class__has_super__Class, Is.Not.EqualTo(InstanceHandle.Empty)); + + // if we reach here we have a fairly robust Compact Core; anything more complicated than this (e.g. OOP + // methods, method bindings, etc.) really ought to be separate .mcl's + + // check to see if Derived works; I suppose this could be optional but it's a nice feature + InstanceHandle a_Derived = Oms.GetInstance(KnownAttributeGuids.Boolean.Derived); + Assert.That(a_Derived, Is.Not.EqualTo(InstanceHandle.Empty)); + } +} \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/UI/UITests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/UI/UITests.cs index d4b8b95..303d52f 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/UI/UITests.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/UI/UITests.cs @@ -57,20 +57,28 @@ public class UITests : OmsTestsBase InstanceHandle loginPageSubedit = Oms.GetInstance(new Guid("{2b7d4481-b7c2-4e26-a917-e3ff7c367a8a}")); OmsContext context = Oms.CreateContext(); + // create a dictionary to hold values for our element contents + // the key is the string value of the GUID of the element content Dictionary dict = new Dictionary(); - dict["{c67f305e-bd4d-4628-816b-55fb85ea1b67}"] = "testing"; - dict["{51b51be3-44fd-48f1-971f-682aee0a6132}"] = "testing"; + dict["{c67f305e-bd4d-4628-816b-55fb85ea1b67}"] = "testing"; // user name + dict["{51b51be3-44fd-48f1-971f-682aee0a6132}"] = "testing"; // password + // set global work data `Referral URL` to our example referrer context.SetGlobal(Oms.GetInstance(KnownAttributeGuids.Text.ReferralURL), "https://example.com/referer"); Response pageOutput = Oms.ProcessElement(context, loginPageSubedit, dict); JsonObject json = pageOutput.GetResponse(Oms, context); + // check to ensure the global work data `Token` is properly set string s_Token = context.GetGlobal(Oms.GetInstance(KnownAttributeGuids.Text.Token)); Assert.That(s_Token, Is.Not.Empty); Assert.That(json["result"].GetValue(), Is.EqualTo("success")); + + // check to ensure the Build UI Response Method tells us to redirect to a destination URL Assert.That(json["type"].GetValue(), Is.EqualTo("redirect")); + + // check to ensure the Build UI Response Method receives the value assigned to `Referral URL` earlier Assert.That(json["destinationUrl"].GetValue(), Is.EqualTo("https://example.com/referer")); } diff --git a/mocha-dotnet/tests/Mocha.Modeling.Tests/GlobalUsings.cs b/mocha-dotnet/tests/Mocha.Modeling.Tests/GlobalUsings.cs new file mode 100644 index 0000000..cefced4 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Modeling.Tests/GlobalUsings.cs @@ -0,0 +1 @@ +global using NUnit.Framework; \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Modeling.Tests/Mocha.Modeling.Tests.csproj b/mocha-dotnet/tests/Mocha.Modeling.Tests/Mocha.Modeling.Tests.csproj new file mode 100644 index 0000000..bd642b5 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Modeling.Tests/Mocha.Modeling.Tests.csproj @@ -0,0 +1,28 @@ + + + + net8.0 + enable + enable + + false + true + + + + + + + + + + + + + + + + + + + diff --git a/mocha-dotnet/tests/Mocha.Modeling.Tests/OmsModelingTestsBase.cs b/mocha-dotnet/tests/Mocha.Modeling.Tests/OmsModelingTestsBase.cs new file mode 100644 index 0000000..59cee59 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Modeling.Tests/OmsModelingTestsBase.cs @@ -0,0 +1,76 @@ +// 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 Mocha.Modeling; +using Mocha.Testing; + +namespace Mocha.Modeling.Tests; + +public abstract class OmsModelingTestsBase2 : OmsTestsBase where TDatabase : IOmsDatabase +{ + private TDatabase? _db; + protected virtual TDatabase GetDatabase() + { + if (_db == null) + { + _db = CreateDatabase(); + } + return _db; + } + + private TDatabase CreateDatabase() + { + OmsObjectFactory factory = new OmsObjectFactory(); + return factory.CreateDatabase(Oms); + } + + protected override void AfterSetup() + { + base.AfterSetup(); + + TDatabase db = GetDatabase(); + db.Initialize(Oms); + } + +} +public abstract class OmsModelingTestsBase : OmsTestsBase where TDatabase : IOmsDatabase, new() +{ + private TDatabase? _db; + protected virtual TDatabase GetDatabase() + { + if (_db == null) + { + _db = CreateDatabase(); + } + return _db; + } + + private TDatabase CreateDatabase() + { + TDatabase db = new TDatabase(); + return db; + } + + protected override void AfterSetup() + { + base.AfterSetup(); + + TDatabase db = GetDatabase(); + db.Initialize(Oms); + } + +} diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/MovieRentalTests.cs b/mocha-dotnet/tests/Mocha.Modeling.Tests/SampleDatabases/MovieRentalTests.cs similarity index 100% rename from mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/MovieRentalTests.cs rename to mocha-dotnet/tests/Mocha.Modeling.Tests/SampleDatabases/MovieRentalTests.cs diff --git a/mocha-dotnet/tests/Mocha.Modeling.Tests/SampleDatabases/VehicleForHireInterfaceTests.cs b/mocha-dotnet/tests/Mocha.Modeling.Tests/SampleDatabases/VehicleForHireInterfaceTests.cs new file mode 100644 index 0000000..ea50ce2 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Modeling.Tests/SampleDatabases/VehicleForHireInterfaceTests.cs @@ -0,0 +1,121 @@ +// Copyright (C) 2025 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 Microsoft.CodeAnalysis.VisualBasic.Syntax; +using Mocha.Modeling; +using Mocha.Modeling.Tests; + +namespace Mocha.Core.Tests.SampleDatabases; + +public interface IVehicleForHireDB : IOmsDatabase +{ + + [OmsName("Business"), OmsGlobalIdentifier("{c4e1d676-726c-42f4-9edb-a0ca0ee0e612}")] + public interface Business : IOmsClass + { + [OmsGlobalIdentifier("{9153A637-992E-4712-ADF2-B03F0D9EDEA6}")] + string Name { get; set; } + + /// + /// Business.has Driver + /// + /// + [OmsRelationshipType("has"), OmsGlobalIdentifier("{63e5ab3a-4009-4af9-8171-5b23472c1c8c}")] + IList Drivers { get; } + + /// + /// Business.has Vehicle + /// + /// + [OmsRelationshipType("has"), OmsGlobalIdentifier("{c54125f6-c3b0-40b2-99f9-e0ed20a75bfa}")] + IList Vehicles { get; } + + [OmsRelationshipType("has"), OmsGlobalIdentifier("{9d4946c3-db8d-4f27-b21b-52e9b0fdf231}")] + BusinessPermit Permit { get; set; } + } + + [OmsName("Business Permit"), OmsGlobalIdentifier("{7680b302-c9c2-4275-abf8-373adafbf58e}")] + public interface BusinessPermit : IOmsClass + { + + } + + [OmsName("Driver"), OmsGlobalIdentifier("{10c51c07-c757-4b00-b406-d54c99460f32}")] + public interface Driver : IOmsClass + { + + } + + [OmsName("Vehicle"), OmsGlobalIdentifier("{4b6f186c-babe-4c42-840b-3840373f68c2}")] + public interface Vehicle : IOmsClass + { + [OmsRelationshipType("has"), OmsGlobalIdentifier("{867126b9-294e-454f-b9c5-88df44353014}"), OmsRelationshipTarget("{38a492b9-901f-4074-af45-642e812b51f5}")] + VehiclePermit Permit { get; set; } + + [OmsRelationshipType("has multiple"), OmsGlobalIdentifier("{2b3111a2-d520-4897-bd3d-00b2e9a49f8b}"), OmsRelationshipTarget("{38a492b9-901f-4074-af45-642e812b51f5}")] + IList Permits { get; } + } + + [OmsName("Vehicle Permit"), OmsGlobalIdentifier("{38a492b9-901f-4074-af45-642e812b51f5}")] + public interface VehiclePermit : IOmsClass + { + [OmsName("Permit Index"), OmsGlobalIdentifier("{759f4beb-ee43-47b8-9add-16967bba8ec8}")] + int PermitIndex { get; set; } + + [OmsName("Permit Name"), OmsGlobalIdentifier("{c4f4a59f-c246-4d88-a2fd-588195d5e943}")] + string PermitName { get; set; } + } + + IList Businesses { get; } +} + +public class VehicleForHireInterfaceTests : OmsModelingTestsBase2 +{ + + [Test] + public void ClassesCreated() + { + CheckClassExists(new Guid("{c4e1d676-726c-42f4-9edb-a0ca0ee0e612}"), "Business"); + CheckClassExists(new Guid("{10c51c07-c757-4b00-b406-d54c99460f32}"), "Driver"); + CheckClassExists(new Guid("{4b6f186c-babe-4c42-840b-3840373f68c2}"), "Vehicle"); + CheckClassExists(new Guid("{38a492b9-901f-4074-af45-642e812b51f5}"), "Vehicle Permit"); + } + + [Test] + public void RelationshipsCreated() + { + CheckRelationshipExists(new Guid("{867126b9-294e-454f-b9c5-88df44353014}"), new Guid("{4b6f186c-babe-4c42-840b-3840373f68c2}"), "has", new Guid("{38a492b9-901f-4074-af45-642e812b51f5}")); + } + + [Test] + public void CreateBusinessAndTestLinq() + { + IVehicleForHireDB db = GetDatabase(); + IVehicleForHireDB.Business busn = db.CreateInstance(); + db.Businesses.Add(busn); + + var query1 = from b in db.Businesses select b; + Assert.That(query1.Count(), Is.EqualTo(1)); + + var query2 = from b in db.Businesses where b.Name == "test" select b; + Assert.That(query2.Count(), Is.EqualTo(0)); + + var query3 = from b in db.Businesses where b.Name == "Ryde Rentals Transportation" select b; + var busn3 = query3.First(); + Assert.That(busn3.GlobalIdentifier, Is.EqualTo(busn.GlobalIdentifier)); + } +} \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/VehicleForHireTests.cs b/mocha-dotnet/tests/Mocha.Modeling.Tests/SampleDatabases/VehicleForHireTests.cs similarity index 93% rename from mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/VehicleForHireTests.cs rename to mocha-dotnet/tests/Mocha.Modeling.Tests/SampleDatabases/VehicleForHireTests.cs index 27414a6..b8efe5f 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/VehicleForHireTests.cs +++ b/mocha-dotnet/tests/Mocha.Modeling.Tests/SampleDatabases/VehicleForHireTests.cs @@ -16,8 +16,8 @@ // along with Mocha.NET. If not, see . -using Mocha.Core.Modeling; -using Mocha.Core.Tests.Modeling; +using Mocha.Modeling; +using Mocha.Modeling.Tests; namespace Mocha.Core.Tests.SampleDatabases; @@ -49,7 +49,7 @@ public class VehicleForHireDB : OmsDatabase public readonly IList Vehicles; [OmsRelationshipType("has"), OmsGlobalIdentifier("{9d4946c3-db8d-4f27-b21b-52e9b0fdf231}")] - public BusinessPermit Permit; + public OmsRelationship Permit; } [OmsName("Business Permit"), OmsGlobalIdentifier("{7680b302-c9c2-4275-abf8-373adafbf58e}")] @@ -87,10 +87,8 @@ public class VehicleForHireDB : OmsDatabase public readonly IList Businesses; } -public class VehicleForHireTests : OmsModelingTestsBase +public class VehicleForHireTests : OmsModelingTestsBase { - private OmsDatabase db = new VehicleForHireDB(); - protected override OmsDatabase GetDatabase() => db; [Test] public void ClassesCreated() @@ -110,7 +108,7 @@ public class VehicleForHireTests : OmsModelingTestsBase [Test] public void CreateBusinessAndTestLinq() { - VehicleForHireDB db = (VehicleForHireDB) GetDatabase(); + VehicleForHireDB db = GetDatabase(); VehicleForHireDB.Business busn = new VehicleForHireDB.Business("Ryde Rentals Transportation"); db.Businesses.Add(busn); diff --git a/mocha-dotnet/tests/Mocha.Modeling.Tests/UnitTest1.cs b/mocha-dotnet/tests/Mocha.Modeling.Tests/UnitTest1.cs new file mode 100644 index 0000000..96db036 --- /dev/null +++ b/mocha-dotnet/tests/Mocha.Modeling.Tests/UnitTest1.cs @@ -0,0 +1,15 @@ +namespace Mocha.Modeling.Tests; + +public class Tests +{ + [SetUp] + public void Setup() + { + } + + [Test] + public void Test1() + { + Assert.Pass(); + } +} \ No newline at end of file diff --git a/mocha-dotnet/tests/Mocha.Oms.Server.Tests/RemoteTests.cs b/mocha-dotnet/tests/Mocha.Oms.Server.Tests/RemoteTests.cs index 197a751..9e05421 100644 --- a/mocha-dotnet/tests/Mocha.Oms.Server.Tests/RemoteTests.cs +++ b/mocha-dotnet/tests/Mocha.Oms.Server.Tests/RemoteTests.cs @@ -156,9 +156,18 @@ public class RemoteTests Assert.That(json["text"].ToString(), Is.EqualTo(sz)); } + /// + /// Ensures that a POST request sent to an element properly processes the element with the given + /// element content parameter values. + /// + /// [Test] - public async Task Element_with_Parameters_Test() + public async Task Element_with_Parameters_Test__CreateClass() { + // POST /tenants/{tenantname}/instances/{task_iid}/element + // + // ec_56$48=test&ec_56$49=57$1&ec_56$50=4170$1 + InstanceHandle h = program.Oms.GetInstance(KnownInstanceGuids.Classes.Class); InstanceKey k = program.Oms.GetInstanceKey(h); Guid g = program.Oms.GetGlobalIdentifier(h); @@ -166,10 +175,19 @@ public class RemoteTests HttpClient client = new HttpClient(); + // ec_56%2448=Test+Class+1&ec_56%2449=57%241&ec_56%2450=4170%241&ReturnToURL= + // 2997$11 Create Class Dictionary coll = new Dictionary(); - coll["56$272"] = "86$1"; - HttpResponseMessage resp = client.Send(new HttpRequestMessage(HttpMethod.Post, BuildUrl("/tenants/super/instances/2997$48/element")) { Content = new FormUrlEncodedContent(coll) }); + coll["56$48"] = "Test Class 1"; + coll["56$49"] = "57$1"; + coll["56$50"] = "4170$1"; + HttpResponseMessage resp = client.Send(new HttpRequestMessage(HttpMethod.Post, BuildUrl("/tenants/super/instances/2997$11/element")) { Content = new FormUrlEncodedContent(coll) }); + + // Dictionary coll = new Dictionary(); + // coll["56$272"] = "86$1"; + // HttpResponseMessage resp = client.Send(new HttpRequestMessage(HttpMethod.Post, BuildUrl("/tenants/super/instances/2997$48/element")) { Content = new FormUrlEncodedContent(coll) }); + Assert.That(resp.StatusCode, Is.EqualTo(System.Net.HttpStatusCode.OK)); Assert.That(resp.Content.Headers.ContentType.ToString(), Is.EqualTo("application/json")); @@ -177,7 +195,7 @@ public class RemoteTests JsonObject json = JsonNode.Parse(content) as JsonObject; Assert.That(json["result"].ToString(), Is.EqualTo("success")); - Assert.That(json["value"]["widget"].ToString(), Is.EqualTo("root")); + Assert.That(json["type"].ToString(), Is.EqualTo("redirect")); }