diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsClass.cs b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsClass.cs index 2d03306..76931d3 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsClass.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsClass.cs @@ -1,6 +1,19 @@ +using System.Diagnostics; + namespace Mocha.Core.Modeling; public class OmsClass { + public Guid GlobalIdentifier { get; internal set; } = Guid.Empty; + protected object GetAttributeValue() + { + StackTrace st = new StackTrace(); + StackFrame frame = st.GetFrame(0); + return 1; + } + protected void SetAttributeValue(object value) + { + + } } \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsDatabase.cs b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsDatabase.cs index 7d0da5a..7c52b4b 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsDatabase.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsDatabase.cs @@ -1,5 +1,7 @@ using System.Globalization; using System.Reflection; +using MBS.Core.Reflection; +using Mocha.Core.Modeling.PropertyImplementations; namespace Mocha.Core.Modeling; @@ -13,6 +15,9 @@ public class OmsDatabase Oms = oms; _Type = this.GetType(); + // First create all the classes we've defined in the database. + // These are nested types of the class derived from OmsDatabase. + Type[] nestedTypes = null; try { @@ -33,6 +38,44 @@ public class OmsDatabase CreateClass(t); } + + foreach (Type t in nestedTypes) + { + if (t == null) continue; + if (t.IsAbstract) continue; + + if (!t.IsSubclassOf(typeof(OmsClass))) + continue; + + SetupClassProperties(t); + } + + // Now that our schema is set up, go through OmsDatabase public properties + // and assign OmsPropertyImplementations to the properties which represent + // Lists of instances. + FieldInfo[] fis = _Type.GetFields(BindingFlags.Public | BindingFlags.Instance); + foreach (FieldInfo fi in fis) + { + if (fi.FieldType.IsSubclassOfGeneric(typeof(ICollection<>))) + { + // this is a collection of instances of the given type, which must inherit from OmsClass + Type genericTypeArg = fi.FieldType.GenericTypeArguments[0]; + if (!genericTypeArg.IsSubclassOf(typeof(OmsClass))) + { + throw new InvalidOperationException("Generic type argument of collection type must inherit from OmsClass"); + } + + fi.SetValue(this, OmsInstanceList.CreateGeneric(this, genericTypeArg)); + } + else if (fi.FieldType.IsSubclassOf(typeof(OmsClass))) + { + // pi.SetValue(this, new OmsNonsingularRelationshipProperty()); + } + else + { + + } + } } private void CreateClass(Type t) @@ -53,12 +96,6 @@ public class OmsDatabase } } - PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance); - foreach (PropertyInfo property in properties) - { - SetupClassProperty(t, property); - } - if (globalIdentifier == Guid.Empty) { globalIdentifier = Guid.NewGuid(); @@ -68,8 +105,40 @@ public class OmsDatabase Oms.CreateClass(className, globalIdentifier); } + private void SetupClassProperties(Type t) + { + PropertyInfo[] properties = t.GetProperties(BindingFlags.Public | BindingFlags.Instance); + foreach (PropertyInfo property in properties) + { + SetupClassProperty(t, property); + } + } + + public static Guid GetGlobalIdentifierForClass(Type t) + { + if (!t.IsSubclassOf(typeof(OmsClass))) + { + throw new InvalidOperationException("Must be subclass of OmsClass"); + } + + object[] attrs = t.GetCustomAttributes(false); + foreach (object attr in attrs) + { + if (attr is OmsGlobalIdentifierAttribute) + { + return ((OmsGlobalIdentifierAttribute)attr).GlobalIdentifier; + } + } + return Guid.Empty; + } + private void SetupClassProperty(Type t, PropertyInfo pi) { + if (pi.PropertyType == typeof(Guid) && pi.Name == "GlobalIdentifier") + { + return; + } + object[] propertyAttrs = pi.GetCustomAttributes(false); Guid globalIdentifier = Guid.Empty; foreach (object attr in propertyAttrs) @@ -85,10 +154,72 @@ public class OmsDatabase globalIdentifier = Guid.NewGuid(); } - InstanceHandle relationshipInstance = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.Relationship), globalIdentifier); - ApplyNativeAttributes(relationshipInstance, propertyAttrs); + Guid myGuid = GetGlobalIdentifierForClass(t); + InstanceHandle sourceClassInstance = Oms.GetInstance(myGuid); + InstanceHandle propertyInstance = InstanceHandle.Empty; + + if (pi.PropertyType.IsSubclassOfGeneric(typeof(ICollection<>))) + { + Type arg = pi.PropertyType.GenericTypeArguments[0]; + Guid guid = GetGlobalIdentifierForClass(arg); + InstanceHandle relationshipDestinationClassInstance = Oms.GetInstance(guid); + + CreateRelationship(out propertyInstance, globalIdentifier, sourceClassInstance, relationshipDestinationClassInstance, false); + } + else if (pi.PropertyType.IsSubclassOf(typeof(OmsClass))) + { + Guid guid = GetGlobalIdentifierForClass(pi.PropertyType); + InstanceHandle relationshipDestinationClassInstance = Oms.GetInstance(guid); + + CreateRelationship(out propertyInstance, globalIdentifier, sourceClassInstance, relationshipDestinationClassInstance, true); + } + else + { + // attribute + if (pi.PropertyType == typeof(string)) + { + // Text Attribute + CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.TextAttribute), pi.Name); + } + else if (pi.PropertyType == typeof(bool)) + { + // Boolean Attribute + CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.BooleanAttribute), pi.Name); + } + else if (pi.PropertyType == typeof(decimal) + || pi.PropertyType == typeof(int)) + { + // Numeric Attribute + CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.NumericAttribute), pi.Name); + } + else if (pi.PropertyType == typeof(DateTime)) + { + // Date Attribute + CreateAttribute(out propertyInstance, Oms.GetInstance(KnownInstanceGuids.Classes.DateAttribute), pi.Name); + } + else + { + throw new InvalidOperationException(String.Format("OMS type of property with CLR type '{0}' could not be determined", pi.PropertyType)); + } + } + + ApplyNativeAttributes(propertyInstance, propertyAttrs); } + private void CreateAttribute(out InstanceHandle propertyInstance, InstanceHandle attributeClassInstance, string name) + { + propertyInstance = Oms.CreateInstanceOf(attributeClassInstance); + Oms.SetAttributeValue(propertyInstance, Oms.GetInstance(KnownAttributeGuids.Text.Name), name); + } + + private void CreateRelationship(out InstanceHandle relationshipInstance, Guid globalIdentifier, InstanceHandle sourceClassInstance, InstanceHandle relationshipDestinationClassInstance, bool relationshipSingular) + { + relationshipInstance = Oms.CreateInstanceOf(Oms.GetInstance(KnownInstanceGuids.Classes.Relationship), globalIdentifier); + Oms.AssignRelationship(relationshipInstance, Oms.GetInstance(KnownRelationshipGuids.Relationship__has_source__Class), sourceClassInstance); + Oms.AssignRelationship(relationshipInstance, Oms.GetInstance(KnownRelationshipGuids.Relationship__has_destination__Class), relationshipDestinationClassInstance); + Oms.SetAttributeValue(relationshipInstance, Oms.GetInstance(KnownAttributeGuids.Boolean.Singular), relationshipSingular); + } + private void ApplyNativeAttributes(InstanceHandle inst, object[] propertyAttrs) { foreach (object attr in propertyAttrs) diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs new file mode 100644 index 0000000..5b11a61 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Modeling/OmsInstanceList.cs @@ -0,0 +1,96 @@ +using System.Collections; + +namespace Mocha.Core.Modeling; + +public class OmsInstanceList +{ + internal static OmsInstanceList CreateGeneric(OmsDatabase omsdb, Type type) + { + Guid gl = OmsDatabase.GetGlobalIdentifierForClass(type); + + Type t = typeof(OmsInstanceList<>); + Type concrete = t.MakeGenericType(new Type[] { type }); + return (OmsInstanceList) concrete.Assembly.CreateInstance(concrete.FullName, false, System.Reflection.BindingFlags.Default, null, new object[] { omsdb, gl }, null, null); + } +} + +public class OmsInstanceList : OmsInstanceList, IList where T : OmsClass +{ + private OmsDatabase omsdb; + private Guid classGuid; + + public OmsInstanceList(OmsDatabase omsdb, Guid classGuid) + { + this.omsdb = omsdb; + this.classGuid = classGuid; + } + + public T this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public int Count + { + get + { + InstanceHandle ih = omsdb.Oms.GetInstance(classGuid); + return omsdb.Oms.CountInstances(ih); + } + } + + public bool IsReadOnly => throw new NotImplementedException(); + + public void Add(T item) + { + if (item.GlobalIdentifier == Guid.Empty) + { + Guid localClassGuid = OmsDatabase.GetGlobalIdentifierForClass(item.GetType()); + Guid localInstanceGuid = Guid.NewGuid(); + + omsdb.Oms.CreateInstanceOf(omsdb.Oms.GetInstance(localClassGuid), localInstanceGuid); + } + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(T item) + { + throw new NotImplementedException(); + } + + public void CopyTo(T[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + public int IndexOf(T item) + { + throw new NotImplementedException(); + } + + public void Insert(int index, T item) + { + throw new NotImplementedException(); + } + + public bool Remove(T item) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsNonsingularRelationshipProperty.cs b/mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsNonsingularRelationshipProperty.cs new file mode 100644 index 0000000..3cd6900 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsNonsingularRelationshipProperty.cs @@ -0,0 +1,5 @@ +namespace Mocha.Core.Modeling.PropertyImplementations; + +public class OmsNonsingularRelationshipProperty : OmsRelationshipProperty +{ +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsRelationshipProperty.cs b/mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsRelationshipProperty.cs new file mode 100644 index 0000000..76be6f6 --- /dev/null +++ b/mocha-dotnet/src/lib/Mocha.Core/Modeling/PropertyImplementations/OmsRelationshipProperty.cs @@ -0,0 +1,6 @@ +namespace Mocha.Core.Modeling.PropertyImplementations; + +public abstract class OmsRelationshipProperty +{ + +} \ No newline at end of file diff --git a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/Mini/MiniOms.cs b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/Mini/MiniOms.cs index 8d519e3..f4f6cd9 100644 --- a/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/Mini/MiniOms.cs +++ b/mocha-dotnet/src/lib/Mocha.Core/OmsImplementations/Mini/MiniOms.cs @@ -116,11 +116,18 @@ public class MiniOms : MemoryOms CreateRelationship(c_Class, "has", c_Attribute, KnownRelationshipGuids.Class__has__Attribute, false, "for", KnownRelationshipGuids.Attribute__for__Class); AddAttribute(c_Class, a_Name); + AddAttribute(c_Attribute, a_Name); + AddAttribute(c_Relationship, a_RelationshipType); AddAttribute(c_Relationship, a_Singular); //* ========== Superclass / Subclass Relationships ========== *// CreateRelationship(c_Class, "has super", c_Class, KnownRelationshipGuids.Class__has_super__Class, false, "has sub", KnownRelationshipGuids.Class__has_sub__Class); + + AddSuperClass(c_TextAttribute, c_Attribute); + AddSuperClass(c_BooleanAttribute, c_Attribute); + AddSuperClass(c_NumericAttribute, c_Attribute); + AddSuperClass(c_DateAttribute, c_Attribute); c_WorkSet = CreateClass("Work Set", KnownInstanceGuids.Classes.WorkSet); SetInstanceKey(c_WorkSet, new InstanceKey(1, 15)); diff --git a/mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/VehicleForHireTests.cs b/mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/VehicleForHireTests.cs index d49e146..f61ed5c 100644 --- a/mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/VehicleForHireTests.cs +++ b/mocha-dotnet/tests/Mocha.Core.Tests/SampleDatabases/VehicleForHireTests.cs @@ -9,19 +9,36 @@ public class VehicleForHireDB : OmsDatabase [OmsName("Business"), OmsGlobalIdentifier("{c4e1d676-726c-42f4-9edb-a0ca0ee0e612}")] public class Business : OmsClass { + public Business(string name) + { + Name = name; + } + + [OmsGlobalIdentifier("{9153A637-992E-4712-ADF2-B03F0D9EDEA6}")] + public readonly string Name; + /// /// Business.has Driver /// /// [OmsRelationshipType("has"), OmsGlobalIdentifier("{63e5ab3a-4009-4af9-8171-5b23472c1c8c}")] - public OmsRelationship Drivers { get; } + public readonly IList Drivers; /// /// Business.has Vehicle /// /// [OmsRelationshipType("has"), OmsGlobalIdentifier("{c54125f6-c3b0-40b2-99f9-e0ed20a75bfa}")] - public OmsRelationship Vehicles { get; } + public readonly IList Vehicles; + + [OmsRelationshipType("has"), OmsGlobalIdentifier("{9d4946c3-db8d-4f27-b21b-52e9b0fdf231}")] + public BusinessPermit Permit; + } + + [OmsName("Business Permit"), OmsGlobalIdentifier("{7680b302-c9c2-4275-abf8-373adafbf58e}")] + public class BusinessPermit : OmsClass + { + } [OmsName("Driver"), OmsGlobalIdentifier("{10c51c07-c757-4b00-b406-d54c99460f32}")] @@ -43,13 +60,20 @@ public class VehicleForHireDB : OmsDatabase [OmsName("Vehicle Permit"), OmsGlobalIdentifier("{38a492b9-901f-4074-af45-642e812b51f5}")] public class VehiclePermit : OmsClass { + [OmsName("Permit Index"), OmsGlobalIdentifier("{759f4beb-ee43-47b8-9add-16967bba8ec8}")] + public int PermitIndex { get { return (int) GetAttributeValue(); } set { SetAttributeValue(value); } } + [OmsName("Permit Name"), OmsGlobalIdentifier("{c4f4a59f-c246-4d88-a2fd-588195d5e943}")] + public string PermitName { get; set; } } + + public readonly IList Businesses; } public class VehicleForHireTests : OmsModelingTestsBase { - protected override OmsDatabase GetDatabase() => new VehicleForHireDB(); + private OmsDatabase db = new VehicleForHireDB(); + protected override OmsDatabase GetDatabase() => db; [Test] public void ClassesCreated() @@ -61,8 +85,12 @@ public class VehicleForHireTests : OmsModelingTestsBase } [Test] - public void RelationshipsCreated() - { + public void RelationshipsCreated() { CheckRelationshipExists(new Guid("{867126b9-294e-454f-b9c5-88df44353014}"), new Guid("{4b6f186c-babe-4c42-840b-3840373f68c2}"), "has", new Guid("{38a492b9-901f-4074-af45-642e812b51f5}")); + + VehicleForHireDB db = (VehicleForHireDB) GetDatabase(); + + VehicleForHireDB.Business busn = new VehicleForHireDB.Business("Ryde Rentals Transportation"); + db.Businesses.Add(busn); } } \ No newline at end of file