preliminary implementation of inheritable method binding definitions and properly choosing the appropriate implementing method
This commit is contained in:
parent
2b4b8b8408
commit
79d86a933c
@ -50,6 +50,8 @@ namespace Mocha.Core
|
|||||||
}
|
}
|
||||||
public static class Boolean
|
public static class Boolean
|
||||||
{
|
{
|
||||||
|
// {989b5a95-d5bb-470c-b067-487316d22da2}
|
||||||
|
public static Guid Abstract { get; } = new Guid("{868d682b-f77b-4ed4-85a9-337f338635c1}");
|
||||||
public static Guid EvaluateWorkSet { get; } = new Guid("{62c28f9e-5ce8-4ce5-8a56-1e80f1af7f6a}");
|
public static Guid EvaluateWorkSet { get; } = new Guid("{62c28f9e-5ce8-4ce5-8a56-1e80f1af7f6a}");
|
||||||
public static Guid MethodIsOfTypeSpecified { get; } = new Guid("{6e9df667-0f95-4320-a4be-5cdb00f1d4ee}");
|
public static Guid MethodIsOfTypeSpecified { get; } = new Guid("{6e9df667-0f95-4320-a4be-5cdb00f1d4ee}");
|
||||||
public static Guid DisplayVersionInBadge { get; } = new Guid("{BE5966A4-C4CA-49A6-B504-B6E8759F392D}");
|
public static Guid DisplayVersionInBadge { get; } = new Guid("{BE5966A4-C4CA-49A6-B504-B6E8759F392D}");
|
||||||
|
|||||||
@ -114,6 +114,8 @@ namespace Mocha.Core
|
|||||||
public static Guid Method_Binding__executes__Method { get; } = new Guid("{B782A592-8AF5-4228-8296-E3D0B24C70A8}");
|
public static Guid Method_Binding__executes__Method { get; } = new Guid("{B782A592-8AF5-4228-8296-E3D0B24C70A8}");
|
||||||
|
|
||||||
public static Guid Method__has_return_type__Class { get; } = new Guid("{1241c599-e55d-4dcf-9200-d0e48c217ef8}");
|
public static Guid Method__has_return_type__Class { get; } = new Guid("{1241c599-e55d-4dcf-9200-d0e48c217ef8}");
|
||||||
|
public static Guid Method__implements__Method { get; } = new Guid("{83c992d7-03ec-483f-b6f1-225083f201e3}");
|
||||||
|
public static Guid Method__implemented_by__Method { get; } = new Guid("{f19e7779-a6b6-4914-a5cc-3c48fa1c9491}");
|
||||||
|
|
||||||
public static Guid Method_Binding__has__Parameter_Assignment { get; } = new Guid("{24938109-94f1-463a-9314-c49e667cf45b}");
|
public static Guid Method_Binding__has__Parameter_Assignment { get; } = new Guid("{24938109-94f1-463a-9314-c49e667cf45b}");
|
||||||
public static Guid Parameter_Assignment__for__Method_Binding { get; } = new Guid("{19c4a5db-fd26-44b8-b431-e081e6ffff8a}");
|
public static Guid Parameter_Assignment__for__Method_Binding { get; } = new Guid("{19c4a5db-fd26-44b8-b431-e081e6ffff8a}");
|
||||||
@ -481,5 +483,6 @@ namespace Mocha.Core
|
|||||||
public static Guid Invoke_Web_Service_Method__uses__Executable_returning_Element { get; } = new Guid("{020e26ba-565d-47a4-bdcc-f0050ff6b848}");
|
public static Guid Invoke_Web_Service_Method__uses__Executable_returning_Element { get; } = new Guid("{020e26ba-565d-47a4-bdcc-f0050ff6b848}");
|
||||||
public static Guid Invoke_Web_Service_Method__correlated_instances_from__Executable_returning_Instance_Set { get; } = new Guid("{8f1406b4-f48f-45fb-a6ef-2ad51998735f}");
|
public static Guid Invoke_Web_Service_Method__correlated_instances_from__Executable_returning_Instance_Set { get; } = new Guid("{8f1406b4-f48f-45fb-a6ef-2ad51998735f}");
|
||||||
public static Guid Invoke_Web_Service_Method__has_error__Executable_returning_Element { get; } = new Guid("{86aa5338-d913-4044-af8d-7aa4e1a9ddbc}");
|
public static Guid Invoke_Web_Service_Method__has_error__Executable_returning_Element { get; } = new Guid("{86aa5338-d913-4044-af8d-7aa4e1a9ddbc}");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1347,9 +1347,9 @@ public abstract class Oms
|
|||||||
{
|
{
|
||||||
if (ClassImplementations.TryGet<MethodImplementation>(parentClassId, out MethodImplementation? impl))
|
if (ClassImplementations.TryGet<MethodImplementation>(parentClassId, out MethodImplementation? impl))
|
||||||
{
|
{
|
||||||
|
InstanceHandle forClass = GetRelatedInstance(methodOrMethodBinding, GetInstance(KnownRelationshipGuids.Method__for__Class));
|
||||||
if (targetInstance == null)
|
if (targetInstance == null)
|
||||||
{
|
{
|
||||||
InstanceHandle forClass = GetRelatedInstance(methodOrMethodBinding, GetInstance(KnownRelationshipGuids.Method__for__Class));
|
|
||||||
if (forClass != InstanceHandle.Empty)
|
if (forClass != InstanceHandle.Empty)
|
||||||
{
|
{
|
||||||
IInstanceReference? irTarget = context.GetWorkData<IInstanceReference?>(forClass);
|
IInstanceReference? irTarget = context.GetWorkData<IInstanceReference?>(forClass);
|
||||||
@ -1366,6 +1366,29 @@ public abstract class Oms
|
|||||||
//sthrow new NullReferenceException(String.Format("Attempt to call instance method `{0}` without an instance", methodOrMethodBinding));
|
//sthrow new NullReferenceException(String.Format("Attempt to call instance method `{0}` without an instance", methodOrMethodBinding));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool is_abstract = GetAttributeValue<bool>(methodOrMethodBinding, GetInstance(KnownAttributeGuids.Boolean.Abstract));
|
||||||
|
if (is_abstract)
|
||||||
|
{
|
||||||
|
InstanceHandle forClassInstance = context.GetWorkData<InstanceHandle>(forClass);
|
||||||
|
InstanceHandle parentClass2 = GetParentClass(forClassInstance);
|
||||||
|
string parentClassName = GetAttributeValue<string>(parentClass2, GetInstance(KnownAttributeGuids.Text.Name));
|
||||||
|
|
||||||
|
IEnumerable<InstanceHandle> methodsImplementingMethod = GetRelatedInstances(methodOrMethodBinding, GetInstance(KnownRelationshipGuids.Method__implemented_by__Method));
|
||||||
|
foreach (InstanceHandle ih in methodsImplementingMethod)
|
||||||
|
{
|
||||||
|
InstanceHandle forClassInstance2 = GetRelatedInstance(ih, GetInstance(KnownRelationshipGuids.Method__for__Class));
|
||||||
|
if (parentClass2 == forClassInstance2)
|
||||||
|
{
|
||||||
|
// call implementing method instead
|
||||||
|
retval = Execute(context, ih, targetInstance);
|
||||||
|
if (retval != InstanceHandle.Empty)
|
||||||
|
{
|
||||||
|
return retval.GetValueOrDefault();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw new InvalidOperationException(String.Format("No method implementation for abstract method {0} on class {1} ({2})", GetInstanceKey(methodOrMethodBinding), parentClassName.Replace(' ', '_') + "--IS", GetInstanceKey(parentClass2)));
|
||||||
|
}
|
||||||
if (targetInstance == null)
|
if (targetInstance == null)
|
||||||
{
|
{
|
||||||
|
|
||||||
@ -2809,4 +2832,9 @@ public abstract class Oms
|
|||||||
// find AttributeImplementation whose ClassGuid matches
|
// find AttributeImplementation whose ClassGuid matches
|
||||||
return ClassImplementations.Get<AttributeImplementation>(classGuid);
|
return ClassImplementations.Get<AttributeImplementation>(classGuid);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public InstanceHandle GetInstance(object method__implements__Method)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -15,6 +15,8 @@
|
|||||||
// 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/>.
|
||||||
|
|
||||||
|
using Mocha.Core.Oop;
|
||||||
|
using Mocha.Core.Oop.Methods;
|
||||||
using Mocha.Testing;
|
using Mocha.Testing;
|
||||||
|
|
||||||
namespace Mocha.Core.Tests;
|
namespace Mocha.Core.Tests;
|
||||||
@ -51,4 +53,89 @@ public class InheritanceTests : OmsTestsBase
|
|||||||
InstanceHandle ihParentClass = Oms.GetParentClass(ihTextAttribute);
|
InstanceHandle ihParentClass = Oms.GetParentClass(ihTextAttribute);
|
||||||
Assert.That(ihParentClass, Is.EqualTo(ihClass));
|
Assert.That(ihParentClass, Is.EqualTo(ihClass));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Unimplemented_Abstract_Method_Call_throws_Exception()
|
||||||
|
{
|
||||||
|
// this is what we want to achieve:
|
||||||
|
// Abstract Base Class (e.g. Layout) defines attribute `Widget Name` which should be overridden by subclasses
|
||||||
|
// Abstract Base Class also defines a single Get Attribute method which returns `Widget Name`
|
||||||
|
|
||||||
|
// Concrete Subclass (e.g. Stylized Header Layout) overrides `Widget Name` attribute by re-defining it on the subclass
|
||||||
|
// Calling `Abstract Base Class@get Widget Name` with the subclass as `this` parm SHOULD return the overridden attribute value
|
||||||
|
|
||||||
|
// The Method should only be defined ONCE, and changes based on the overridden attributes of the subclasses
|
||||||
|
|
||||||
|
InstanceHandle c_TestClass = Oms.GetInstance(TEST_CLASS_GUID);
|
||||||
|
|
||||||
|
// `Test Class@get Widget Name(BA)` which is Abstract
|
||||||
|
BuildAttributeMethod m_TestMethod = Oms.MethodBuilder.CreateBuildAttributeMethod(c_TestClass, "get", "Widget Name", Core.Oop.AccessModifier.Public, false, Oms.GetInstance(KnownAttributeGuids.Text.Name), "DO_NOT_USE");
|
||||||
|
Oms.SetAttributeValue(m_TestMethod, Oms.GetInstance(KnownAttributeGuids.Boolean.Abstract), true);
|
||||||
|
ReturnAttributeMethodBinding ramb = m_TestMethod.CreateMethodBinding(Oms);
|
||||||
|
|
||||||
|
InstanceHandle c_TestClass2 = Oms.GetInstance(TEST_CLASS2_GUID);
|
||||||
|
Oms.AddSuperClass(c_TestClass2, c_TestClass);
|
||||||
|
|
||||||
|
// i_TestClass1 is an instance of c_TestClass2, which by inheritance includes c_TestClass
|
||||||
|
InstanceHandle i_TestClass1 = Oms.CreateInstanceOf(c_TestClass2);
|
||||||
|
|
||||||
|
OmsContext context = Oms.CreateContext();
|
||||||
|
Assert.That(delegate ()
|
||||||
|
{
|
||||||
|
// set the c_TestClass to i_TestClass1
|
||||||
|
// this call SHOULD succeed given that c_TestClass is a superclass of c_TestClass2, but I don't think this is enforced (yet)
|
||||||
|
context.SetWorkData(c_TestClass, i_TestClass1);
|
||||||
|
|
||||||
|
// this should throw an exception since c_TestClass2 does not yet implement the m_TestMethod defined as abstract on c_TestClass
|
||||||
|
string nom = Oms.ExecuteReturningAttributeValue<string>(context, ramb);
|
||||||
|
}, Throws.InvalidOperationException);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void Overridden_Abstract_Method_Call_does_not_throw_Exception()
|
||||||
|
{
|
||||||
|
// this is what we want to achieve:
|
||||||
|
// Abstract Base Class (e.g. Layout) defines attribute `Widget Name` which should be overridden by subclasses
|
||||||
|
// Abstract Base Class also defines a single Get Attribute method which returns `Widget Name`
|
||||||
|
|
||||||
|
// Concrete Subclass (e.g. Stylized Header Layout) overrides `Widget Name` attribute by re-defining it on the subclass
|
||||||
|
// Calling `Abstract Base Class@get Widget Name` with the subclass as `this` parm SHOULD return the overridden attribute value
|
||||||
|
|
||||||
|
// The Method should only be defined ONCE, and changes based on the overridden attributes of the subclasses
|
||||||
|
|
||||||
|
InstanceHandle c_TestClass = Oms.GetInstance(TEST_CLASS_GUID);
|
||||||
|
|
||||||
|
// `Test Class@get Widget Name(BA)` which is Abstract
|
||||||
|
BuildAttributeMethod m_TestMethod = Oms.MethodBuilder.CreateBuildAttributeMethod(c_TestClass, "get", "Widget Name", Core.Oop.AccessModifier.Public, false, Oms.GetInstance(KnownAttributeGuids.Text.Name), "DO_NOT_USE");
|
||||||
|
Oms.SetAttributeValue(m_TestMethod, Oms.GetInstance(KnownAttributeGuids.Boolean.Abstract), true);
|
||||||
|
ReturnAttributeMethodBinding ramb = m_TestMethod.CreateMethodBinding(Oms);
|
||||||
|
|
||||||
|
InstanceHandle c_TestClass2 = Oms.GetInstance(TEST_CLASS2_GUID);
|
||||||
|
Oms.AddSuperClass(c_TestClass2, c_TestClass);
|
||||||
|
|
||||||
|
// `Test Class 2@get Widget Name(BA)` which is overriding the Abstract defined above
|
||||||
|
string NOM_TEST_VALUE = "Hello World Test Class";
|
||||||
|
BuildAttributeMethod m_TestMethod2 = Oms.MethodBuilder.CreateBuildAttributeMethod(c_TestClass2, "get", "Widget Name", Core.Oop.AccessModifier.Public, false, Oms.GetInstance(KnownAttributeGuids.Text.Name), NOM_TEST_VALUE);
|
||||||
|
ReturnAttributeMethodBinding ramb2 = m_TestMethod2.CreateMethodBinding(Oms);
|
||||||
|
Oms.AssignRelationship(m_TestMethod2, Oms.GetInstance(KnownRelationshipGuids.Method__implements__Method), m_TestMethod);
|
||||||
|
|
||||||
|
// i_TestClass1 is an instance of c_TestClass2, which by inheritance includes c_TestClass
|
||||||
|
InstanceHandle i_TestClass1 = Oms.CreateInstanceOf(c_TestClass2);
|
||||||
|
|
||||||
|
OmsContext context = Oms.CreateContext();
|
||||||
|
string nom = "";
|
||||||
|
Assert.That(delegate ()
|
||||||
|
{
|
||||||
|
// set the c_TestClass to i_TestClass1
|
||||||
|
// this call SHOULD succeed given that c_TestClass is a superclass of c_TestClass2, but I don't think this is enforced (yet)
|
||||||
|
context.SetWorkData(c_TestClass, i_TestClass1);
|
||||||
|
|
||||||
|
// this should throw an exception since c_TestClass2 does not yet implement the m_TestMethod defined as abstract on c_TestClass
|
||||||
|
nom = Oms.ExecuteReturningAttributeValue<string>(context, ramb);
|
||||||
|
}, Throws.Nothing);
|
||||||
|
|
||||||
|
Assert.That(nom, Is.EqualTo(NOM_TEST_VALUE));
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user