diff --git a/build b/build
index d104199..2e5a5d1 100755
--- a/build
+++ b/build
@@ -16,4 +16,16 @@ THEME_NAME=avondale
for each in mocha-dotnet/src/assets/ui-html/css/common/*.less mocha-dotnet/src/assets/ui-html/css/$THEME_NAME/*.less; do cat $each; echo ""; done | lessc --plugin=less-plugin-clean-css=advanced - output/assets/ui-html/$ASSET_UIHTML_VERSION/css/mochaApp.css
cp mocha-dotnet/src/app/Mocha.ServerApplication/bin/Debug/net8.0/* output/
+if [ ! -d output/plugins ]; then
+ mkdir output/plugins
+fi
+
+for each in mocha-dotnet/src/plugins/*; do
+ pushd $each
+ dotnet build
+ popd
+
+ cp $each/bin/Debug/net8.0/*.dll output/plugins
+done
+
exit 0
diff --git a/mocha-dotnet.sln b/mocha-dotnet.sln
index 853d6f6..5337d46 100644
--- a/mocha-dotnet.sln
+++ b/mocha-dotnet.sln
@@ -37,6 +37,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{27C300F5
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Core.Tests", "mocha-dotnet\tests\Mocha.Core.Tests\Mocha.Core.Tests.csproj", "{20B7D199-322C-4942-85FE-46B90C75E92A}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "plugins", "plugins", "{B024FD23-7084-4DDF-A185-D58BEE7A006F}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Mocha.Plugins.Libraries.Yaml", "mocha-dotnet\src\plugins\Mocha.Plugins.Libraries.Yaml\Mocha.Plugins.Libraries.Yaml.csproj", "{96400F5A-98ED-48FF-8000-6135BDF10360}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -63,6 +67,10 @@ Global
{20B7D199-322C-4942-85FE-46B90C75E92A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{20B7D199-322C-4942-85FE-46B90C75E92A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{20B7D199-322C-4942-85FE-46B90C75E92A}.Release|Any CPU.Build.0 = Release|Any CPU
+ {96400F5A-98ED-48FF-8000-6135BDF10360}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {96400F5A-98ED-48FF-8000-6135BDF10360}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {96400F5A-98ED-48FF-8000-6135BDF10360}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {96400F5A-98ED-48FF-8000-6135BDF10360}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -82,6 +90,8 @@ Global
{1345068A-1AF8-4FAA-A336-457E73ED23A7} = {D67F792A-95F6-44EC-80A8-C17A0D0B0644}
{27C300F5-5172-4225-A6F7-3503B9007DD8} = {2A2B84F1-FA6B-4D67-ACB8-E277D50C98F2}
{20B7D199-322C-4942-85FE-46B90C75E92A} = {27C300F5-5172-4225-A6F7-3503B9007DD8}
+ {B024FD23-7084-4DDF-A185-D58BEE7A006F} = {66EB3261-A473-41C7-8D40-E1B4DC8ED4B3}
+ {96400F5A-98ED-48FF-8000-6135BDF10360} = {B024FD23-7084-4DDF-A185-D58BEE7A006F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D28A9CF8-0235-4F8F-865F-C460BDCAE16D}
diff --git a/mocha-dotnet/src/lib/Mocha.Core/Library.cs b/mocha-dotnet/src/lib/Mocha.Core/Library.cs
new file mode 100644
index 0000000..eb186dd
--- /dev/null
+++ b/mocha-dotnet/src/lib/Mocha.Core/Library.cs
@@ -0,0 +1,13 @@
+
+namespace Mocha.Core;
+
+public class Library
+{
+ ///
+ /// Applies all attribute and relationship assignments once all instances have been
+ /// accounted for.
+ ///
+ public void Link()
+ {
+ }
+}
diff --git a/mocha-dotnet/src/lib/Mocha.Core/LibraryHandle.cs b/mocha-dotnet/src/lib/Mocha.Core/LibraryHandle.cs
new file mode 100644
index 0000000..627f22f
--- /dev/null
+++ b/mocha-dotnet/src/lib/Mocha.Core/LibraryHandle.cs
@@ -0,0 +1,107 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of Mocha.NET.
+//
+// Mocha.NET is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Mocha.NET is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Mocha.NET. If not, see .
+
+//
+// InstanceReference.cs
+//
+// Author:
+// Michael Becker
+//
+// Copyright (c) 2022 Mike Becker's Software
+//
+// This program is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program. If not, see .
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Reflection.Metadata;
+using MBS.Core;
+
+namespace Mocha.Core
+{
+ ///
+ /// Represents an opaque handle to an instance in a Mocha object management system (OMS).
+ ///
+ public struct LibraryHandle : IEquatable
+ {
+ private NanoId _ID;
+ private bool isNotEmpty;
+
+ public static LibraryHandle Create()
+ {
+ LibraryHandle handle = new LibraryHandle();
+ handle._ID = NanoId.Generate();
+ handle.isNotEmpty = true;
+ return handle;
+ }
+
+ public byte[] ToByteArray()
+ {
+ return System.Text.Encoding.ASCII.GetBytes(_ID.ToString());
+ }
+
+ public static readonly LibraryHandle Empty = new LibraryHandle();
+
+ public bool IsEmpty { get { return !isNotEmpty; } }
+
+ public bool Equals(LibraryHandle other)
+ {
+ if (IsEmpty && other.IsEmpty)
+ return true;
+
+ return _ID == other._ID && !IsEmpty;
+ }
+ public override bool Equals([NotNullWhen(true)] object? obj)
+ {
+ if (obj is LibraryHandle)
+ {
+ return this.Equals(((LibraryHandle)obj));
+ }
+ return base.Equals(obj);
+ }
+ public override int GetHashCode()
+ {
+ return _ID.GetHashCode();
+ }
+
+ public static bool operator ==(LibraryHandle left, LibraryHandle right)
+ {
+ return left.Equals(right);
+ }
+ public static bool operator !=(LibraryHandle left, LibraryHandle right)
+ {
+ return !left.Equals(right);
+ }
+
+ public override string ToString()
+ {
+ if (IsEmpty)
+ return "(empty) ";
+
+ return _ID.ToString();
+ }
+ }
+}
diff --git a/mocha-dotnet/src/lib/Mocha.Core/LibraryPlugin.cs b/mocha-dotnet/src/lib/Mocha.Core/LibraryPlugin.cs
new file mode 100644
index 0000000..50be1c4
--- /dev/null
+++ b/mocha-dotnet/src/lib/Mocha.Core/LibraryPlugin.cs
@@ -0,0 +1,39 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of Mocha.NET.
+//
+// Mocha.NET is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Mocha.NET is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Mocha.NET. If not, see .
+
+using System.Reflection;
+using MBS.Core.Extensibility;
+
+namespace Mocha.Core;
+
+public abstract class LibraryPlugin : Plugin
+{
+ protected abstract void LoadInternal(string filename, Library library);
+ public void Load(string filename, Library library)
+ {
+ LoadInternal(filename, library);
+ }
+
+ protected virtual bool SupportsFileNameInternal(string filename)
+ {
+ return false;
+ }
+ public bool SupportsFileName(string filename)
+ {
+ return SupportsFileNameInternal(filename);
+ }
+}
\ No newline at end of file
diff --git a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs
index a71d95f..ce20bfc 100644
--- a/mocha-dotnet/src/lib/Mocha.Core/Oms.cs
+++ b/mocha-dotnet/src/lib/Mocha.Core/Oms.cs
@@ -27,6 +27,8 @@ using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text;
using MBS.Core;
+using MBS.Core.Extensibility;
+using MBS.Core.IO;
using Mocha.Core.MethodImplementations;
using Mocha.Core.Oop;
@@ -83,10 +85,17 @@ public abstract class Oms
SelectTenant(tenant);
InitializeTenant();
-
+
+ OnTenantCreated(new OmsTenantCreatedEventArgs(tenant, tenantName));
return tenant;
}
+ public EventHandler? TenantCreated;
+ protected virtual void OnTenantCreated(OmsTenantCreatedEventArgs e)
+ {
+ TenantCreated?.Invoke(this, e);
+ }
+
public TenantHandle GetTenantByName(string tenantName)
{
if (_tenantsByName.ContainsKey(tenantName))
@@ -1018,4 +1027,83 @@ public abstract class Oms
protected virtual void InitializeTenantInternal()
{
}
+
+ protected abstract void AddLibraryReferenceInternal(LibraryHandle library);
+ ///
+ /// Adds the specified library reference to the current tenant.
+ ///
+ ///
+ public void AddLibraryReference(LibraryHandle library)
+ {
+ AddLibraryReferenceInternal(library);
+ }
+
+ private Dictionary _libraries = new Dictionary();
+ ///
+ /// Gets the for the library with the given name.
+ ///
+ ///
+ ///
+ public LibraryHandle GetLibrary(string name)
+ {
+ if (_libraries.ContainsKey(name))
+ {
+ return _libraries[name];
+ }
+ return LibraryHandle.Empty;
+ }
+
+ ///
+ /// Loads the specified library file into memory. To add it to the tenant,
+ /// first the desired tenant, and then call
+ /// .
+ ///
+ ///
+ ///
+ public LibraryHandle LoadLibrary(string filename)
+ {
+ // some examples:
+ // .mcl compiled binary library file (fastest?)
+ // /path/to/directory containing a bunch of XML / JSON / YAML files
+ // .mcz ZIP archive of XML / JSON / YAML files (slowest?)
+ LibraryHandle lh = LibraryHandle.Create();
+ return lh;
+
+ Library lib = new Library();
+ // _libraries[name] = lh;
+
+ if (!File.Exists(filename) && !Directory.Exists(filename))
+ {
+ throw new FileNotFoundException(null, filename);
+ }
+
+ // get a list of all library loaders we have implemented
+ LibraryPlugin[] plugins = Plugin.Get();
+ foreach (LibraryPlugin plugin in plugins)
+ {
+ Console.WriteLine("found plugin: {0}", plugin.GetType().FullName);
+ if (plugin.SupportsFileName(filename))
+ {
+ // if passed a directory, will enumerate all the
+ // .xml / .json / .yaml files in the directory and
+ // subdirectories
+ plugin.Load(filename, lib);
+ }
+ }
+
+ // if the files have not already been compiled, we need to do two passes.
+ // - the first pass loads all instances into our tenant
+ // - the second pass applies the attributes and relationships, which depend on the
+ // presence of the instances
+
+ // compiling to .mcl binary library format does this all ahead of time and should
+ // be faster.
+ // let's be lazy and just look at the file extension to determine how to parse
+
+ // once all the instances have been loaded, tell the in-memory library manager
+ // to do its second pass "linking" the objects
+ lib.Link();
+
+ return lh;
+ }
}
diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsTenantCreatedEventArgs.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsTenantCreatedEventArgs.cs
new file mode 100644
index 0000000..4f2237b
--- /dev/null
+++ b/mocha-dotnet/src/lib/Mocha.Core/OmsTenantCreatedEventArgs.cs
@@ -0,0 +1,31 @@
+// Copyright (C) 2024 Michael Becker
+//
+// This file is part of Mocha.NET.
+//
+// Mocha.NET is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Mocha.NET is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Mocha.NET. If not, see .
+
+namespace Mocha.Core;
+
+public class OmsTenantCreatedEventArgs : EventArgs
+{
+ public TenantHandle Tenant { get; }
+ public string Name { get; }
+
+ public OmsTenantCreatedEventArgs(TenantHandle tenant, string name)
+ {
+ Tenant = tenant;
+ Name = name;
+ }
+
+}
\ No newline at end of file
diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/Mocha.Plugins.Libraries.Yaml.csproj b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/Mocha.Plugins.Libraries.Yaml.csproj
new file mode 100644
index 0000000..7ba799f
--- /dev/null
+++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/Mocha.Plugins.Libraries.Yaml.csproj
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+ net8.0
+ enable
+ enable
+
+
+
diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/YamlLibraryPlugin.cs b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/YamlLibraryPlugin.cs
new file mode 100644
index 0000000..31f0ffd
--- /dev/null
+++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/YamlLibraryPlugin.cs
@@ -0,0 +1,28 @@
+using Mocha.Core;
+
+namespace Mocha.Plugins.Libraries.Yaml;
+
+public class YamlLibraryPlugin : LibraryPlugin
+{
+ protected override bool SupportsFileNameInternal(string filename)
+ {
+ return Directory.Exists(filename) || Path.GetExtension(filename).Equals("yaml");
+ }
+
+ protected override void LoadInternal(string filename, Library library)
+ {
+ if (Directory.Exists(filename))
+ {
+ string[] files = Directory.GetFiles(filename, "*.yaml", SearchOption.AllDirectories);
+ foreach (string file in files)
+ {
+ FileStream fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read);
+ StreamReader sr = new StreamReader(fs);
+
+ var yaml = new YamlStream();
+ yaml.Load(sr);
+
+ }
+ }
+ }
+}
diff --git a/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/YamlParser.cs b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/YamlParser.cs
new file mode 100644
index 0000000..17f14d0
--- /dev/null
+++ b/mocha-dotnet/src/plugins/Mocha.Plugins.Libraries.Yaml/YamlParser.cs
@@ -0,0 +1,9 @@
+namespace Mocha.Plugins.Libraries.Yaml;
+
+public class YamlParser
+{
+ public Dictionary Parse(Stream stream)
+ {
+ StreamReader sr = new StreamReader(stream);
+ }
+}