preliminary implementation of derived instances
This commit is contained in:
parent
3a3e681a90
commit
784e04fd58
@ -1 +1 @@
|
||||
Subproject commit 54d5ab8b2c37bb9139eb6800631eece9b60ee25a
|
||||
Subproject commit cdb956eab2cc70bebb94803486e96230e83c465e
|
||||
@ -19,6 +19,8 @@
|
||||
// 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 MBS.Core;
|
||||
|
||||
namespace Mocha.Core
|
||||
{
|
||||
public struct InstanceKey
|
||||
@ -29,7 +31,25 @@ namespace Mocha.Core
|
||||
private bool _isNotEmpty;
|
||||
public bool IsEmpty { get { return !_isNotEmpty; } }
|
||||
|
||||
public bool IsDerived { get; }
|
||||
public bool IsDerived { get { return _derivedDataString != null; } }
|
||||
|
||||
internal Oms? _oms;
|
||||
internal InstanceHandle _inst;
|
||||
internal InstanceHandle _parentClassInstance;
|
||||
private string? _derivedDataString = null;
|
||||
internal void SetDerivedData(Oms oms, InstanceHandle inst, Dictionary<InstanceHandle, object?> derivedData)
|
||||
{
|
||||
_oms = oms;
|
||||
_inst = inst;
|
||||
_parentClassInstance = oms.GetParentClass(inst);
|
||||
|
||||
Dictionary<InstanceHandle, object?> _derivedData = new Dictionary<InstanceHandle, object?>();
|
||||
foreach (KeyValuePair<InstanceHandle, object?> kvp in derivedData)
|
||||
{
|
||||
_derivedData[kvp.Key] = kvp.Value;
|
||||
}
|
||||
_derivedDataString = GetDerivedDataString(_derivedData);
|
||||
}
|
||||
|
||||
public static readonly InstanceKey Empty = new InstanceKey();
|
||||
|
||||
@ -58,7 +78,6 @@ namespace Mocha.Core
|
||||
{
|
||||
ClassIndex = 0;
|
||||
InstanceIndex = 0;
|
||||
IsDerived = false;
|
||||
_isNotEmpty = false;
|
||||
}
|
||||
else
|
||||
@ -67,32 +86,50 @@ namespace Mocha.Core
|
||||
if (instanceKey.Contains("$"))
|
||||
{
|
||||
splitChar = '$';
|
||||
IsDerived = false;
|
||||
}
|
||||
else if (instanceKey.Contains("!"))
|
||||
{
|
||||
splitChar = '!';
|
||||
IsDerived = true;
|
||||
}
|
||||
|
||||
if (splitChar != '\0')
|
||||
{
|
||||
string[] split = instanceKey.Split(new char[] { '$' });
|
||||
string[] split = instanceKey.Split(new char[] { splitChar });
|
||||
if (split.Length == 2)
|
||||
{
|
||||
if (Int32.TryParse(split[0], out int ci) && Int32.TryParse(split[1], out int ii))
|
||||
if (splitChar == '!')
|
||||
{
|
||||
ClassIndex = ci;
|
||||
InstanceIndex = ii;
|
||||
_isNotEmpty = true;
|
||||
if (Int32.TryParse(split[0], out int ci))
|
||||
{
|
||||
ClassIndex = ci;
|
||||
InstanceIndex = -1;
|
||||
SetDerivedDataString(split[1]);
|
||||
_isNotEmpty = true;
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Int32.TryParse(split[0], out int ci) && Int32.TryParse(split[1], out int ii))
|
||||
{
|
||||
ClassIndex = ci;
|
||||
InstanceIndex = ii;
|
||||
_isNotEmpty = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new ArgumentException("must be a string containing two integers separated by a '$' or a '!'");
|
||||
}
|
||||
public InstanceKey(int classIndex, int instanceIndex)
|
||||
|
||||
private void SetDerivedDataString(string base64String)
|
||||
{
|
||||
_derivedDataString = base64String;
|
||||
}
|
||||
|
||||
public InstanceKey(int classIndex, int instanceIndex)
|
||||
{
|
||||
ClassIndex = classIndex;
|
||||
InstanceIndex = instanceIndex;
|
||||
@ -116,9 +153,70 @@ namespace Mocha.Core
|
||||
{
|
||||
if (IsDerived)
|
||||
{
|
||||
return String.Format("{0}!{1}", ClassIndex, InstanceIndex);
|
||||
return String.Format("{0}!{1}", ClassIndex, _derivedDataString);
|
||||
}
|
||||
return String.Format("{0}${1}", ClassIndex, InstanceIndex);
|
||||
}
|
||||
}
|
||||
|
||||
internal string GetDerivedDataString(Dictionary<InstanceHandle, object?>? derivedData = null)
|
||||
{
|
||||
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)
|
||||
{
|
||||
string value = (string)kvp.Value;
|
||||
bw.Write((int)value.Length);
|
||||
bw.Write((string)value);
|
||||
}
|
||||
}
|
||||
|
||||
bw.Flush();
|
||||
bw.Close();
|
||||
byte[] data = ms.ToArray();
|
||||
|
||||
string b64 = Convert.ToBase64String(data);
|
||||
return b64;
|
||||
}
|
||||
|
||||
internal Dictionary<InstanceHandle, object?>? GetDerivedData()
|
||||
{
|
||||
Dictionary<InstanceHandle, object?> derivedData = new Dictionary<InstanceHandle, object?>();
|
||||
|
||||
InstanceKey ikParentClass = new InstanceKey(1, ClassIndex);
|
||||
_parentClassInstance = _oms.GetInstance(ikParentClass);
|
||||
|
||||
if (_derivedDataString != null)
|
||||
{
|
||||
byte[] data = Convert.FromBase64String(_derivedDataString);
|
||||
MemoryStream ms = new MemoryStream(data);
|
||||
BinaryReader br = new BinaryReader(ms);
|
||||
|
||||
IEnumerable<InstanceHandle> attributes = _oms.GetRelatedInstances(_parentClassInstance, _oms.GetInstance(KnownRelationshipGuids.Class__has__Attribute));
|
||||
foreach (InstanceHandle att in attributes)
|
||||
{
|
||||
if (_oms.IsInstanceOf(att, _oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute)))
|
||||
{
|
||||
if (br.BaseStream.EndOfStream())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
int length = br.ReadInt32();
|
||||
string value = br.ReadString();
|
||||
derivedData[att] = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return derivedData;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,6 +60,7 @@ namespace Mocha.Core
|
||||
public static Guid UseAnyCondition { get; } = new Guid("{31a8a2c2-1f55-4dfe-b177-427a2219ef8c}");
|
||||
public static Guid ValidateOnlyOnSubmit { get; } = new Guid("{400fcd8e-823b-4f4a-aa38-b444f763259b}");
|
||||
public static Guid UserIsLoggedIn { get; } = new Guid("{8e93d9f3-a897-4c97-935c-b3427f90633b}");
|
||||
public static Guid Derived { get; } = new Guid("{66991ca1-ef08-4f30-846c-4984c2a3139d}");
|
||||
}
|
||||
public static class Numeric
|
||||
{
|
||||
|
||||
@ -301,6 +301,15 @@ public abstract class Oms
|
||||
{
|
||||
return new InstanceKey(1, 1);
|
||||
}
|
||||
if (IsDerivedInstance(instance))
|
||||
{
|
||||
InstanceHandle parent = GetParentClass(instance);
|
||||
InstanceKey ikParent = GetInstanceKeyInternal(parent);
|
||||
|
||||
InstanceKey ik = new InstanceKey(ikParent.InstanceIndex, -1);
|
||||
ik.SetDerivedData(this, instance, GetDerivedData(instance));
|
||||
return ik;
|
||||
}
|
||||
return GetInstanceKeyInternal(instance);
|
||||
}
|
||||
|
||||
@ -388,8 +397,42 @@ public abstract class Oms
|
||||
}
|
||||
throw new KeyNotFoundException(String.Format("inst not found: {0}", globalIdentifier));
|
||||
}
|
||||
|
||||
private Dictionary<string, InstanceHandle> _derivedInstances = new Dictionary<string, InstanceHandle>();
|
||||
public InstanceHandle GetInstance(InstanceKey ik)
|
||||
{
|
||||
if (ik.IsDerived)
|
||||
{
|
||||
ik._oms = this;
|
||||
InstanceHandle inst;
|
||||
if (!_derivedInstances.ContainsKey(ik.ToString()))
|
||||
{
|
||||
InstanceHandle pclass = GetInstance(new InstanceKey(1, ik.ClassIndex));
|
||||
|
||||
inst = InstanceHandle.Create();
|
||||
_derivedInstances[ik.ToString()] = inst;
|
||||
|
||||
SetParentClass(inst, pclass);
|
||||
ik._inst = inst;
|
||||
ik._parentClassInstance = pclass;
|
||||
|
||||
_derivedData[inst] = new Dictionary<InstanceHandle, object?>();
|
||||
IEnumerable<KeyValuePair<InstanceHandle, object?>>? kvps = ik.GetDerivedData();
|
||||
if (kvps != null)
|
||||
{
|
||||
foreach (KeyValuePair<InstanceHandle, object?> kvp in kvps)
|
||||
{
|
||||
_derivedData[inst][kvp.Key] = kvp.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
inst = _derivedInstances[ik.ToString()];
|
||||
}
|
||||
return inst;
|
||||
}
|
||||
|
||||
if (TryGetInstance(ik, out InstanceHandle ih))
|
||||
{
|
||||
return ih;
|
||||
@ -584,8 +627,43 @@ public abstract class Oms
|
||||
value = default(T);
|
||||
return false;
|
||||
}
|
||||
public T GetAttributeValue<T>(InstanceHandle source, InstanceHandle attribute, T defaultValue = default(T), DateTime? effectiveDate = null)
|
||||
|
||||
private Dictionary<InstanceHandle, Dictionary<InstanceHandle, object?>?> _derivedData = new Dictionary<InstanceHandle, Dictionary<InstanceHandle, object?>?>();
|
||||
public Dictionary<InstanceHandle, object?>? GetDerivedData(InstanceHandle source)
|
||||
{
|
||||
if (IsDerivedInstance(source))
|
||||
{
|
||||
if (!_derivedData.ContainsKey(source))
|
||||
{
|
||||
_derivedData[source] = new Dictionary<InstanceHandle, object?>();
|
||||
}
|
||||
return _derivedData[source];
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public bool IsDerivedInstance(InstanceHandle 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)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public T GetAttributeValue<T>(InstanceHandle source, InstanceHandle attribute, T defaultValue = default(T), DateTime? effectiveDate = null)
|
||||
{
|
||||
Dictionary<InstanceHandle, object>? derivedData = GetDerivedData(source);
|
||||
if (derivedData != null)
|
||||
{
|
||||
if (derivedData.ContainsKey(attribute))
|
||||
{
|
||||
return (T)derivedData[attribute];
|
||||
}
|
||||
}
|
||||
if (TryGetAttributeValue(source, attribute, effectiveDate, out T value))
|
||||
{
|
||||
return value;
|
||||
@ -596,6 +674,16 @@ public abstract class Oms
|
||||
public void SetAttributeValue(InstanceHandle source, InstanceHandle attribute, object value, DateTime? effectiveDate = null)
|
||||
{
|
||||
ValidateConstraintsForAttribute(source, attribute, value);
|
||||
|
||||
if (IsDerivedInstance(source))
|
||||
{
|
||||
if (!_derivedData.ContainsKey(source))
|
||||
{
|
||||
_derivedData[source] = new Dictionary<InstanceHandle, object>();
|
||||
}
|
||||
_derivedData[source][attribute] = value;
|
||||
return;
|
||||
}
|
||||
SetAttributeValueInternal(source, attribute, value, effectiveDate.GetValueOrDefault(DateTime.Now));
|
||||
}
|
||||
|
||||
|
||||
54
mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs
Normal file
54
mocha-dotnet/tests/Mocha.Core.Tests/DerivedInstanceTests.cs
Normal file
@ -0,0 +1,54 @@
|
||||
using System;
|
||||
using Newtonsoft.Json.Serialization;
|
||||
|
||||
namespace Mocha.Core.Tests;
|
||||
|
||||
public class DerivedInstanceTests : OmsTestsBase
|
||||
{
|
||||
InstanceHandle TEST_CLASS_DERIVED;
|
||||
protected override void AfterSetup()
|
||||
{
|
||||
base.AfterSetup();
|
||||
|
||||
TEST_CLASS_DERIVED = Oms.CreateClass("Test Class (Derived)");
|
||||
Oms.SetAttributeValue(TEST_CLASS_DERIVED, Oms.GetInstance(KnownAttributeGuids.Boolean.Derived), true);
|
||||
|
||||
// Derived classes are represented only by InstanceKeys with the form {ClassId}!{DerivedData}, where
|
||||
// {DerivedData} is a Base64 string encoding all the attributes and relationships associated with the
|
||||
// derived instance.
|
||||
|
||||
// ? Should we be concerned with memory leaks given the lazy implementation of GetInstance on a derived
|
||||
// ? InstanceKey essentially allocates a new InstanceHandle each time it's called ???
|
||||
Oms.AddAttribute(TEST_CLASS_DERIVED, Oms.GetInstance(KnownAttributeGuids.Text.Name));
|
||||
}
|
||||
|
||||
const string TEST_DERIVED_VALUE = "Test Class Derived #1";
|
||||
|
||||
[Test]
|
||||
public void Derived_Instance_Test_get_Text_Attribute_Value()
|
||||
{
|
||||
InstanceHandle iTestClassDerived = Oms.CreateInstanceOf(TEST_CLASS_DERIVED);
|
||||
Oms.SetAttributeValue(iTestClassDerived, Oms.GetInstance(KnownAttributeGuids.Text.Name), TEST_DERIVED_VALUE);
|
||||
|
||||
InstanceKey ik = Oms.GetInstanceKey(iTestClassDerived);
|
||||
|
||||
InstanceHandle h2 = Oms.GetInstance(ik);
|
||||
Dictionary<InstanceHandle, object?>? dict = Oms.GetDerivedData(h2);
|
||||
|
||||
string attVName = Oms.GetAttributeValue<string>(h2, Oms.GetInstance(KnownAttributeGuids.Text.Name));
|
||||
Assert.That(attVName, Is.EqualTo(TEST_DERIVED_VALUE));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Derived_Instance_Test__instance_key_Parse()
|
||||
{
|
||||
string ikStr = "154!FQAAABVUZXN0IENsYXNzIERlcml2ZWQgIzE=";
|
||||
InstanceKey ik = InstanceKey.Parse(ikStr);
|
||||
|
||||
InstanceHandle ih = Oms.GetInstance(ik);
|
||||
|
||||
string attVName = Oms.GetAttributeValue<string>(ih, Oms.GetInstance(KnownAttributeGuids.Text.Name));
|
||||
Assert.That(attVName, Is.EqualTo(TEST_DERIVED_VALUE));
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user