let's push through another big beautiful commit

This commit is contained in:
Michael Becker 2025-07-05 23:34:56 -04:00
parent 7d535c00bc
commit 3bc0641c99
74 changed files with 2286 additions and 211 deletions

4
.gitignore vendored
View File

@ -418,6 +418,10 @@ FodyWeavers.xsd
# Mocha CUP output directory
output
# Mocha compiled files
*.mcl
*.mcx
# Backups (made by Notepad++)
*.bak

@ -1 +1 @@
Subproject commit 94de4f0a8e855543c0f1b0679aebf7caa2db56c9
Subproject commit 47d874d77af1f29f7870ee6d885cbf6138b7651d

View File

@ -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}

View File

@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="../../../../framework-dotnet/framework-dotnet/src/lib/MBS.Core/MBS.Core.csproj" />
<ProjectReference Include="../../lib/Mocha.Core/Mocha.Core.csproj" />
<ProjectReference Include="../../plugins/Mocha.Plugins.Libraries.McxMini/Mocha.Plugins.Libraries.McxMini.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,9 @@
using System;
namespace Mocha.Compilers.XmlPreprocessor.ObjectModel;
public class ElementContent : Instance
{
public ElementContent() : base() { }
public ElementContent(InstanceReference reference) : base(reference) { }
}

View File

@ -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;
}
}

View File

@ -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<ElementContent> ElementContents { get; } = new List<ElementContent>();
}
public class TaskProcessing
{
public List<ElementContent> ElementContents { get; } = new List<ElementContent>();
}
public TaskSelection Selection { get; } = new TaskSelection();
public TaskProcessing Processing { get; } = new TaskProcessing();
}

View File

@ -0,0 +1,81 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using System.Xml;
using Mocha.Compilers.XmlPreprocessor.ObjectModel;
class Parser
{
public Dictionary<string, string> EntityDefinitions { get; } = new Dictionary<string, string>();
private string EvaluateEntityDefinitions(string value)
{
foreach (KeyValuePair<string, string> kvp in EntityDefinitions)
{
value = value.Replace(String.Concat("@", kvp.Key, ";"), kvp.Value);
}
return value;
}
public List<Instance> Instances { get; } = new List<Instance>();
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<InstanceReference> list = new List<InstanceReference>();
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<InstanceReference> list = new List<InstanceReference>();
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);
}
}

View File

@ -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<string, string> kvp in libSystem.EntityDefinitions)
{
p.EntityDefinitions[kvp.Key] = kvp.Value;
}
p.Load(fs);
}
}

View File

@ -176,10 +176,16 @@ public class Program : WebApplication
if (jo.ContainsKey("name"))
{
string name = jo["name"].GetValue<string>();
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<int>();
}
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 });

View File

@ -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);
}
}
}

View File

@ -158,16 +158,11 @@ namespace Mocha.Core
return String.Format("{0}${1}", ClassIndex, InstanceIndex);
}
internal string GetDerivedDataString(Dictionary<InstanceHandle, object?>? derivedData = null)
internal static string GetDerivedDataString(Dictionary<InstanceHandle, object?> derivedData)
{
MemoryStream ms = new MemoryStream();
BinaryWriter bw = new BinaryWriter(ms);
if (derivedData == null)
{
derivedData = GetDerivedData();
}
foreach (KeyValuePair<InstanceHandle, object?> kvp in derivedData)
{
if (kvp.Value is string)

View File

@ -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
/// <summary>
/// PU - Process Updates Method - 11
/// </summary>
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

View File

@ -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}");

View File

@ -7,9 +7,9 @@ public class Library
/// Applies all attribute and relationship assignments once all instances have been
/// accounted for.
/// </summary>
public void Link()
{
}
public void Link()
{
}
public Guid GlobalIdentifier { get; set; } = Guid.Empty;
@ -17,4 +17,5 @@ public class Library
public List<LibraryAttribute> Attributes { get; } = new List<LibraryAttribute>();
public List<LibraryRelationship> Relationships { get; } = new List<LibraryRelationship>();
public List<Guid> LibraryReferences { get; } = new List<Guid>();
public Dictionary<string, string> EntityDefinitions { get; } = new Dictionary<string, string>();
}

View File

@ -0,0 +1,40 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.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<InstanceHandle> 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;
}
}

View File

@ -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<string, string> 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<string>(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<bool>(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<decimal>(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<DateTime>(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;
}
}

View File

@ -15,26 +15,6 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
//
// Transaction.cs
//
// Author:
// Michael Becker <alcexhim@gmail.com>
//
// 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 <http://www.gnu.org/licenses/>.
using System;
using System.Collections;
using System.Collections.Generic;

View File

@ -0,0 +1,54 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using System.Text.Json.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<string>(ParentInstance, OMS.GetInstance(KnownAttributeGuids.Text.Value));
}
if (value != null)
{
// if we finally have a value, use it
obj.Add("value", value);
}
}
}

View File

@ -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<string>(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

View File

@ -0,0 +1,28 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public enum AccessModifier
{
None,
Public,
Protected,
Internal,
ProtectedInternal,
Private
}

View File

@ -0,0 +1,28 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.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<IClassMember> Items { get; } = new List<IClassMember>();
}

View File

@ -0,0 +1,29 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
using System.Text;
public abstract class CodeGenerator
{
protected abstract string GenerateCodeInternal(ICodeItem item);
public string GenerateCode(ICodeItem item)
{
return GenerateCodeInternal(item);
}
}

View File

@ -0,0 +1,25 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
using System.Collections.Generic;
public class Document
{
public List<Class> Classes { get; } = new List<Class>();
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public class CreateInstance : IMethodMember
{
public ObjectReference ObjectReference { get; set; }
public List<object> ParameterValues { get; } = new List<object>();
public CreateInstance(IEnumerable<string> objectNames, IEnumerable<object> parameterValues)
{
ObjectReference = new ObjectReference(objectNames);
ParameterValues.AddRange(parameterValues);
}
}

View File

@ -0,0 +1,41 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration.Expressions;
public class MethodCall : IMethodMember
{
public ObjectReference ObjectName { get; set; }
public string MethodName { get; set; }
public List<MethodParameter> Parameters { get; } = new List<MethodParameter>();
public IEnumerable<ObjectReference>? GenericParameters { get; set; } = null;
public MethodCall(IEnumerable<string> objectNames, string methodName, IEnumerable<object>? 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);
}
}
}
}

View File

@ -0,0 +1,31 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.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;
}

View File

@ -0,0 +1,28 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public class ObjectReference
{
public IEnumerable<string> ObjectNames { get; set; } = null;
public ObjectReference(IEnumerable<string> objectNames)
{
ObjectNames = objectNames;
}
}

View File

@ -0,0 +1,30 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.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;
}
}

View File

@ -0,0 +1,27 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration.Expressions;
public class Return : IMethodMember
{
public object? Value { get; set; }
public Return(object? value)
{
Value = value;
}
}

View File

@ -0,0 +1,261 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using System.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<string>? 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<T>(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;
}
}

View File

@ -0,0 +1,160 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using System.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<MethodParameter> 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<object> 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<string> 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<string>? 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<string> dlls = new List<string> {
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<Diagnostic> failures = result.Diagnostics.Where(diagnostic =>
diagnostic.IsWarningAsError ||
diagnostic.Severity == DiagnosticSeverity.Error);
List<string> errors = new List<string>();
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());
}
}
}
}

View File

@ -0,0 +1,23 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public interface IClassMember : ICodeItem
{
string? Name { get; set; }
}

View File

@ -0,0 +1,22 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public interface ICodeExpression
{
}

View File

@ -0,0 +1,22 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public interface ICodeItem
{
}

View File

@ -0,0 +1,24 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public interface ICodeType : ICodeItem
{
public AccessModifier AccessModifier { get; set; }
public string? Name { get; set; }
}

View File

@ -0,0 +1,22 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public interface IMethodMember : ICodeItem
{
}

View File

@ -0,0 +1,43 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public class Method : IClassMember
{
public string? Name { get; set; } = null;
public List<IMethodMember> Items { get; } = new List<IMethodMember>();
public List<MethodParameter> Parameters { get; } = new List<MethodParameter>();
public Method(IEnumerable<IMethodMember>? items = null) : this(null, items)
{
}
public Method(string name, IEnumerable<IMethodMember>? items = null)
{
Name = name;
if (items != null)
{
Items.AddRange(items);
}
}
public Method(string name, IEnumerable<MethodParameter> parms, IEnumerable<IMethodMember>? items = null)
: this(name, items)
{
Parameters.AddRange(parms);
}
}

View File

@ -0,0 +1,29 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling.CodeGeneration;
public class Namespace : ICodeItem
{
public string[] Names { get; set; }
public List<ICodeType> Items { get; } = new List<ICodeType>();
public Namespace(string[] names)
{
Names = names;
}
}

View File

@ -0,0 +1,37 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.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;
}

View File

@ -1,4 +1,6 @@
namespace Mocha.Core.Modeling.ImplicitAttributes;
namespace Mocha.Modeling.ImplicitAttributes;
using Mocha.Core;
public class TextAttribute
{

View File

@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.14.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Scripting" Version="4.14.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../../../framework-dotnet/framework-dotnet/src/lib/MBS.Core/MBS.Core.csproj" />
<ProjectReference Include="../Mocha.Core/Mocha.Core.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,24 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Modeling;
public abstract class ObjectFactory
{
}

View File

@ -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<Guid, object> _attributeValuesTemp = new Dictionary<Guid, object>();
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);
}
}
}
}
}

View File

@ -0,0 +1,89 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using System.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;
}
}

View File

@ -1,4 +1,4 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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<T>(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<T> factory = new OmsObjectFactory<T>();
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<InstanceHandle> list = new List<InstanceHandle>();
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<InstanceHandle, OmsClass> _classesByHandle = new Dictionary<InstanceHandle, OmsClass>();
private Dictionary<OmsClass, InstanceHandle> _handlesByClass = new Dictionary<OmsClass, InstanceHandle>();
public OmsClass GetClass(InstanceHandle handle)
// FIXME ???
private static Dictionary<InstanceHandle, OmsClass> _classesByHandle = new Dictionary<InstanceHandle, OmsClass>();
private static Dictionary<OmsClass, InstanceHandle> _handlesByClass = new Dictionary<OmsClass, InstanceHandle>();
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;
}
}

View File

@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.Modeling;
namespace Mocha.Modeling;
public class OmsGlobalIdentifierAttribute : Attribute
{

View File

@ -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<T> : OmsInstanceList, IList<T> where T : OmsClass
{
private OmsDatabase omsdb;
private IOmsDatabase omsdb;
private Guid classGuid;
public class Enumerator : IEnumerator<T>
@ -55,8 +57,8 @@ public class OmsInstanceList<T> : OmsInstanceList, IList<T> where T : OmsClass
private IEnumerable<InstanceHandle> _list;
private IEnumerator<InstanceHandle> _listEnumerator;
private OmsDatabase omsdb;
public Enumerator(OmsDatabase omsdb, IEnumerable<InstanceHandle> list)
private IOmsDatabase omsdb;
public Enumerator(IOmsDatabase omsdb, IEnumerable<InstanceHandle> list)
{
this.omsdb = omsdb;
_list = list;
@ -79,7 +81,7 @@ public class OmsInstanceList<T> : OmsInstanceList, IList<T> where T : OmsClass
}
}
public OmsInstanceList(OmsDatabase omsdb, Guid classGuid)
public OmsInstanceList(IOmsDatabase omsdb, Guid classGuid)
{
this.omsdb = omsdb;
this.classGuid = classGuid;

View File

@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.Modeling;
namespace Mocha.Modeling;
public class OmsNameAttribute : Attribute
{

View File

@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.Modeling;
namespace Mocha.Modeling;
public abstract class OmsNativeAttribute : Attribute
{

View File

@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.Modeling;
namespace Mocha.Modeling;
public abstract class OmsNativeRelationship : Attribute
{

View File

@ -0,0 +1,160 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using System.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<TOmsClass> : 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;
}
}

View File

@ -15,7 +15,7 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.Modeling;
namespace Mocha.Modeling;
public class OmsRelationship<TSource, TDestination> where TSource : OmsClass where TDestination : OmsClass
{

View File

@ -15,13 +15,15 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.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)
{
}
}

View File

@ -15,7 +15,9 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.Modeling;
namespace Mocha.Modeling;
using Mocha.Core;
public class OmsRelationshipTypeAttribute : OmsNativeAttribute
{

View File

@ -15,7 +15,9 @@
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core.Modeling;
namespace Mocha.Modeling;
using Mocha.Core;
public class OmsSingularAttribute : OmsNativeAttribute
{

View File

@ -0,0 +1,12 @@
using System;
using Mocha.Core;
namespace Mocha.Testing;
public class CoreOmsTestsBase : OmsTestsBase
{
protected override Oms CreateOmsInternal()
{
return new EmbeddedCoreOms();
}
}

View File

@ -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");
}
}

View File

@ -18,6 +18,7 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Resources/net.alcetech.Mocha.Core.mcl" />
<EmbeddedResource Include="Resources/net.alcetech.Mocha.System.mcl" />
<EmbeddedResource Include="Resources/net.alcetech.Mocha.Web.mcl" />
</ItemGroup>

View File

@ -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);

View File

@ -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)
{

View File

@ -1,4 +1,4 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// 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 <https://www.gnu.org/licenses/>.
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);
}
}

View File

@ -0,0 +1,92 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using System.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));
}
}

View File

@ -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<string, string> dict = new Dictionary<string, string>();
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<string>(Oms.GetInstance(KnownAttributeGuids.Text.Token));
Assert.That(s_Token, Is.Not.Empty);
Assert.That(json["result"].GetValue<string>(), Is.EqualTo("success"));
// check to ensure the Build UI Response Method tells us to redirect to a destination URL
Assert.That(json["type"].GetValue<string>(), Is.EqualTo("redirect"));
// check to ensure the Build UI Response Method receives the value assigned to `Referral URL` earlier
Assert.That(json["destinationUrl"].GetValue<string>(), Is.EqualTo("https://example.com/referer"));
}

View File

@ -0,0 +1 @@
global using NUnit.Framework;

View File

@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.6.0" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
<PackageReference Include="NUnit.Analyzers" Version="3.6.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../../../framework-dotnet/framework-dotnet/src/lib/MBS.Core/MBS.Core.csproj" />
<ProjectReference Include="../../src/lib/Mocha.Core/Mocha.Core.csproj" />
<ProjectReference Include="../../src/lib/Mocha.Modeling/Mocha.Modeling.csproj" />
<ProjectReference Include="../../src/lib/Mocha.Testing/Mocha.Testing.csproj" />
<ProjectReference Include="../../src/plugins/Mocha.Plugins.Libraries.McxMini/Mocha.Plugins.Libraries.McxMini.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,76 @@
// Copyright (C) 2024 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using Mocha.Modeling;
using Mocha.Testing;
namespace Mocha.Modeling.Tests;
public abstract class OmsModelingTestsBase2<TDatabase> : OmsTestsBase where TDatabase : IOmsDatabase
{
private TDatabase? _db;
protected virtual TDatabase GetDatabase()
{
if (_db == null)
{
_db = CreateDatabase();
}
return _db;
}
private TDatabase CreateDatabase()
{
OmsObjectFactory<TDatabase> factory = new OmsObjectFactory<TDatabase>();
return factory.CreateDatabase(Oms);
}
protected override void AfterSetup()
{
base.AfterSetup();
TDatabase db = GetDatabase();
db.Initialize(Oms);
}
}
public abstract class OmsModelingTestsBase<TDatabase> : 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);
}
}

View File

@ -0,0 +1,121 @@
// Copyright (C) 2025 Michael Becker <alcexhim@gmail.com>
//
// This file is part of Mocha.NET.
//
// Mocha.NET is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Mocha.NET is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
using 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; }
/// <summary>
/// Business.has Driver
/// </summary>
/// <value></value>
[OmsRelationshipType("has"), OmsGlobalIdentifier("{63e5ab3a-4009-4af9-8171-5b23472c1c8c}")]
IList<Driver> Drivers { get; }
/// <summary>
/// Business.has Vehicle
/// </summary>
/// <value></value>
[OmsRelationshipType("has"), OmsGlobalIdentifier("{c54125f6-c3b0-40b2-99f9-e0ed20a75bfa}")]
IList<Vehicle> 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<VehiclePermit> 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<Business> Businesses { get; }
}
public class VehicleForHireInterfaceTests : OmsModelingTestsBase2<IVehicleForHireDB>
{
[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<IVehicleForHireDB.Business>();
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));
}
}

View File

@ -16,8 +16,8 @@
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
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<Vehicle> Vehicles;
[OmsRelationshipType("has"), OmsGlobalIdentifier("{9d4946c3-db8d-4f27-b21b-52e9b0fdf231}")]
public BusinessPermit Permit;
public OmsRelationship<Business, BusinessPermit> Permit;
}
[OmsName("Business Permit"), OmsGlobalIdentifier("{7680b302-c9c2-4275-abf8-373adafbf58e}")]
@ -87,10 +87,8 @@ public class VehicleForHireDB : OmsDatabase
public readonly IList<Business> Businesses;
}
public class VehicleForHireTests : OmsModelingTestsBase
public class VehicleForHireTests : OmsModelingTestsBase<VehicleForHireDB>
{
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);

View File

@ -0,0 +1,15 @@
namespace Mocha.Modeling.Tests;
public class Tests
{
[SetUp]
public void Setup()
{
}
[Test]
public void Test1()
{
Assert.Pass();
}
}

View File

@ -156,9 +156,18 @@ public class RemoteTests
Assert.That(json["text"].ToString(), Is.EqualTo(sz));
}
/// <summary>
/// Ensures that a POST request sent to an element properly processes the element with the given
/// element content parameter values.
/// </summary>
/// <returns></returns>
[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<string, string> coll = new Dictionary<string, string>();
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<string, string> coll = new Dictionary<string, string>();
// 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"));
}