move attribute conversion and handling code into separate AttributeImplementation class for ease of adding new attribute types

This commit is contained in:
Michael Becker 2025-10-21 08:43:07 -04:00
parent c97b8b0b1f
commit 6e99d815eb
12 changed files with 379 additions and 91 deletions

View File

@ -0,0 +1,55 @@
// 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.Text.Json.Nodes;
namespace Mocha.Core;
public abstract class AttributeImplementation<T> : AttributeImplementation
{
public T ConvertFrom(Oms oms, object? value, T defaultValue = default(T))
{
object? converted = ConvertFromInternal(oms, value);
if (converted is T)
{
return (T)converted;
}
return defaultValue;
}
}
public abstract class AttributeImplementation : ClassImplementation
{
protected abstract object? ConvertFromInternal(Oms oms, object? value);
public object? ReadDerivedData(BinaryReader br)
{
return ReadDerivedDataInternal(br);
}
protected abstract object? ReadDerivedDataInternal(BinaryReader br);
protected abstract object? ExecuteBuildAttributeMethodInternal(Oms oms, OmsContext context, InstanceHandle method);
public object? ExecuteBuildAttributeMethod(Oms oms, OmsContext context, InstanceHandle method)
{
return ExecuteBuildAttributeMethodInternal(oms, context, method);
}
protected abstract void ExecuteBuildElementInternal(Oms oms, InstanceHandle targetInstance, InstanceHandle elementContent, InstanceHandle elementContentInstance, JsonObject objCell);
public void ExecuteBuildElement(Oms oms, InstanceHandle targetInstance, InstanceHandle elementContent, InstanceHandle elementContentInstance, JsonObject objCell)
{
ExecuteBuildElementInternal(oms, targetInstance, elementContent, elementContentInstance, objCell);
}
}

View File

@ -0,0 +1,57 @@
// 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.Text.Json.Nodes;
namespace Mocha.Core.AttributeImplementations;
public class BooleanAttributeImplementation : AttributeImplementation<bool>
{
public override Guid ClassGuid => KnownInstanceGuids.Classes.BooleanAttribute;
protected override object? ConvertFromInternal(Oms oms, object? value)
{
if (value is string)
{
return Boolean.Parse((string)value);
}
return value;
}
protected override object? ReadDerivedDataInternal(BinaryReader br)
{
throw new NotImplementedException();
}
protected override object? ExecuteBuildAttributeMethodInternal(Oms oms, OmsContext context, InstanceHandle method)
{
object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Text.Value)); // initial value
if (value is string)
{
bool val = Boolean.Parse((string)value);
return val;
}
return null;
}
protected override void ExecuteBuildElementInternal(Oms oms, InstanceHandle targetInstance, InstanceHandle elementContent, InstanceHandle elementContentInstance, JsonObject objCell)
{
objCell.Add("widget", "checkBox");
objCell.Add("value", oms.GetAttributeValue<bool>(targetInstance, elementContentInstance));
objCell.Add("text", oms.GetAttributeValue<bool>(targetInstance, elementContentInstance) ? "Yes" : "No");
}
}

View File

@ -0,0 +1,64 @@
// 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.Text.Json.Nodes;
namespace Mocha.Core.AttributeImplementations;
public class DateAttributeImplementation : AttributeImplementation<DateTime>
{
public override Guid ClassGuid => KnownInstanceGuids.Classes.DateAttribute;
protected override object? ConvertFromInternal(Oms oms, object? value)
{
if (value is string)
{
return Boolean.Parse((string)value);
}
return value;
}
protected override object? ReadDerivedDataInternal(BinaryReader br)
{
throw new NotImplementedException();
}
protected override object? ExecuteBuildAttributeMethodInternal(Oms oms, OmsContext context, InstanceHandle method)
{
object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Text.Value)); // initial value
if (value is string)
{
DateTime val = DateTime.Parse((string)value);
return val;
}
return null;
}
protected override void ExecuteBuildElementInternal(Oms oms, InstanceHandle targetInstance, InstanceHandle elementContent, InstanceHandle elementContentInstance, JsonObject objCell)
{
objCell.Add("widget", "date");
DateTime dt = oms.GetAttributeValue<DateTime>(targetInstance, elementContentInstance);
JsonObject objDate = new JsonObject();
objDate.Add("Y", dt.Year.ToString());
objDate.Add("M", dt.Month.ToString().PadLeft(2, '0'));
objDate.Add("D", dt.Day.ToString().PadLeft(2, '0'));
objCell.Add("value", objDate);
objCell.Add("text", dt.ToString());
objCell.Add("dateTimePrecision", "DAY");
}
}

View File

@ -0,0 +1,58 @@
// 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.Text.Json.Nodes;
namespace Mocha.Core.AttributeImplementations;
public class NumericAttributeImplementation : AttributeImplementation<decimal>
{
public override Guid ClassGuid => KnownInstanceGuids.Classes.NumericAttribute;
protected override object? ConvertFromInternal(Oms oms, object? value)
{
if (value is string)
{
return Boolean.Parse((string)value);
}
return value;
}
protected override object? ReadDerivedDataInternal(BinaryReader br)
{
throw new NotImplementedException();
}
protected override object? ExecuteBuildAttributeMethodInternal(Oms oms, OmsContext context, InstanceHandle method)
{
object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Numeric.Value)); // initial value
if (value is decimal)
{
return value;
}
return null;
}
protected override void ExecuteBuildElementInternal(Oms oms, InstanceHandle targetInstance, InstanceHandle elementContent, InstanceHandle elementContentInstance, JsonObject objCell)
{
objCell.Add("widget", "number");
objCell.Add("value", oms.GetAttributeValue<decimal>(targetInstance, elementContentInstance));
objCell.Add("text", oms.GetAttributeValue<decimal>(targetInstance, elementContentInstance).ToString());
objCell.Add("precision", 6);
objCell.Add("format", "#0.######");
}
}

View File

@ -0,0 +1,80 @@
// 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.Text.Json.Nodes;
namespace Mocha.Core.AttributeImplementations;
public class TextAttributeImplementation : AttributeImplementation<string>
{
public override Guid ClassGuid => KnownInstanceGuids.Classes.TextAttribute;
protected override object? ConvertFromInternal(Oms oms, object? value)
{
return (string)value; //! ???
}
protected override object? ReadDerivedDataInternal(BinaryReader br)
{
int length = br.ReadInt32();
string value = br.ReadString();
return value;
}
protected override object? ExecuteBuildAttributeMethodInternal(Oms oms, OmsContext context, InstanceHandle method)
{
object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Text.Value)); // initial value
if (value is string)
{
IEnumerable<InstanceHandle> buildsWithRambs = oms.GetRelatedInstances(method, oms.GetInstance(KnownRelationshipGuids.Build_Attribute_Method__builds_with__Build_Attribute_Method_Component));
foreach (InstanceHandle ihComponent in buildsWithRambs)
{
InstanceHandle ihRamb = oms.GetRelatedInstance(ihComponent, oms.GetInstance(KnownRelationshipGuids.Build_Attribute_Method_Component__uses__Executable_returning_Attribute));
object? val = null;
if (oms.IsInstanceOf(ihRamb, oms.GetInstance(KnownInstanceGuids.Classes.Attribute)))
{
val = null;
}
else if (oms.IsInstanceOf(ihRamb, oms.GetInstance(KnownInstanceGuids.Classes.Executable)))
{
InstanceHandle wd = oms.Execute(context, ihRamb);
val = context.GetWorkData(wd);
}
if (val is string)
{
value = ((string)value) + (string)val;
}
}
return value;
}
return null;
}
protected override void ExecuteBuildElementInternal(Oms oms, InstanceHandle targetInstance, InstanceHandle elementContent, InstanceHandle elementContentInstance, JsonObject objCell)
{
objCell.Add("widget", "text");
if (targetInstance == InstanceHandle.Empty)
{
objCell.Add("value", oms.GetAttributeValue<string>(elementContentInstance, oms.GetInstance(KnownAttributeGuids.Text.Value)));
}
else
{
objCell.Add("value", oms.GetAttributeValue<string>(targetInstance, elementContentInstance));
}
}
}

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;
namespace Mocha.Core;
public struct AttributeValue
{

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.Core;
public abstract class ClassImplementation
{
public abstract Guid ClassGuid { get; }
}

View File

@ -203,15 +203,15 @@ namespace Mocha.Core
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)))
AttributeImplementation? impl = oms.GetAttributeImplementation(att);
if (impl != null)
{
if (br.BaseStream.EndOfStream())
{
break;
}
int length = br.ReadInt32();
string value = br.ReadString();
object? value = impl.ReadDerivedData(br);
derivedData[att] = value;
}
}

View File

@ -28,6 +28,7 @@ namespace Mocha.Core
public static Guid CSSValue { get; } = new Guid("{C0DD4A42-F503-4EB3-8034-7C428B1B8803}");
public static Guid RelationshipType { get; } = new Guid("{71106B12-1934-4834-B0F6-D894637BAEED}");
public static Guid Order { get; } = new Guid("{49423f66-8837-430d-8cac-7892ebdcb1fe}");
public static Guid HelpText { get; } = new Guid("{edfe6493-0adf-4d49-9f19-babbe9a99acf}");
public static Guid TargetURL { get; } = new Guid("{970F79A0-9EFE-4E7D-9286-9908C6F06A67}");
public static Guid ReferralURL { get; } = new Guid("{6daaa721-db70-43ad-b373-6a8038e69d2e}");

View File

@ -32,59 +32,12 @@ public class BuildAttributeMethodImplementation : MethodImplementation
// InstanceHandle forInstance = (InstanceHandle) context.GetWorkData(irForClass);
if (oms.IsInstanceOf(returnsAttribute, oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute)))
{
object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Text.Value)); // initial value
if (value is string)
{
IEnumerable<InstanceHandle> buildsWithRambs = oms.GetRelatedInstances(method, oms.GetInstance(KnownRelationshipGuids.Build_Attribute_Method__builds_with__Build_Attribute_Method_Component));
foreach (InstanceHandle ihComponent in buildsWithRambs)
{
InstanceHandle ihRamb = oms.GetRelatedInstance(ihComponent, oms.GetInstance(KnownRelationshipGuids.Build_Attribute_Method_Component__uses__Executable_returning_Attribute));
object? val = null;
if (oms.IsInstanceOf(ihRamb, oms.GetInstance(KnownInstanceGuids.Classes.Attribute)))
{
val = null;
}
else if (oms.IsInstanceOf(ihRamb, oms.GetInstance(KnownInstanceGuids.Classes.Executable)))
{
InstanceHandle wd = oms.Execute(context, ihRamb);
val = context.GetWorkData(wd);
}
if (val is string)
{
value = ((string)value) + (string)val;
}
}
}
context.SetWorkData(returnsAttribute, value);
}
else if (oms.IsInstanceOf(returnsAttribute, oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute)))
{
object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Text.Value)); // initial value
if (value is string)
{
bool val = Boolean.Parse((string)value);
context.SetWorkData(returnsAttribute, val);
}
}
else if (oms.IsInstanceOf(returnsAttribute, oms.GetInstance(KnownInstanceGuids.Classes.NumericAttribute)))
{
object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Numeric.Value)); // initial value
if (value is decimal)
AttributeImplementation impl = oms.GetAttributeImplementation(returnsAttribute);
object? value = impl.ExecuteBuildAttributeMethod(oms, context, method);
if (value != null)
{
context.SetWorkData(returnsAttribute, value);
}
}
else
{
throw new NotImplementedException();
}
return returnsAttribute;
}

View File

@ -138,46 +138,17 @@ public class BuildElementMethodImplementation : MethodImplementation
{
objCell.Add("enabled", false);
}
// objCell.Add("helpText", "blah blah");
if (oms.IsInstanceOf(ecInst, oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute)))
string? helpText = oms.GetAttributeValue<string>(elementContent, oms.GetInstance(KnownAttributeGuids.Text.HelpText));
if (helpText != null)
{
objCell.Add("widget", "text");
if (targetInstance == InstanceHandle.Empty)
{
objCell.Add("value", oms.GetAttributeValue<string>(ecInst, oms.GetInstance(KnownAttributeGuids.Text.Value)));
objCell.Add("helpText", helpText);
}
else
{
objCell.Add("value", oms.GetAttributeValue<string>(targetInstance, ecInst));
}
}
else if (oms.IsInstanceOf(ecInst, oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute)))
{
objCell.Add("widget", "checkBox");
objCell.Add("value", oms.GetAttributeValue<bool>(targetInstance, ecInst));
objCell.Add("text", oms.GetAttributeValue<bool>(targetInstance, ecInst) ? "Yes" : "No");
}
else if (oms.IsInstanceOf(ecInst, oms.GetInstance(KnownInstanceGuids.Classes.NumericAttribute)))
{
objCell.Add("widget", "number");
objCell.Add("value", oms.GetAttributeValue<decimal>(targetInstance, ecInst));
objCell.Add("text", oms.GetAttributeValue<decimal>(targetInstance, ecInst).ToString());
objCell.Add("precision", 6);
objCell.Add("format", "#0.######");
}
else if (oms.IsInstanceOf(ecInst, oms.GetInstance(KnownInstanceGuids.Classes.DateAttribute)))
{
objCell.Add("widget", "date");
DateTime dt = oms.GetAttributeValue<DateTime>(targetInstance, ecInst);
JsonObject objDate = new JsonObject();
objDate.Add("Y", dt.Year.ToString());
objDate.Add("M", dt.Month.ToString().PadLeft(2, '0'));
objDate.Add("D", dt.Day.ToString().PadLeft(2, '0'));
objCell.Add("value", objDate);
objCell.Add("text", dt.ToString());
objCell.Add("dateTimePrecision", "DAY");
if (oms.IsInstanceOf(ecInst, oms.GetInstance(KnownInstanceGuids.Classes.Attribute)))
{
AttributeImplementation impl = oms.GetAttributeImplementation(ecInst);
impl.ExecuteBuildElement(oms, targetInstance, elementContent, ecInst, objCell);
}
else
{

View File

@ -57,6 +57,12 @@ public abstract class Oms
{
RegisterMethodImplementation(impl.MethodClassGuid, impl);
}
AttributeImplementation[] attributeImplementations = MBS.Core.Reflection.TypeLoader.GetAvailableTypes<AttributeImplementation>(new System.Reflection.Assembly[] { Assembly.GetExecutingAssembly() });
foreach (AttributeImplementation impl in attributeImplementations)
{
RegisterAttributeImplementation(impl.ClassGuid, impl);
}
}
protected virtual void InitializeInternal()
@ -1219,6 +1225,12 @@ public abstract class Oms
methodImplementations[methodClassId] = implementation;
}
private Dictionary<Guid, AttributeImplementation> attributeImplementations = new Dictionary<Guid, AttributeImplementation>();
private void RegisterAttributeImplementation(Guid attributeClassId, AttributeImplementation implementation)
{
attributeImplementations[attributeClassId] = implementation;
}
public InstanceHandle Execute(OmsContext context, MethodBinding methodBinding, IInstanceReference? targetInstance = null)
{
InstanceHandle? targetInstanceHandle = null;
@ -2768,4 +2780,17 @@ public abstract class Oms
}
return InstanceHandle.Empty;
}
public AttributeImplementation GetAttributeImplementation(InstanceHandle att)
{
InstanceHandle parentClass = GetParentClass(att);
Guid classGuid = GetGlobalIdentifier(parentClass);
// find AttributeImplementation whose ClassGuid matches
if (attributeImplementations.ContainsKey(classGuid))
{
return attributeImplementations[classGuid];
}
throw new NotImplementedException(String.Format("implementation for attribute class {0} not found", classGuid));
}
}