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 // You should have received a copy of the GNU General Public License
// along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>. // along with Mocha.NET. If not, see <https://www.gnu.org/licenses/>.
namespace Mocha.Core; namespace Mocha.Core;
public struct AttributeValue 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)); IEnumerable<InstanceHandle> attributes = oms.GetRelatedInstances(_parentClassInstance, oms.GetInstance(KnownRelationshipGuids.Class__has__Attribute));
foreach (InstanceHandle att in attributes) 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()) if (br.BaseStream.EndOfStream())
{ {
break; break;
} }
int length = br.ReadInt32(); object? value = impl.ReadDerivedData(br);
string value = br.ReadString();
derivedData[att] = value; 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 CSSValue { get; } = new Guid("{C0DD4A42-F503-4EB3-8034-7C428B1B8803}");
public static Guid RelationshipType { get; } = new Guid("{71106B12-1934-4834-B0F6-D894637BAEED}"); 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 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 TargetURL { get; } = new Guid("{970F79A0-9EFE-4E7D-9286-9908C6F06A67}");
public static Guid ReferralURL { get; } = new Guid("{6daaa721-db70-43ad-b373-6a8038e69d2e}"); 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); // InstanceHandle forInstance = (InstanceHandle) context.GetWorkData(irForClass);
if (oms.IsInstanceOf(returnsAttribute, oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute))) AttributeImplementation impl = oms.GetAttributeImplementation(returnsAttribute);
{ object? value = impl.ExecuteBuildAttributeMethod(oms, context, method);
object? value = oms.UnsafeGetAttributeValue(method, oms.GetInstance(KnownAttributeGuids.Text.Value)); // initial value if (value != null)
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)
{ {
context.SetWorkData(returnsAttribute, value); context.SetWorkData(returnsAttribute, value);
} }
}
else
{
throw new NotImplementedException();
}
return returnsAttribute; return returnsAttribute;
} }

View File

@ -138,46 +138,17 @@ public class BuildElementMethodImplementation : MethodImplementation
{ {
objCell.Add("enabled", false); 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"); objCell.Add("helpText", helpText);
if (targetInstance == InstanceHandle.Empty)
{
objCell.Add("value", oms.GetAttributeValue<string>(ecInst, oms.GetInstance(KnownAttributeGuids.Text.Value)));
} }
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); if (oms.IsInstanceOf(ecInst, oms.GetInstance(KnownInstanceGuids.Classes.Attribute)))
JsonObject objDate = new JsonObject(); {
objDate.Add("Y", dt.Year.ToString()); AttributeImplementation impl = oms.GetAttributeImplementation(ecInst);
objDate.Add("M", dt.Month.ToString().PadLeft(2, '0')); impl.ExecuteBuildElement(oms, targetInstance, elementContent, ecInst, objCell);
objDate.Add("D", dt.Day.ToString().PadLeft(2, '0'));
objCell.Add("value", objDate);
objCell.Add("text", dt.ToString());
objCell.Add("dateTimePrecision", "DAY");
} }
else else
{ {

View File

@ -57,6 +57,12 @@ public abstract class Oms
{ {
RegisterMethodImplementation(impl.MethodClassGuid, impl); 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() protected virtual void InitializeInternal()
@ -1219,6 +1225,12 @@ public abstract class Oms
methodImplementations[methodClassId] = implementation; 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) public InstanceHandle Execute(OmsContext context, MethodBinding methodBinding, IInstanceReference? targetInstance = null)
{ {
InstanceHandle? targetInstanceHandle = null; InstanceHandle? targetInstanceHandle = null;
@ -2768,4 +2780,17 @@ public abstract class Oms
} }
return InstanceHandle.Empty; 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));
}
} }